Einen fehlschlagenden Pod debuggen: describe, logs, exec und port-forward
Eine wiederholbare Checkliste, um herauszufinden, warum ein Pod Pending ist, abstürzt oder nicht antwortet.
Früher oder später startet ein Pod nicht, startet ständig neu oder läuft, antwortet aber nicht. Die Lösung ist fast immer mit einer Handvoll kubectl-Befehlen sichtbar, in der richtigen Reihenfolge eingesetzt. Diese Anleitung gibt dir diese Reihenfolge: den Status lesen, nach Events fragen, die Logs lesen, in den Container gelangen und dann den Netzwerkpfad nachverfolgen.
Zuerst den Status lesen
Beginne mit der Status-Spalte:
kubectl get pods -o wide
Die häufigsten Zustände und wo du zuerst nachsiehst:
- Pending. Der Pod ist keinem Knoten zugewiesen. Meist gibt es nicht genug CPU oder Speicher, eine Quota ist ausgeschöpft oder ein Volume kann nicht binden.
describesagt, welches. - ContainerCreating. Der Knoten bereitet den Container vor. Bleibt das so, prüfe auf ein fehlendes Volume oder einen langsamen Image-Pull.
- ErrImagePull / ImagePullBackOff. Das Image kann nicht gepullt werden, meist wegen eines Tippfehlers im Namen oder Tag oder fehlender Zugangsdaten für eine private Registry.
- CrashLoopBackOff. Der Container startet, beendet sich und wird mit wachsenden Verzögerungen wiederholt neu gestartet. Die Anwendung schlägt fehl, lies also ihre Logs.
- CreateContainerConfigError. Der Pod referenziert eine ConfigMap, ein Secret oder einen Schlüssel, die nicht existieren.
- Running, aber
READY 0/1. Der Container läuft, scheitert aber an seiner Readiness-Probe. - OOMKilled (in
describeangezeigt). Der Container hat sein Speicherlimit überschritten.
Kubernetes fragen, warum: describe und Events
kubectl describe pod POD_NAME
Scrolle zum Abschnitt Events ganz unten. Er liest sich wie eine Zeitleiste: Scheduled, Pulling, Failed, BackOff. Meldungen wie 0/3 nodes are available: insufficient cpu oder Failed to pull image "ngnix:1.25" nennen das Problem meist unverblümt. Weiter oben sieh dir den State und Last State jedes Containers für Exit-Codes und Gründe an. Für eine namespace-weite Sicht dessen, was passiert ist:
kubectl get events --sort-by=.metadata.creationTimestamp
Die Ausgabe des Containers lesen
kubectl logs POD_NAME
kubectl logs POD_NAME -c CONTAINER_NAME
kubectl logs POD_NAME --all-containers
kubectl logs POD_NAME --previous
kubectl logs POD_NAME -f --since=10m --tail=100
Nutze -c, wenn ein Pod mehrere Container hat, -f zum Streamen, --since und --tail, um die Ausgabe zu begrenzen, und --previous, um die Logs der zuletzt abgestürzten Instanz zu lesen, was du bei einer Absturzschleife brauchst. Zum Ausprobieren erstelle ein Deployment, dessen Container absichtlich fehlschlägt:
kubectl create deployment crashy --image=busybox:1.36 -- \
sh -c 'echo starting; echo "fatal: DB_URL is not set" >&2; exit 1'
kubectl get pods -l app=crashy
kubectl logs deployment/crashy --previous
Der Status wird CrashLoopBackOff, und das Log zeigt, warum. Räume mit kubectl delete deployment crashy auf.
Beachte, dass Logs zum Pod gehören. Wird er gelöscht oder ersetzt, werden seine Logs mit aufgeräumt. Für Historie brauchst du also einen Log-Sammler wie Fluentd oder Fluent Bit, der Logs an einen dauerhaften Ort sendet.
Hineingelangen: exec, port-forward und cp
kubectl exec -it POD_NAME -- sh
kubectl exec POD_NAME -- env
kubectl port-forward pod/POD_NAME 8080:80
curl http://localhost:8080
kubectl cp POD_NAME:/path/to/file ./file
exec führt einen Befehl im Container aus, port-forward verbindet einen lokalen Port mit einem Port im Pod (funktioniert auch für svc/NAME), und cp kopiert Dateien hinein oder heraus. Viele Produktions-Images sind minimal und haben weder Shell noch curl. In diesem Fall hängt kubectl debug -it POD_NAME --image=busybox:1.36 --target=CONTAINER_NAME einen temporären Debugging-Container an, der die Namespaces des Pods teilt.
Alles, was du in einem laufenden Container änderst, etwa das Bearbeiten einer Konfigurationsdatei, geht verloren, sobald der Pod ersetzt wird. Nutze exec zum Untersuchen, aber mache die Korrektur im Image, im Manifest oder in einer ConfigMap.
Rückwärts durch den Anfragepfad arbeiten
Sieht der Pod gesund aus, aber die Anwendung ist nicht erreichbar, verfolge den Pfad von innen nach außen:
- Läuft der Pod und ist
READY? Korrigiere zuerst den Pod. - Passt der Service-Selector zu den Labels des Pods?
kubectl get pods --show-labelsundkubectl describe service NAMEsollten übereinstimmen. - Hat der Service Endpoints?
kubectl get endpointslices -l kubernetes.io/service-name=NAMEsollte Pod-IPs auflisten. Eine leere Liste bedeutet eine Selector-Abweichung oder keine bereiten Pods. - Passen die Ports? Der
targetPortdes Services muss der Port sein, auf dem der Container tatsächlich lauscht. - Funktioniert es von einem anderen Pod aus?
kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://NAME. Das testet DNS und den Service und schließt alles außerhalb des Clusters aus. - Könnte eine NetworkPolicy es blockieren? Liste sie mit
kubectl get networkpolicy. - Prüfe zuletzt den Ingress oder externen Load Balancer davor.
Jeder Schritt grenzt das Problem auf eine Schicht ein, was schneller ist als zu raten.
Beliebte Anleitungen
-
Was Kubernetes ist und welche Probleme es löst Warum Container allein nicht mehr skalieren und wie Kubernetes eine Gruppe von Maschinen zu einer Plattform macht. -
Wie ein Kubernetes-Cluster funktioniert: Control Plane und Knoten Die Komponenten, die einen Cluster am Laufen halten, und was zwischen kubectl apply und einem laufenden Container passiert. -
Deployments: Skalieren, Selbstheilung und Rolling Updates Betreibe Replikas eines Pods, aktualisiere sie ohne Ausfallzeit und rolle zurück, wenn ein Release schiefgeht. -
Services: Stabiles Networking für kurzlebige Pods Gib Pods eine stabile Adresse, stelle sie innerhalb und außerhalb des Clusters bereit und beschränke, wer mit wem sprechen darf.