Asterrr's Handbook

Troubleshooting nodes

Why a node goes NotReady and how to bring it back, from node conditions and leases to the kubelet, the container runtime, kubeconfig and certificates, using systemctl and journalctl.

Exam tasks: 5.1 (troubleshoot clusters and nodes)

The decision: the node is NotReady (or Pods on it never start). Is it the kubelet, the container runtime, the kubelet's credentials, or the node running out of something?

How a node becomes NotReady

The kubelet on each node renews a Lease in kube-node-lease and posts node status. The node lifecycle controller in kube-controller-manager watches both.

What you seeMost likely causeWhere to look
Ready is Unknown, "Kubelet stopped posting node status"kubelet stopped, crashed, can't reach the API server, or the node is downsystemctl status kubelet, journalctl -u kubelet
Ready is False, "container runtime is down" or "PLEG is not healthy"containerd or CRI-O stopped or hungsystemctl status containerd, crictl info
Ready is False, "network plugin not ready: cni config uninitialized"No CNI installed, or its config is missing on this node/etc/cni/net.d/, CNI DaemonSet Pod on that node
MemoryPressure, DiskPressure, PIDPressure TrueNode is short on memory, disk or process IDs; kubelet evicts Podsk describe node, df -h, free -m
Ready but SchedulingDisabledSomeone ran kubectl cordon or draink uncordon <node>
Node missing from k get nodes entirelykubelet never registered: wrong kubeconfig, expired bootstrap token, wrong API server addressjournalctl -u kubelet
50s
Default --node-monitor-grace-period before the controller marks a silent node Unknown.
300s
Default tolerationSeconds added to Pods for the not-ready and unreachable taints, so eviction starts about 5 minutes later.
/var/lib/kubelet/config.yaml
KubeletConfiguration written by kubeadm: cgroup driver, staticPodPath, eviction thresholds.
/etc/kubernetes/kubelet.conf
The kubeconfig the kubelet uses to talk to the API server.
/var/lib/kubelet/pki
kubelet client and serving certificates, including kubelet-client-current.pem.

Step 1: read the node from the API

k get nodes -o wide
k describe node worker-2 | sed -n '/Conditions/,/Addresses/p'   # conditions and their messages
k describe node worker-2 | grep -A5 Taints

The Message column of the conditions usually names the broken piece. Copy it into your head before you SSH.

Step 2: check the kubelet on the node

ssh worker-2
sudo systemctl status kubelet        # active (running)? enabled? restarting in a loop?
sudo journalctl -u kubelet -e        # jump to the end; -f to follow; --since "10 min ago"
sudo systemctl cat kubelet           # unit file plus the kubeadm drop-in, shows which flags and files load

Common kubelet failures and fixes:

  • Stopped or disabled. sudo systemctl enable --now kubelet. A task that says "make it survive reboots" wants enable, not just start.
  • Bad config file. The log shows a parse error or an unknown field in /var/lib/kubelet/config.yaml. Fix the file, then sudo systemctl restart kubelet.
  • Wrong binary path or flag in the systemd drop-in or /var/lib/kubelet/kubeadm-flags.env. After editing a unit file run sudo systemctl daemon-reload before the restart.
  • Cgroup driver mismatch. The kubelet's cgroupDriver must match the runtime's (SystemdCgroup = true in containerd's config.toml for the systemd driver).
  • Can't reach the API server. "connection refused" or "x509" errors to the server: in /etc/kubernetes/kubelet.conf. Check the address and port, and that the CA in that file matches the cluster.

Exam signal

journalctl -u kubelet is long. Search it: sudo journalctl -u kubelet --no-pager | grep -iE 'error|fail' | tail -20. The last error before the restart loop is the one that matters.

Swap and cgroup v1

Older material says "turn off swap or the kubelet won't start". Current kubelets can run with swap when failSwapOn: false is set, but the default is still true, so on a lab node whose log complains about swap, sudo swapoff -a (and removing the swap line from /etc/fstab) is the quick fix. Separately, from v1.35 the kubelet won't start on a cgroup v1 host by default. If the log says cgroup v1 is unsupported, the node OS needs cgroup v2; that isn't a kubelet flag you should flip in an exam.

Step 3: check the container runtime

sudo systemctl status containerd
sudo crictl info | head -20          # runtime ready? network ready?
sudo crictl ps -a                    # containers on this node, including exited ones
sudo crictl pods                     # Pod sandboxes
  • crictl reads its endpoint from /etc/crictl.yaml, for example runtime-endpoint: unix:///run/containerd/containerd.sock. A wrong endpoint looks like "the runtime is down" even when it isn't.
  • If containerd is stopped: sudo systemctl enable --now containerd, then give the kubelet a few seconds.

Legacy: use containerd or CRI-O through the CRI, inspected with crictl instead

dockershim was removed in v1.24, so docker ps on a node and systemctl status docker are no longer how you check the runtime. v1.35 is also the last release to support containerd 1.x; plan for containerd 2.

Step 4: credentials and certificates

  • kubeadm sets the kubelet up for client certificate rotation: the current cert is a symlink, /var/lib/kubelet/pki/kubelet-client-current.pem. If it expired (a node left off for months), the log shows x509 "certificate has expired" errors.
  • Check: sudo openssl x509 -noout -enddate -in /var/lib/kubelet/pki/kubelet-client-current.pem.
  • On a control plane node, sudo kubeadm certs check-expiration lists every kubeadm-managed cert.
  • Kubelet serving certs may need CSR approval if serverTLSBootstrap is on: k get csr then k certificate approve <name>.

Step 5: resource pressure

  • DiskPressure: the kubelet evicts Pods and garbage-collects images. Find the culprit with df -h and sudo du -sh /var/lib/containerd /var/log/pods.
  • MemoryPressure: compare free -m with kubectl top pods -A --sort-by=memory on that node (see Resource usage).
  • Eviction thresholds come from evictionHard in the kubelet config, for example memory.available: "100Mi".

Scenarios

Scenario
`k get nodes` shows worker-3 as NotReady. `k describe node worker-3` shows every condition as Unknown with the message 'Kubelet stopped posting node status'. You can SSH to the node. What should you check first?
Scenario
On worker-1, an admin changed the kubelet's systemd drop-in file to point ExecStart at /usr/local/bin/kubelet, which doesn't exist. The node went NotReady. You correct the path back to /usr/bin/kubelet in the drop-in. What else is required for the node to recover?
Scenario
A node shows Ready=False with 'container runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized'. Other nodes are Ready. What is the most likely fix?

Drill

kubectl config use-context lab-nodes. Node edge-worker is NotReady. Bring it back to Ready so that it stays Ready after a reboot. Don't recreate the node.

Further reading

On this page