Worker node failures
What NotReady means, how to read node conditions, and the four things that take a worker down on the exam: the kubelet stopped, its configuration broken, its certificate or kubeconfig wrong, or the container runtime gone.
After this lesson
- 01You can read a node's Conditions and say whether the kubelet, the runtime, the disk or the network is the problem.
- 02You can bring a NotReady node back with systemctl, journalctl and a one-line fix in /var/lib/kubelet/config.yaml.
- 03You can predict what happens to the Pods on a node that stays down and use cordon and drain deliberately.
Troubleshoot clusters and nodes is the competency here, and the kubelet is the cluster component it is really about. A worker-node task on the exam reads: node X is NotReady, fix it. The whole job happens on the node, over ssh, with three tools: systemctl, journalctl and a text editor. This lesson gives you the order to use them in, the files that matter, and what the cluster does to the Pods on that node while you work.
NotReady and the node conditions
The kubelet posts its node status every 10 seconds and renews a Lease in kube-node-lease at the same rate. When the controller-manager sees neither for node-monitor-grace-period (50 seconds by default), it sets the Ready condition to Unknown, which kubectl get nodes prints as NotReady. So: Ready False means the kubelet is alive and reporting a problem; Ready Unknown means the kubelet is not reporting at all. The Reason and Message columns of describe node say which.
kubectl get nodes -o wide
kubectl describe node cka-worker2 | sed -n '/^Conditions:/,/^Addresses:/p'
kubectl describe node cka-worker2 | grep -A3 '^Taints'
kubectl get lease -n kube-node-lease # RENEW TIME stops moving for a dead kubelet (-o yaml)
kubectl get pods -A -o wide --field-selector spec.nodeName=cka-worker2The kubelet: stopped, crash-looping, or misconfigured
On the node, the first command is always systemctl status kubelet. It has three answers. inactive (dead): someone stopped it; systemctl start kubelet brings the node back within seconds, and systemctl enable kubelet makes sure a reboot does not undo it (the exam often checks this). activating (auto-restart) with Result: exit-code: the kubelet starts and exits at once, over and over; the reason is in its journal, and it is almost always a path in its configuration. active (running) while the node is still NotReady: the kubelet runs but cannot do its job; again the journal says why, typically the API server address, a certificate, or the container runtime.
systemctl status kubelet --no-pager
journalctl -u kubelet -n 50 --no-pager # the last 50 lines; the error repeats near the end
journalctl -u kubelet -f # follow while you fix; Ctrl-C to stop
systemctl start kubelet
systemctl enable kubelet
systemctl restart kubelet # after editing its configuration
systemctl is-active kubelet; systemctl is-enabled kubeletWhere the kubelet's configuration lives
systemctl cat kubelet | grep -E 'ExecStart|Environment'
cat /var/lib/kubelet/kubeadm-flags.env
grep -nE 'staticPodPath|clientCAFile|containerRuntimeEndpoint|clusterDNS|cgroupDriver' /var/lib/kubelet/config.yaml
grep server: /etc/kubernetes/kubelet.conf
ls -l /var/lib/kubelet/pki/A broken config.yaml produces a journal line such as failed to run Kubelet: unable to load client CA file /etc/kubernetes/pki/ca.crtt: open …: no such file or directory, or a YAML unmarshal error with a line number. Read the path it quotes, ls -l it, fix the file, systemctl restart kubelet. A wrong staticPodPath does not stop a worker (it has no static Pods), but on the control plane it makes the whole control plane vanish, which is the previous lesson's problem. A wrong cgroupDriver or containerRuntimeEndpoint fails the kubelet's startup checks the same way; the message names the value.
Certificates and the kubelet's kubeconfig
/etc/kubernetes/kubelet.conf points at /var/lib/kubelet/pki/kubelet-client-current.pem, a symlink to the latest rotated certificate; with rotateCertificates: true the kubelet renews it before expiry. If the node has been off for longer than the certificate lived, or the link is broken, the journal shows Unauthorized or x509: certificate has expired while the kubelet is active and the node stays NotReady. Check expiry with openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates -subject. The repair on a kubeadm node is a fresh client certificate through the CSR API or re-joining the node, which the certificates lesson covers; on the exam, confirming the expiry and naming the cause is usually the hard part.
Container runtime down, disk full
The kubelet needs containerd through /run/containerd/containerd.sock. With containerd stopped, crictl ps fails with connect: no such file or directory, the kubelet journal repeats container runtime is down or PLEG is not healthy, and the node reports Ready False with reason KubeletNotReady. The fix is systemctl start containerd (and enable); the kubelet reconnects by itself. crictl reads its endpoint from /etc/crictl.yaml; if it complains about the socket while containerd runs, that file is pointing somewhere else.
DiskPressure makes the kubelet evict Pods (they show Evicted in kubectl get pods) and adds the taint node.kubernetes.io/disk-pressure:NoSchedule so nothing new lands there. On the node, df -h /var/lib/containerd /var/lib/kubelet shows which filesystem is full. crictl rmi --prune removes unused images, journalctl --vacuum-size=200M shrinks the journal, and finding the one Pod writing logs into an emptyDir is usually the real answer. The thresholds are evictionHard in config.yaml; the lab's kind nodes set them to 0% so a full laptop does not evict your Pods.
What happens to the Pods on a NotReady node
- t = 0. The kubelet stops. Containers keep running under containerd; the Pods still answer traffic. Nothing in the cluster notices yet.
- t ≈ 50s. The node controller marks Ready
Unknown. It taints the nodenode.kubernetes.io/unreachable:NoExecute(ornode.kubernetes.io/not-ready:NoExecutefor Ready False). The scheduler stops placing Pods there. - t ≈ 50s to 5m50s. Nothing is evicted. Every Pod carries two default tolerations,
not-readyandunreachablefortolerationSeconds: 300, added by the DefaultTolerationSeconds admission plugin. kubectl still lists the Pods asRunning; the node controller only sets their Ready condition to false, which drops them out of their Services' endpoints. - t ≈ 5m50s. The tolerations expire and the Pods are deleted through the API. Their Deployments and ReplicaSets create replacements on healthy nodes. The originals show
Terminatinguntil the kubelet returns to confirm, or forever if it does not;kubectl delete pod --force --grace-period=0removes the record. - Exceptions. DaemonSet Pods tolerate both taints indefinitely and stay. Bare Pods are deleted and never replaced. StatefulSet Pods are not replaced until the old one is confirmed gone, by design, so a StatefulSet on a dead node needs you to force-delete its Pod.
kubectl get pod <pod> -o jsonpath='{.spec.tolerations}' | tr ',' '\n'
# {"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300}
# {"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}
kubectl get node cka-worker2 -o jsonpath='{.spec.taints}'cordon and drain as a tool
When a node is sick but still reachable (a failing disk, a runtime you are about to restart), do not wait for the node controller: kubectl cordon marks it unschedulable at once, and kubectl drain evicts its Pods respecting PodDisruptionBudgets so their controllers recreate them elsewhere now, not in six minutes. Fix the node, then kubectl uncordon. The cluster-upgrades lesson uses the same commands for maintenance.
kubectl cordon cka-worker2
kubectl drain cka-worker2 --ignore-daemonsets --delete-emptydir-data --timeout=120s
# ... repair the node ...
kubectl uncordon cka-worker2
kubectl get nodes # SchedulingDisabled must be goneThe node checklist on one screen
In the docs · the tab you get on exam day
- Nodes ↗search: “node heartbeats”
- Node Status ↗search: “node conditions”
- Kubelet Configuration (v1beta1) ↗search: “kubelet configuration”
- Taints and Tolerations ↗search: “taint based evictions”
context · kubectl config use-context kind-cka
Find a node by role and record its address
node-role.kubernetes.io/control-plane) and write its InternalIP address, and nothing else, to /tmp/cka/control-plane-ip.txt.- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-1/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-1/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-1/cleanup | bash
context · kubectl config use-context kind-cka
Bring a NotReady node back
cka-worker2 is reported as NotReady. Investigate on the node and bring it back to Ready. The fix must survive a reboot of the node.Setup breaks part of your lab on purpose. Run the cleanup step when you finish, or reset the lab.
- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-2/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-2/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-2/cleanup | bash
context · kubectl config use-context kind-cka
A kubelet that will not start
cka-worker2 went NotReady after a configuration change on the node. The kubelet's configuration file must stay where it is. Find the problem, fix it and make the node Ready again.Setup breaks part of your lab on purpose. Run the cleanup step when you finish, or reset the lab.
- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-3/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-3/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/ts-node-3/cleanup | bash
Finished reading?
0 of 3 tasks passed. Readiness counts tasks, so run them before you move on.