16 — Troubleshoot: debug like an owner
ELI5 hook
When the toy breaks, don’t shake the whole house. Follow the assembly line: Wish (API) → Seat (scheduler) → Babysitter (kubelet) → Runtime (CRI) → Linux (process/net/disk). Each layer has a different flashlight.
Mental model — the ladder
flowchart TB
e1["1. Events + describe"]
e2["2. Workload controller status"]
e3["3. Pod phase / conditions / logs"]
e4["4. Node conditions + kubelet"]
e5["5. CRI: crictl / containerd"]
e6["6. Linux: dmesg mounts iptables"]
e1 --> e2 --> e3 --> e4 --> e5 --> e6
Ask: Is desired state wrong, or is actual state failing to converge?
Playbooks
Pending Pod
kubectl describe pod <name> # Events at the bottom
kubectl get nodes -o wide
kubectl describe nodes | grep -A5 Allocatable
Common: insufficient CPU/memory, taints, PVC unbound, affinity, missing RuntimeClass.
ImagePullBackOff
kubectl describe pod <name> | grep -A5 -i pull
# Check tag/digest, imagePullSecrets, registry auth, rate limits
CrashLoopBackOff
kubectl logs <pod> -c <container> --previous
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[*].lastState}'
Process exited — fix the app/config, not “restart the node.”
Service has no endpoints
kubectl get endpoints,endpointslice -l kubernetes.io/service-name=<svc>
kubectl get pods -l <selector> --show-labels
# readiness failing? label mismatch?
Node NotReady
kubectl describe node <node>
# DiskPressure MemoryPressure NetworkUnavailable
# Then on the node: systemctl status kubelet containerd
Network path
- Pod → Pod IP directly
- Pod → ClusterIP
- DNS:
nslookup kubernetes/wgetto FQDN - NetworkPolicy deny?
- CNI pods healthy?
Volume stuck
kubectl describe pvc <pvc>
kubectl get volumeattachment
kubectl describe pod <pod> # MountVolume timeouts
Lab — deliberate failure drill
# 1) Bad image
kubectl run bad --image=ghcr.io/this-does-not-exist-12345/nope:latest
kubectl describe pod bad | tail -25
kubectl delete pod bad
# 2) Readiness that never passes
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: never-ready
labels:
app: never
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
readinessProbe:
httpGet:
path: /nope
port: 80
periodSeconds: 3
EOF
kubectl expose pod never-ready --port=80 --name=never-svc --selector=app=never
kubectl get endpoints never-svc
kubectl delete pod never-ready svc never-svc
Use labs/08-crictl-inspect.sh when API status lags runtime reality.
Gotchas
- Fixating on logs only — Events explain scheduling/mount/pull; logs explain process.
- Deleting Pending Pods — Loses evidence; describe first, copy Events.
- Wrong container in multi-container Pod — Always
-c. - Clock / cert expiry — Sudden auth failures across the board.
- API server healthy ≠ apps healthy — Climb the full ladder.
Course wrap (core arc)
You can now narrate a request end-to-end:
YAML → API/etcd → controller → Pod → scheduler bind → kubelet → CRI → namespaces/cgroups → process → CNI IP → Service VIP → (optional) Ingress → client.
That story is the Kubernetes core arc.
Keep going for book depth: 17 — etcd deep dive → controllers → CNI → eBPF → CSI.
Quiz: /quizzes/k8s-security-ops/
Milestone: pass all five core K8s quizzes → Cluster Internals. Add deep dives → Cluster Scholar.
Back to the syllabus.