Troubleshooting#
Everything that follows builds on one rule: status codes first, logs second, describe always. After writing dozens of deployments in the v1 notes, here’s the operator-grade field guide.
Pod Status Quick Reference#
| Status | Anlamı | İlk bakılacak yer |
|---|---|---|
Pending | Node’a schedule edilemedi | describe → Events (resources? taints? affinity? quota?) |
ImagePullBackOff | Image çekilemiyor (tag/repo/auth) | describe → Events + image adı |
CrashLoopBackOff | Başlıyor, çöküyor, üstel beklemeyle tekrar | logs --previous + exit code |
ErrImagePull | ImagePullBackOff‘un ilk hali | Aynı |
Running ama broken | Process yaşıyor, app ölü | logs + port-forward ile elle dene |
OOMKilled | Memory limiti aşıldı | describe → Last State |
Exit Code Cheat Sheet#
The exit code in Last State: Terminated usually tells the whole story:
| Code | Kim gönderdi | Ne anlama gelir |
|---|---|---|
0 | Uygulama | Temiz çıkış - Job’larda başarı, servislerde şüpheli |
1 | Uygulama | Genel hata - stack trace’e bak |
126 | Runtime | Komut çalıştırılamadı (izin/execute bit) |
127 | Runtime | Komut yok (command: yazım hatasının klasik belirtisi) |
137 | Kernel (SIGKILL) | 128+9 → OOMKilled ya da elle kill -9 |
139 | Kernel (SIGSEGV) | Segmentation fault |
143 | Kernel (SIGTERM) | 128+15 → k8s’in normal kapatması (graceful shutdown) |
137and143are just128 + signal number. Seeing137on a memory-limited pod? That’s the kernel enforcing your limit - go re-read the scaling notes on limits vs requests.
The Golden Path#
# 1. Hangi pod, hangi durumda?
kubectl get pods
# 2. Detay + Events - okunacak en zengin kaynak
kubectl describe pod <pod-name>
# 3. Loglar; çöken pod'un önceki attempt'ı için --previous
kubectl logs <pod-name>
kubectl logs <pod-name> --previous
kubectl logs <pod-name> -c <container-name>
# 4. İçeri girip elle dene
kubectl exec -it <pod-name> -- sh
# 5. Cluster geneli olay akışı (neden Pending olduğunu burada da görürsün)
kubectl get events --sort-by='.lastTimestamp'
kubectl get events --field-selector reason=FailedSchedulingUseful structured queries once describe walls of text get tiring:
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}{" exit="}{.status.containerStatuses[0].lastState.terminated.exitCode}{"\n"}'
kubectl get pod <pod> -o jsonpath='{.spec.containers[*].image}{"\n"}'
kubectl get events --field-selector involvedObject.name=<pod>And when you’re unsure what a field even is:
kubectl explain pod.spec.securityContext --recursive | lessDon’t forget
get endpoints <service>for “service returns 404/connection refused” mysteries: an empty endpoints list means the service’s selector matches no pods - the app may be perfectly healthy. Half of “broken ingress” bugs are selector mismatches, not app bugs.
kubectl debug: Debugging Without a Shell#
exec requires a shell in the image - distroless images have none, and adding tools to prod images is bad practice anyway. Ephemeral containers solve this:
kubectl debug -it <pod-name> --image=cilium/netshoot:latest --target=appWhat happens: a temporary container joins the pod’s process namespace, so you can inspect the target container’s processes (ps aux) - no shell required in the target image. --target even shares the target’s process namespace specifically.
For node-level problems (disk, kubelet, network interfaces):
kubectl debug node/<node-name> -it --image=ubuntu:22.04You get a container on that node with the host’s filesystem mounted at /host - the node’s logs live at /host/var/log, kubelet’s journal via journalctl (run chroot /host first for full tooling).
Türkçe homelab checklist (kafam karıştığında sırayla uyguladığım liste):
- Pod durumu ne? (
get pods) - Events ne diyor? (
describe- Events ve Last State bölümleri) - Log’da stack trace var mı? (
logs --previous) - Exit code ne? (yukarıdaki tablo -
137= OOM,127= command yazım hatası) - Env var’lar configmap/secret’tan geliyorsa, o key gerçekten var mı?
- Selector’lar eşleşiyor mu? (deployment selector ↔ pod labels; service selector ↔ endpoints)
- PVC bağlıysa mount path ve izinler doğru mu? (
fsGrouphatırlatması: security-context) - İzinler/root sorunları distroless image’larda mı çıktı? (distroless notları)
How to Diagnose a CrashLoopBackOff#
- Cause one: point the api deployment at a ConfigMap key that doesn’t exist (or delete a key it needs), then watch:
kubectl get pods --watch- Read the state machine:
kubectl get pod <api-pod> -o jsonpath='{.status.containerStatuses[0].state}{"\n"}'waiting.reason: CrashLoopBackOff - and restartCount climbing.
- Get the last attempt’s story:
kubectl logs <api-pod> --previous | tail -n 20- Cross-check the exit code in
describe:
kubectl describe pod <api-pod> | grep -A5 "Last State"The distinction between
logsanddescribeis where beginners lose time:logsshows what the app printed before dying;describe’s Events andLast Stateshow what k8s observed. A container that dies before printing anything has an emptylogsand a tellingdescribe- missing env, missing file, bad command (exit127).
- Fix the root cause (restore the ConfigMap key), and watch
restartCountreset with the new pod. Note: k8s does not reset the backoff timer for the same pod; after repeated fixes,delete podis faster than waiting out a long backoff.
How to Debug a Shell-less Container#
- Distroless-style: attach a toolbox container to a running pod and share process visibility:
kubectl debug -it <api-pod> --image=cilium/netshoot:latest --target=synergychat-api- Inside the debug container, inspect the target’s processes:
ps auxYou’ll see the target container’s process tree (thanks to --target + process namespace sharing).
- Test network reachability from inside the pod’s network namespace - the debug container shares the pod’s IP, so this tests exactly what the app pod sees:
curl --max-time 3 http://api-service:8080/healthz- Cleanup happens automatically when you exit (ephemeral containers don’t survive pod restarts) - verify with
kubectl get pod <pod> -o jsonpath='{.spec.ephemeralContainers}'.
How to Trace Scheduling Failures#
- Reproduce a
Pendingfrom the v1 notes - set an absurd resource request ontestram:
kubectl set resources deployment/synergychat-testram --limits=memory=40000Mi --requests=memory=40000Mi
kubectl get pods --watch- Filter the noise out of global events:
kubectl get events --field-selector reason=FailedSchedulingYou’ll see exactly why: 0/1 nodes are available: 1 Insufficient memory.
Confirm the decision logic in
describe(the Events section repeats the same verdict with more context), then reason about the fix path: lower the request, add a node (minikube node add), or remove a taint that blocks the only candidate node.Clean up and verify recovery:
kubectl set resources deployment/synergychat-testram --limits=memory=256Mi --requests=memory=128Mi
kubectl get podsfor more control-plane-troubleshooting