10.2 Cluster & Node-Level Troubleshooting

Key Takeaways

  • Triage top-down: confirm the control plane answers, then node health, then networking and DNS, then the workload — most reported Pod problems are actually node or cluster problems.
  • A node reports NotReady when the kubelet stops sending heartbeats, and after a grace period the node controller marks its Pods for eviction.
  • MemoryPressure and DiskPressure trigger kubelet eviction, which removes BestEffort Pods first, then Burstable, then Guaranteed.
  • Events are the highest-value diagnostic surface in Kubernetes but expire after roughly one hour by default, so they must be shipped somewhere durable.
  • A Pending Pod is a scheduling problem, ImagePullBackOff is a registry or credential problem, and CrashLoopBackOff is an application problem — the phase names the layer.
Last updated: August 2026

10.2 Cluster & Node-Level Troubleshooting

Quick Answer: Troubleshoot top-down. Confirm the control plane answers, then node health, then networking and DNS, then the workload. A large share of incidents reported as "my Pod is broken" are in fact a NotReady node, an exhausted disk, or a CNI failure. The state a Pod is stuck in names the layer at fault: Pending is scheduling, ImagePullBackOff is registry or credentials, CrashLoopBackOff is the application.

Section 10.1 covered probes and container-level debugging. This section covers everything above the Pod.


1. A Triage Order That Works

1. CONTROL PLANE   kubectl get --raw='/readyz?verbose'
                   kubectl get componentstatuses          (legacy but quick)
                   ↓ answers?
2. NODES           kubectl get nodes -o wide
                   kubectl describe node <n>   ← conditions + events + allocated resources
                   ↓ all Ready?
3. CLUSTER ADD-ONS kubectl get pods -n kube-system        ← CoreDNS, CNI, kube-proxy
                   ↓ all Running?
4. NAMESPACE       kubectl get events -n <ns> --sort-by=.lastTimestamp
                   kubectl get pods -n <ns> -o wide
                   ↓ narrowed to one workload?
5. WORKLOAD        kubectl describe pod / logs --previous / exec   (section 10.1)

Running step 5 first is the classic mistake: you read an application stack trace for twenty minutes before noticing the node it sits on has a full disk.


2. Node Conditions and What They Mean

kubectl describe node reports a condition block that is the fastest read in the whole system:

ConditionTrue meansConsequence
ReadyThe kubelet is healthy and accepting PodsFalse/Unknown ⇒ node controller begins evicting after a grace period (~5 minutes by default)
MemoryPressureAvailable memory has fallen below the eviction thresholdKubelet evicts Pods and taints the node node.kubernetes.io/memory-pressure
DiskPressureRoot or image filesystem is critically lowKubelet garbage-collects images and evicts Pods; new image pulls fail
PIDPressureHost process IDs are near exhaustionNew processes and containers fail to start
NetworkUnavailableThe node's network route is not correctly configuredUsually a CNI plugin fault

NotReady — What Actually Happened

Ready: Unknown means the kubelet stopped renewing its Lease in kube-node-lease. Common causes, in rough order of frequency:

  1. The kubelet process crashed or is wedged — systemctl status kubelet, journalctl -u kubelet.
  2. The container runtime (containerd/CRI-O) is down — the kubelet cannot report container status.
  3. The CNI plugin is failing — the node cannot satisfy the Kubernetes networking model.
  4. Network partition between node and control plane.
  5. Resource exhaustion on the node making the kubelet unresponsive.
  6. Expired certificates — the kubelet's client certificate lapsed and it can no longer authenticate.

When the node controller declares a node lost, Pods managed by controllers are recreated elsewhere; naked Pods and StatefulSet Pods with attached volumes are the slow cases, because a volume must be detached before it can be attached elsewhere.


3. Eviction Under Pressure

When a node runs low on memory or disk, the kubelet reclaims resources before the kernel OOM killer does something less discriminating. The eviction order follows QoS class, which is where section 4.1's material becomes operational:

Evicted first  ─────────────────────────────────────►  Evicted last
  BestEffort        Burstable (over its request)        Guaranteed
 (no requests)     (requests < limits)              (requests == limits)

Distinguish two lookalike failures:

SignalMeaning
Pod status Evicted, node event NodeHasDiskPressure/NodeHasInsufficientMemoryNode-level eviction — the node ran out, not the container
Container reason OOMKilled, exit code 137Container-level — that container exceeded its own memory limit

The fix differs completely: eviction means the node is under-provisioned or workloads lack requests; OOMKilled means one container's limit is too low or it leaks.


4. Events Are the Highest-Value Surface

kubectl get events -A --sort-by=.lastTimestamp | tail -40
kubectl get events -n production --field-selector type=Warning

Events explain why the control plane did what it did — FailedScheduling with the exact predicate that failed, FailedMount, Unhealthy probe failures, NodeNotReady, Preempted.

The trap: Events are stored in etcd with a default TTL of about one hour. An incident investigated the next morning has no events left. Shipping events to a durable store (an events exporter into Loki or Elasticsearch) is standard practice in any cluster you have to operate.


5. Reading a Pending Pod

Pending always means "not yet placed or not yet started", and kubectl describe pod names the reason precisely:

Event messageCauseFix
Insufficient cpu / Insufficient memoryNo node has enough allocatable capacity for the requestsLower requests, add nodes, or enable the Cluster Autoscaler
node(s) had untolerated taint …Every candidate node is taintedAdd a toleration or untaint
node(s) didn't match Pod's node affinity/selectorNo node carries the required labelsFix the selector or label a node
node(s) didn't find available persistent volumes to bindNo PV matches the PVCCheck the StorageClass, capacity, and access mode
node(s) had volume node affinity conflictThe volume is in a different zone from the candidate nodesUse WaitForFirstConsumer binding
n node(s) didn't match pod topology spread constraintsThe placement would breach maxSkewAdd capacity in the under-filled domain

Allocatable, not capacity. A node's schedulable resources are allocatable — capacity minus kube-reserved, system-reserved, and eviction thresholds. A "16-core" node does not offer 16 cores to workloads, which is why a Pod requesting 16 CPUs never schedules on it.


6. Control Plane and Add-On Failures

SymptomLikely component
Every kubectl command times out or is refusedkube-apiserver down, load balancer misrouting, or expired client certificate
API responds but nothing is ever scheduledkube-scheduler not running or has lost leader election
Deleted objects linger; ReplicaSets do not reconcilekube-controller-manager down
Writes fail, reads work, etcdserver: request timed outetcd lost quorum — check member health and disk latency
Nothing resolves anywhereCoreDNS Pods not Ready, or CNI failure beneath them
Services have endpoints but traffic never arriveskube-proxy failing on that node
LoadBalancer Service stuck <pending>cloud-controller-manager down, or no cloud LB integration

On a kubeadm cluster the control plane runs as static Pods, so it can be inspected with kubectl -n kube-system logs kube-apiserver-<node> — or, when the API is unreachable, directly on the node with crictl ps and crictl logs, which talk to the container runtime without going through Kubernetes at all.


7. Node-Level Commands

When kubectl cannot tell you enough, the answers are on the node:

systemctl status kubelet
journalctl -u kubelet -n 200 --no-pager
crictl ps -a                     # containers, independent of the API server
crictl logs <container-id>
df -h /var/lib/containerd /var/log   # DiskPressure is nearly always here
free -h

crictl is the CRI-level equivalent of docker ps, and it is the tool that still works when the API server is the thing that is broken.

Test Your Knowledge

A Pod shows status Evicted and the node reports NodeHasDiskPressure. What actually happened?

A
B
C
D
Test Your Knowledge

A Pod stays Pending and kubectl describe pod shows 0/8 nodes are available: 8 Insufficient cpu. What is the correct interpretation?

A
B
C
D
Test Your Knowledge

Why do experienced operators ship Kubernetes Events to an external log store?

A
B
C
D