Monitoring cluster components
metrics-server, kubectl top, and what to watch.
Run kubectl top pods on a brand-new cluster and there's a decent chance you get back error: Metrics API not available instead of a table of numbers. Nothing's broken. Kubernetes just doesn't measure live resource usage on its own. The control plane knows what you asked for, the CPU and memory a Pod requested (a Pod being the smallest thing you deploy, one or more containers sharing a network address). It has no idea what that Pod is actually burning right now. To see that, you install a small add-on called metrics-server. It's the speedometer bolted to the dashboard. It tells you how fast you're going this instant and nothing about the trip so far.
Where the live numbers come from
Every worker node already runs a kubelet, the agent that launches your containers and reports their health up to the control plane. Baked into that kubelet is a stats collector called cAdvisor that watches the Linux cgroups (control groups, the kernel feature that boxes in and counts what each container uses) and reads their real CPU and memory counters straight from the kernel. metrics-server does one narrow job. Every 15 seconds it calls each node's kubelet, pulls the newest per-Pod and per-node numbers, and keeps them in memory. Only the latest sample. It writes nothing to disk and runs no database, so it remembers nothing about a minute ago. That's deliberate, and it's why metrics-server stays tiny even on a cluster with thousands of Pods.
Reading those numbers is the clever bit. metrics-server registers itself with the API server as an extension, an APIService named v1beta1.metrics.k8s.io. After that, kubectl top and the autoscaler just ask the normal API server for metrics.k8s.io, and the API server quietly forwards the request on to metrics-server and hands you the answer. Same front door as every other resource, so the same RBAC (Role-Based Access Control, Kubernetes' permission system) decides who's allowed to read it.
kubectl top nodeskubectl top pods -n payments --sort-by=memory
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%node-1 243m 12% 1187Mi 31%node-2 611m 30% 2934Mi 76%NAME CPU(cores) MEMORY(bytes)payments-api-7d4b9c-x2fjq 118m 512Mipayments-api-7d4b9c-h9lpm 74m 486Mi
Check it's actually wired in
When top comes back empty, don't start by reading metrics-server's logs. Start by asking whether the extension is registered and healthy. The API server tracks every aggregated API in an APIService object with an AVAILABLE column. If that says True, the plumbing is fine and the problem is your query. If it says False, the API server can't reach metrics-server, and the reason is printed right there in the object.
kubectl get apiservice v1beta1.metrics.k8s.io
NAME SERVICE AVAILABLE AGEv1beta1.metrics.k8s.io kube-system/metrics-server True 9d
The autoscaler drinks from the same tap
A HorizontalPodAutoscaler (HPA, the controller that adds or removes Pod copies as load changes) doesn't measure anything itself. It reads metrics.k8s.io, the very API metrics-server serves. So two problems share one root. If kubectl top is blank, your HPA's TARGETS column reads <unknown> and it won't scale on CPU or memory, because it's a thermostat wired to a dead sensor. It wants to react, it just can't see the temperature. Fix metrics-server and both come back to life at once.
kubectl get hpa payments-api
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICASpayments-api Deployment/payments-api cpu: 42%/70% 2 10 3
x509: certificate signed by unknown authority while the pod itself sits there looking perfectly Running. In a lab, add --kubelet-insecure-tls to the metrics-server container args. In production, turn on signed kubelet serving certificates instead of switching the check off.Past 'right now': keeping history
metrics-server answers 'what's hot this second' and forgets it immediately. It can't tell you what the cluster was doing at 3am when the pager went off, because it stored nothing. For history, dashboards, and alerts you add a real monitoring stack. The usual one is Prometheus (it scrapes metrics on a schedule and stores them as time-series data), Grafana (the dashboards), and Alertmanager (it routes alerts to your pager or Slack). Most people install the three together with the kube-prometheus-stack Helm chart. Helm is Kubernetes' package installer, so a single command lays down the whole stack. The chart also drops in kube-state-metrics, a component that turns cluster objects into numbers you can graph. What's worth watching once you have that history? Node and Pod usage against their requests and limits, so you can spot a node about to run out of memory and start evicting Pods. Restart counts and CrashLoopBackOff, which is a container that keeps failing and getting restarted. Pods stuck Pending, meaning the scheduler can't find a node to place them on. And the control plane itself, mainly API server request latency and the health of etcd, the key-value database holding all cluster state.
You tell Prometheus what to scrape with a ServiceMonitor, a small custom resource the Prometheus Operator watches for. Point it at your app's Service by label, name the port that exposes /metrics, and the operator regenerates Prometheus' scrape config for you. You never hand-edit a config file, and a new app starts getting scraped the moment its ServiceMonitor lands.
apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata:name: payments-apinamespace: monitoringlabels:release: prometheusspec:selector:matchLabels:app: payments-apiendpoints:- port: metricsinterval: 30s
kubectl apply -f servicemonitor.yamlkubectl get servicemonitor -n monitoring
servicemonitor.monitoring.coreos.com/payments-api createdNAME AGEkube-prometheus-stack-apiserver 9dpayments-api 6s
top is not long-term capacity history. Pair it with a real metrics stack for trends.
Control-plane monitoring on managed services comes from the vendor. Still watch worker resource pressure yourself.
Missing metrics-server makes HPA sit idle. Verify the pipeline before you tune algorithms.
Try this
If metrics-server is present, run kubectl top nodes and top pods. Compare a hot pod to its requests and decide whether you need more replicas or bigger requests.
$ kubectl top nodes$ kubectl top pods -n payments --sort-by=memory$ kubectl get apiservice v1beta1.metrics.k8s.ioNAME SERVICE AVAILABLE AGEv1beta1.metrics.k8s.io kube-system/metrics-server True 9d$ kubectl get hpa payments-api$ kubectl apply -f servicemonitor.yaml$ kubectl get servicemonitor -n monitoringservicemonitor.monitoring.coreos.com/payments-api createdNAME AGEkube-prometheus-stack-apiserver 9dpayments-api 6s
Takeaway
metrics-server feeds kubectl top and HPA. Component health still needs separate probes for API, etcd, and core DNS.