Install Metrics Server in HA Mode on a kubeadm Bare-Metal Kubernetes Cluster (Ubuntu 24.04)

Metrics Server collects CPU / memory usage from each node’s Kubelet and exposes it via the Kubernetes Metrics API. This enables:

  • kubectl top nodes
  • kubectl 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 = kubernetes in 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 are Running and READY 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
  • READY is 1/1 on every control-plane node
  • The AGE is 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top