Horizontal Pod Autoscaler Example
Download the YAML configuration files for the HPA example
Check Metrics Server
Check if the Metrics Server is installed in your cluster. Look for a pod called metrics-server in the kube-system namespace.
Install Metrics Server
If the Metrics Server is not installed, apply the components.yaml file to install it.
Create the Deployment
Create the deployment and verify that the pods are running.
Set Autoscaling Limits
Set up Horizontal Pod Autoscaler to scale the deployment when CPU usage exceeds 50%, with a minimum of 1 pod and maximum of 4 pods.
Deploy BusyBox
Deploy a BusyBox pod that we'll use to generate load on our application.
Connect to BusyBox
Connect to the BusyBox container to run commands that will generate load.
Increase Load
Run this endless loop in the BusyBox container to generate load on the application. In a separate terminal, check the HPA status to see the scaling in action.
Launch K9s
In a separate terminal, launch K9s to visually monitor what's happening in your cluster.
Stop the Load
Press Ctrl-C to terminate the endless loop and type exit to leave the BusyBox container.
Cleanup
Delete all resources created during this tutorial. The last command is optional if you want to keep the Metrics Server for other uses.
YAML Configuration Files
components.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: system:aggregated-metrics-reader
rules:
- apiGroups:
- metrics.k8s.io
resources:
- pods
- nodes
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
rules:
- apiGroups:
- ""
resources:
- pods
- nodes
- nodes/stats
- namespaces
- configmaps
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:metrics-server
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: https
selector:
k8s-app: metrics-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
strategy:
rollingUpdate:
maxUnavailable: 0
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --kubelet-insecure-tls
- --metric-resolution=15s
image: k8s.gcr.io/metrics-server/metrics-server:v0.5.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /livez
port: https
scheme: HTTPS
periodSeconds: 10
name: metrics-server
ports:
- containerPort: 443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /readyz
port: https
scheme: HTTPS
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 200Mi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /tmp
name: tmp-dir
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
serviceAccountName: metrics-server
volumes:
- emptyDir: {}
name: tmp-dir
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
labels:
k8s-app: metrics-server
name: v1beta1.metrics.k8s.io
spec:
group: metrics.k8s.io
groupPriorityMinimum: 100
insecureSkipTLSVerify: true
service:
name: metrics-server
namespace: kube-system
version: v1beta1
versionPriority: 100
Metrics Server Configuration Explanation:
Service Account:
Creates a service account named metrics-server in
the kube-system namespace for the Metrics Server to use.
RBAC Configuration:
- ClusterRole → Defines permissions for reading metrics
- RoleBinding → Binds the service account to the role
- ClusterRoleBinding → Binds the service account to cluster-wide roles
Service:
Exposes the Metrics Server on port 443 within the cluster.
Deployment:
- image → Uses the official Metrics Server image
- args → Includes
--kubelet-insecure-tlsfor development environments - resources → Defines CPU and memory requests
- securityContext → Runs with restricted permissions
APIService:
Registers the Metrics Server API with Kubernetes API server.
deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hpa-deployment
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: guybarrette/hpa-example
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
Deployment Configuration Explanation:
Deployment:
- name: hpa-deployment → Names the deployment
- replicas: 1 → Starts with one pod
- selector.matchLabels → Selects pods with label
run: php-apache - image: guybarrette/hpa-example → Uses a sample PHP Apache image
- resources → Sets CPU limits (500m) and requests (200m)
Service:
- name: php-apache → Names the service
- port: 80 → Exposes the service on port 80
- selector → Routes traffic to pods with label
run: php-apache
pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mybox
spec:
restartPolicy: Always
containers:
- name: mybox
image: busybox
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
command:
- sleep
- "3600"
BusyBox Pod Configuration Explanation:
Pod:
- name: mybox → Names the pod
- restartPolicy: Always → Always restart the pod if it fails
Container:
- image: busybox → Uses the lightweight BusyBox image
- resources → Sets CPU and memory requests and limits
- command → Runs the sleep command for 3600 seconds (1 hour)
Purpose:
This pod is used to generate load on the PHP Apache deployment by running an endless loop that continuously makes HTTP requests.