Overview #
I’m using a K8s Kubernetes cluster with MetalLB, that was deployed with Kubespray on Debian 12 servers based on VMware Workstation VMs. It was necessary to increase to number of CPU cores on the worked node VMs, otherwise the HPA could not start more than 5 pods because of insufficient CPU.
Metrics Server #
Metrics Server Components #
# Download official metrics server YAML manifest from SIGs (Kubernetes Special Interest Groups)
wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
Note: It was necessary to add the --kubelet-insecure-tls
option in the “Deployment” > “containers” > “args” section.
vi 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:
- nodes/metrics
verbs:
- get
- apiGroups:
- ""
resources:
- pods
- nodes
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=10250
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls # Add this line
image: registry.k8s.io/metrics-server/metrics-server:v0.7.1
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /livez
port: https
scheme: HTTPS
periodSeconds: 10
name: metrics-server
ports:
- containerPort: 10250
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:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
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
Deploy the Metrics Server #
# Deploy the metrics server
kubectl apply -f components.yaml
Verify the Metric Server Resources #
# List deployments: Wait till the "metrics-server" deployment is ready
kubectl get deployments --namespace kube-system
# Shell output:
NAME READY UP-TO-DATE AVAILABLE AGE
...
metrics-server 1/1 1 1 28s
Horizontal Pod Autoscaler #
Example Deployment & Service #
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/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
# Deploy the deployment a serivce
kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
Verify the Deployment #
# List deployments
kubectl get deployments
# Shell output:
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 1/1 1 1 108s
# List services
kubectl get services
# Shell output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
php-apache ClusterIP 10.233.27.169 <none> 80/TCP 110s
Horizontal Pod Autoscaler (HPA) #
This will increase and decrease the number of replicas to maintain an average CPU utilization across all Pods of 50%:
# Deploy a pod autoscaler: Maintains between 1 and 10 pod replicas
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10 --name=hpa-example
Verify the HPA Status #
# List the HPA status: Wait till the HPA gets a target output from the metrics server
kubectl get hpa
# Shell output:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-example Deployment/php-apache <unknown>/50% 1 10 0 7s
# Shell output:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-example Deployment/php-apache 0%/50% 1 10 1 26s
Increase the Load #
The following pod acts as a client that is sending queries to the php-apache service:
# Deploy the pod that acts as a load generator: Run in seperate shell
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
Watch the HPA Load #
# Wacht the load of the HPA
kubectl get hpa hpa-example --watch
# Shell output:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
hpa-example Deployment/php-apache 0%/50% 1 10 1 2m50s
hpa-example Deployment/php-apache 223%/50% 1 10 1 3m15s
hpa-example Deployment/php-apache 250%/50% 1 10 4 3m30s
hpa-example Deployment/php-apache 177%/50% 1 10 5 3m45s
hpa-example Deployment/php-apache 83%/50% 1 10 5 4m1s
hpa-example Deployment/php-apache 80%/50% 1 10 7 4m16s
hpa-example Deployment/php-apache 71%/50% 1 10 8 4m31s
hpa-example Deployment/php-apache 54%/50% 1 10 8 4m46s
Verify Example Deployments Pods #
Verify the increased number of pods in the example deployments:
# List the pods of the example deployment
kubectl get deployment php-apache
# Shell outpt:
NAME READY UP-TO-DATE AVAILABLE AGE
php-apache 8/8 8 8 4m58s
# List pods
kubectl get pods -l run=php-apache
# Shell output:
NAME READY STATUS RESTARTS AGE
php-apache-598b474864-6vbjl 1/1 Running 0 96s
php-apache-598b474864-749nz 1/1 Running 0 5m8s
php-apache-598b474864-75k62 1/1 Running 0 81s
php-apache-598b474864-b88j7 1/1 Running 0 50s
php-apache-598b474864-cnqzh 1/1 Running 0 50s
php-apache-598b474864-fqsr7 1/1 Running 0 96s
php-apache-598b474864-kz6b4 1/1 Running 0 96s
php-apache-598b474864-m72vl 1/1 Running 0 35s
List HPA Details #
# List HPA details
kubectl describe hpa hpa-example
# Shell output:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 2m13s (x2 over 20m) horizontal-pod-autoscaler New size: 4; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 118s (x2 over 20m) horizontal-pod-autoscaler New size: 5; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 88s (x2 over 19m) horizontal-pod-autoscaler New size: 8; reason: cpu resource utilization (percentage of request) above target
Delete Resources #
# Delete the HPA
kubectl delete hpa hpa-example
# Delete the example deployment and service
kubectl delete -f https://k8s.io/examples/application/php-apache.yaml
Links #
# Official Documentation
https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/
# Metrics Server Configuration from SIGs (Kubernetes Special Interest Groups)
https://github.com/kubernetes-sigs/metrics-server