Metrics Server collects CPU / memory usage from each node’s Kubelet and exposes it via the Kubernetes Metrics API. This enables:
kubectl top nodeskubectl top pods -A- HPA CPU/Memory autoscaling
- Resource usage views in tools like Lens
This post is based on the following post:
This post installs Metrics Server in HA mode only (multiple replicas) and enables aggregator routing on kube-apiserver so requests sent to Metrics Server are load balanced across instances.
Lab Context (Bare Metal)
Cluster built with kubeadm on bare-metal Ubuntu 24.04:
- k8s-1.maksonlee.com – 192.168.0.99
- k8s-2.maksonlee.com – 192.168.0.100
- k8s-3.maksonlee.com – 192.168.0.101
- Kubernetes: v1.34.x
- containerd + Calico
- Control-plane endpoint:
k8s.maksonlee.com:6443(kube-vip VIP 192.168.0.97)
Prerequisites (Read This First)
Metrics Server scrapes metrics from the Kubelet HTTPS endpoint (TCP/10250). If your nodes use self-signed Kubelet serving certs, Metrics Server typically fails with TLS/x509 errors.
On each node, verify the current Kubelet serving cert:
sudo openssl x509 -in /var/lib/kubelet/pki/kubelet-server-current.pem -noout \
-issuer -subject -dates -ext subjectAltName
Example (good):
issuer=CN = kubernetes
subject=O = system:nodes, CN = system:node:k8s-1.maksonlee.com
notBefore=Dec 29 12:38:30 2025 GMT
notAfter=Dec 29 12:38:30 2026 GMT
X509v3 Subject Alternative Name:
DNS:k8s-1.maksonlee.com, IP Address:192.168.0.99
What “good” looks like:
- Issuer is the cluster CA (commonly
CN = kubernetesin kubeadm clusters) - SAN includes both the node DNS name and node IP
If your issuer is not the cluster CA (or SANs are missing), fix kubelet serving certificates first. Otherwise Metrics Server can fail with x509 errors unless you weaken security with --kubelet-insecure-tls (not recommended).
Install Metrics Server (HA)
Apply the official HA manifest:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/high-availability-1.21+.yaml
Verify Metrics Server Pods Are Running
Wait for the deployment rollout to complete:
kubectl -n kube-system rollout status deploy/metrics-server
Expected:
deployment "metrics-server" successfully rolled out
Then verify the pods:
kubectl -n kube-system get pods -l k8s-app=metrics-server -o wide
Expected:
- Two
metrics-server-*pods areRunningandREADY 1/1 - Pods are scheduled on different nodes (best case for HA)
NAME READY STATUS RESTARTS AGE IP NODE
metrics-server-... 1/1 Running 0 ... 10.244.x.x k8s-1.maksonlee.com
metrics-server-... 1/1 Running 0 ... 10.244.x.x k8s-2.maksonlee.com
Recommended for HA: Enable Aggregator Routing on kube-apiserver
To maximize the efficiency of this highly available configuration, add the following flag to kube-apiserver so requests sent to Metrics Server are load balanced between instances:
--enable-aggregator-routing=true
kubeadm (static pod) steps — do this on ALL control-plane nodes
On k8s-1, k8s-2, k8s-3:
- Edit the apiserver static pod manifest:
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
- Under the kube-apiserver container args, add:
- --enable-aggregator-routing=true
- Save and exit.
Kubelet will automatically restart the kube-apiserver static pod after the file changes.
Verify kube-apiserver Restarted Cleanly
From any admin terminal:
kubectl -n kube-system get pods -l component=kube-apiserver -o wide
Expected:
- All kube-apiserver pods are
Running READYis1/1on every control-plane node- The
AGEis recent (because you just restarted them by editing the static pod manifest)
Example:
NAME READY STATUS RESTARTS AGE IP NODE
kube-apiserver-k8s-1.maksonlee.com 1/1 Running 0 88s 192.168.0.99 k8s-1.maksonlee.com
kube-apiserver-k8s-2.maksonlee.com 1/1 Running 0 51s 192.168.0.100 k8s-2.maksonlee.com
kube-apiserver-k8s-3.maksonlee.com 1/1 Running 0 16s 192.168.0.101 k8s-3.maksonlee.com
Verify Metrics Server Works
Confirm the Metrics APIService is Available
kubectl get apiservice v1beta1.metrics.k8s.io
Expected:
- AVAILABLE = True
Example:
NAME SERVICE AVAILABLE AGE
v1beta1.metrics.k8s.io kube-system/metrics-server True 43s
Test kubectl top
Wait 30–60 seconds after install, then run:
kubectl top nodes
kubectl top pods -A
You should see CPU/memory values instead of “Metrics API not available”.
Did this guide save you time?
Support this site