Kubernetes Kubernetes

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.

12 min Anfänger

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. describe sagt, 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 describe angezeigt). 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:

  1. Läuft der Pod und ist READY? Korrigiere zuerst den Pod.
  2. Passt der Service-Selector zu den Labels des Pods? kubectl get pods --show-labels und kubectl describe service NAME sollten übereinstimmen.
  3. Hat der Service Endpoints? kubectl get endpointslices -l kubernetes.io/service-name=NAME sollte Pod-IPs auflisten. Eine leere Liste bedeutet eine Selector-Abweichung oder keine bereiten Pods.
  4. Passen die Ports? Der targetPort des Services muss der Port sein, auf dem der Container tatsächlich lauscht.
  5. 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.
  6. Könnte eine NetworkPolicy es blockieren? Liste sie mit kubectl get networkpolicy.
  7. 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