Einführung
Ein Pod-Status wie Pending oder CrashLoopBackOff ist ein Symptom, nicht die eigentliche Ursache. Eine effektive Fehleranalyse folgt einer festen Reihenfolge: Objektkonfiguration, Bedingungen, Ereignisse, Zustand der einzelnen Container, Logs und anschließend die Untersuchung des laufenden Systems.
Sie werden zwei vorbereitete Fehler diagnostizieren und beheben und danach einen gesunden Multi-Container-Pod untersuchen. Ziel ist eine wiederverwendbare Methode zur Fehleranalyse, nicht das Auswendiglernen einer einzelnen Lösung.
Gehen Sie bei jedem Vorfall in derselben Reihenfolge vor: Beginnen Sie mit einem groben Überblick, grenzen Sie die Untersuchung auf ein Objekt ein, lesen Sie dessen aktuellen Zustand und Ereignisse, prüfen Sie containerbezogene Hinweise und korrigieren Sie anschließend den zuständigen Controller. So vermeiden Sie zufällige Änderungen und können erklären, warum eine Lösung funktioniert hat.
Mit dem Pod-Zustand beginnen
In diesem Schritt ermitteln Sie, welche Workloads fehlerfrei laufen und welche untersucht werden müssen. Beginnen Sie mit einer Übersicht über den gesamten Namespace. -o wide behält die vertraute Tabelle bei und ergänzt sie um nützliche Spalten wie die Pod-IP-Adresse und den zugewiesenen Node.
cd /home/labex/project/pod-troubleshooting
kubectl get deployments,pods -n practice-debug -o wide
Der gesunde Pod sollte 2/2 Running anzeigen. Einer der Deployment-Pods sollte ein Problem beim Abrufen des Images aufweisen, während der Worker wiederholt neu gestartet wird.
Ein typisches Ergebnis aus der Testumgebung sieht folgendermaßen aus (Pod-Suffixe, Alter und Anzahl der Neustarts können abweichen):
NAME READY STATUS RESTARTS AGE
pod/broken-image-58758b4d69-c7j7g 0/1 ImagePullBackOff 0 33s
pod/crashy-worker-5557cfb974-xwpst 0/1 Error 2 33s
pod/web-with-sidecar 2/2 Running 0 33s
Keine Sorge, wenn der Worker momentan Error statt CrashLoopBackOff anzeigt. Er läuft kurz, beendet sich und wartet, bevor Kubernetes ihn erneut startet. Daher kann sich der angezeigte Status während der Beobachtung ändern. Der steigende Wert bei RESTARTS ist der deutlichere Hinweis darauf, dass dieser Container wiederholt fehlschlägt.
Zeigen Sie die Wartegründe auf Containerebene an, ohne das gesamte Pod-YAML zu lesen:
kubectl get pods -n practice-debug -o custom-columns='POD:.metadata.name,PHASE:.status.phase,READY:.status.containerStatuses[*].ready,WAITING_REASON:.status.containerStatuses[*].state.waiting.reason,RESTARTS:.status.containerStatuses[*].restartCount'
[*] bedeutet „jedes Listenelement“. Deshalb funktioniert dieselbe Tabelle auch für Pods mit mehreren Containern. Nutzen Sie diese erste Übersicht, um zu entscheiden, welcher Diagnosebefehl als Nächstes sinnvoll ist.
WAITING_REASON=ImagePullBackOff deutet beispielsweise auf eine fehlerhafte Image-Konfiguration oder ein Problem beim Zugriff auf die Registry hin. Beim Worker kann der Wartegrund leer sein, während der letzte Versuch den Zustand Terminated aufweist. Deshalb dient diese Tabelle nur als Ausgangspunkt und nicht als vollständige Diagnose.
Nehmen Sie zu diesem Zeitpunkt noch keine Reparaturen vor. Der Image-Fehler und der sich wiederholende Neustart des Workers sind unterschiedliche Symptome und erfordern unterschiedliche Belege. Ein guter erster Befehl schränkt den Suchbereich ein, ohne die Ursache voreilig anzunehmen.
Einen Fehler beim Abrufen eines Images diagnostizieren
In diesem Schritt verfolgen Sie den Image-Fehler vom Status bis zu den Ereignissen. Speichern Sie den erzeugten Pod-Namen, damit spätere Befehle dasselbe Objekt ansprechen:
IMAGE_POD=$(kubectl get pods -n practice-debug -l app=broken-image -o jsonpath='{.items[0].metadata.name}')
echo "$IMAGE_POD"
kubectl describe pod "$IMAGE_POD" -n practice-debug
Lesen Sie die Ausgabe von unten nach oben. Im Abschnitt Events sollte zu sehen sein, dass Kubernetes nginx:not-a-real-tag nicht abrufen kann. Prüfen Sie das konfigurierte Image direkt:
kubectl get pod "$IMAGE_POD" -n practice-debug -o jsonpath='{.spec.containers[0].image}{"\n"}'
kubectl events -n practice-debug --for "pod/${IMAGE_POD}"
Ereignisse erklären, welche Aktion fehlgeschlagen ist, während die JSONPath-Abfrage nachweist, welches gewünschte Image die Ursache war. Die Kombination beider Informationen verhindert, dass ein vorübergehendes Registry-Problem mit einem dauerhaft ungültigen Tag verwechselt wird.
In der getesteten Umgebung sahen die entscheidenden Zeilen ungefähr so aus:
Image: nginx:not-a-real-tag
State: Waiting
Reason: ErrImagePull
Failed to pull image "nginx:not-a-real-tag": ... manifest unknown
Error: ImagePullBackOff
ErrImagePull protokolliert einen fehlgeschlagenen Abrufversuch. ImagePullBackOff bedeutet, dass Kubernetes vor dem nächsten Versuch desselben Vorgangs nun eine Wartezeit einlegt. Die Meldung manifest unknown und der ungültige Tag verbinden das Symptom mit seiner Ursache.
Reparieren Sie den Deployment-Controller, statt den erzeugten Pod zu bearbeiten. kubectl set image ändert die Pod-Vorlage, sodass der Deployment-Controller den fehlerhaften Pod ersetzt:
kubectl set image deployment/broken-image web=nginx:1.27-alpine -n practice-debug
kubectl rollout status deployment/broken-image -n practice-debug --timeout=60s
Am Ende der erfolgreichen Ausführung sollte Folgendes angezeigt werden:
deployment.apps/broken-image image updated
deployment "broken-image" successfully rolled out
Der alte Pod ist vergänglich; die Vorlage des Deployments definiert dagegen den dauerhaft gewünschten Zustand. Nach dem Rollout sollte ein neuer Pod-Name erscheinen und das Deployment ein verfügbares Replikat melden.
Aktuelle und vorherige Logs lesen
In diesem Schritt diagnostizieren Sie einen Container, der startet und sich anschließend beendet. Ermitteln Sie seinen Pod und prüfen Sie die Informationen zu den Neustarts:
WORKER_POD=$(kubectl get pods -n practice-debug -l app=crashy-worker -o jsonpath='{.items[0].metadata.name}')
kubectl get pod "$WORKER_POD" -n practice-debug
kubectl describe pod "$WORKER_POD" -n practice-debug
Bei einem Container, der neu gestartet wird, liest kubectl logs standardmäßig die Logs des aktuellen Versuchs. Mit --previous fordern Sie vom Container-Laufzeitsystem die unmittelbar zuvor beendete Ausführung an:
kubectl logs "$WORKER_POD" -n practice-debug -c worker
kubectl logs "$WORKER_POD" -n practice-debug -c worker --previous || \
echo "The previous container log is no longer available; continue with the current log and describe output."
Die aktuelle oder vorherige Ausgabe sollte die eigene Meldung der Anwendung zeigen:
configuration missing
--previous ist besonders hilfreich, wenn bereits ein neuer Versuch gestartet wurde, bevor Sie den fehlgeschlagenen untersuchen konnten. Es handelt sich jedoch um historische Informationen, die nicht dauerhaft garantiert sind: Die lokale Laufzeitumgebung kann den Datensatz dieses Containers bereits entfernt haben. Falls unable to retrieve container logs angezeigt wird, ist die Fallback-Meldung erwartungsgemäß. Kombinieren Sie das verfügbare aktuelle Log mit describe. Dessen Angaben zu Last State, Exit Code und Restart Count belegen weiterhin, dass der Prozess wiederholt beendet wurde.
Mit der Option -c worker geben Sie den Container ausdrücklich an. Reparieren Sie den Controller, indem Sie den fehlschlagenden Befehl durch eine dauerhaft laufende Schleife ersetzen:
kubectl patch deployment crashy-worker -n practice-debug --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/command","value":["sh","-c","echo worker-ready; while true; do sleep 30; done"]}]'
kubectl rollout status deployment/crashy-worker -n practice-debug --timeout=60s
Der lange Patch kann zunächst einschüchternd wirken. Lesen Sie ihn daher von innen nach außen: path wählt den command des ersten Containers aus, op: replace legt die Art der Änderung fest und value liefert das neue Befehlsarray. Sie ändern nur ein einziges Feld; der übrige Teil des Deployments bleibt unverändert.
Der JSON-Patch ersetzt gezielt ein Feld in der Pod-Vorlage des Deployments. In einer Produktionsumgebung sollten Sie die entsprechende Änderung zusätzlich im Quellmanifest vornehmen.
Warten Sie auf den Rollout-Status, denn der Befehl ändert den gewünschten Zustand sofort, während Ersatz und Bereitschaft asynchron erfolgen. Eine erfolgreiche API-Antwort ist noch kein Beweis für einen gesunden Workload.
Container in einem Multi-Container-Pod gezielt ansprechen
In diesem Schritt untersuchen Sie einen Pod, dessen Container unterschiedliche Aufgaben übernehmen. Listen Sie zunächst ihre Namen auf:
kubectl get pod web-with-sidecar -n practice-debug -o jsonpath='{range .spec.containers[*]}{.name}{"\n"}{end}'
Der JSONPath-Ausdruck range durchläuft die Containerliste. Lesen Sie die Logs jedes Containers unabhängig voneinander:
kubectl logs web-with-sidecar -n practice-debug -c reporter --tail=5
kubectl logs web-with-sidecar -n practice-debug -c web --tail=5
--tail=5 beschränkt die Ausgabe auf die letzten fünf Zeilen. Der Reporter gibt normalerweise wiederholt die Meldung reporter-alive aus. NGINX kann Startmeldungen oder noch keine Request-Logs anzeigen. Beides ist normal, da bisher niemand eine HTTP-Anfrage gesendet hat.
Führen Sie einen Befehl im Web-Container aus:
kubectl exec web-with-sidecar -n practice-debug -c web -- nginx -v
Das getestete Image lieferte folgende Ausgabe:
nginx version: nginx/1.27.5
Die genaue Patch-Version kann sich ändern, wenn das Image aktualisiert wird. Entscheidend ist, dass der Befehl erfolgreich im Container web ausgeführt wurde.
Das erste -- beendet die Optionen von kubectl. Alles danach wird als Befehl im ausgewählten Container ausgeführt.
Dieser Unterschied ist wichtig: -c web wird von kubectl interpretiert, während nginx -v innerhalb des Containers läuft. Wenn Sie -c bei einem Multi-Container-Pod weglassen, wählt kubectl möglicherweise einen Standardcontainer aus und zeigt Informationen des falschen Prozesses.
Container in einem Pod teilen sich dessen Netzwerk-Namespace. Daher kann der BusyBox-Reporter NGINX ebenfalls über die Loopback-Adresse ansprechen. Bei wget -qO- unterdrückt -q Fortschrittsmeldungen und -O- schreibt den Antworttext auf die Standardausgabe. Die Pipe leitet diesen Text an head weiter, das nur die ersten Zeilen ausgibt:
kubectl exec web-with-sidecar -n practice-debug -c reporter -- wget -qO- http://127.0.0.1 | head
Die Antwort sollte mit <!DOCTYPE html> beginnen. Dieses Diagnosemuster lässt sich auch auf spätere Aufgaben übertragen: Führen Sie einen kleinen HTTP-Client innerhalb eines vorbereiteten Pods aus und prüfen Sie das Verhalten der Anwendung, ohne einen neuen externen Port bereitzustellen.
Die Wiederherstellung aus mehreren Blickwinkeln bestätigen
In diesem Schritt überprüfen Sie, ob die Reparatur der Controller gesunde Ersatz-Pods hervorgebracht hat. Prüfen Sie die Verfügbarkeit der Controller und die Bereitschaft der Pods gemeinsam:
kubectl get deployments,pods -n practice-debug
kubectl get pods -n practice-debug -o custom-columns='POD:.metadata.name,READY:.status.containerStatuses[*].ready,RESTARTS:.status.containerStatuses[*].restartCount,OWNER:.metadata.ownerReferences[0].name'
Prüfen Sie abschließend, ob aktuelle Warnereignisse noch auf aktive Fehler hinweisen:
Mit --field-selector type=Warning fordert der Befehl vom API-Server ausschließlich Ereignisse der Kategorie Warning an. --sort-by=.metadata.creationTimestamp sortiert diese anschließend vom ältesten zum neuesten Ereignis, sodass Sie historische Fehler vom aktuellen Zustand unterscheiden können.
kubectl get events -n practice-debug --field-selector type=Warning --sort-by=.metadata.creationTimestamp
Alte Warnungen können auch nach der Wiederherstellung bestehen bleiben. Für den aktuellen Gesundheitszustand sind die Bedingungen der aktuellen Pods und die Verfügbarkeit der Controller maßgeblich. Die Ereignisse erklären dagegen, wie das System diesen Zustand erreicht hat.
Nach den beiden Reparaturen sollte die Ausgabe des aktuellen Zustands ungefähr so aussehen:
NAME READY UP-TO-DATE AVAILABLE
deployment.apps/broken-image 1/1 1 1
deployment.apps/crashy-worker 1/1 1 1
NAME READY STATUS RESTARTS
pod/broken-image-... 1/1 Running 0
pod/crashy-worker-... 1/1 Running 0
pod/web-with-sidecar 2/2 Running 0
Möglicherweise sehen Sie weiterhin alte Warnungen wie Failed, BackOff oder sogar ein frühes FailedCreatePodSandBox aus dem Clusterstart. Vergleichen Sie Objektbezeichnungen und Zeitstempel: Die Warnungen beziehen sich auf ersetzte Pods, während die neuen Pods Running sind und beide Deployments als verfügbar gemeldet werden. Ereignisse sind eine Historie und kein Live-Dashboard für den Gesundheitszustand.
Ihre abschließende Bewertung sollte alle aktuellen Hinweise zusammenführen: Beide Deployments sind verfügbar, alle vorgesehenen Container sind bereit, und kein aktiver Pod befindet sich mehr in einem Zustand zum Abrufen eines Images oder in einer Absturzschleife. Eine Fehleranalyse endet mit der Überprüfung, nicht mit dem Reparaturbefehl.
Zusammenfassung
Sie haben Fehler beim Abrufen von Images und beim Starten von Containern mithilfe von Bedingungen, Ereignissen, Containerzuständen, aktuellen Logs und vorherigen Logs diagnostiziert. Sie haben die Vorlagen der Controller statt der erzeugten Pods repariert und gelernt, einzelne Container gezielt mit logs und exec anzusprechen. In der nächsten Aufgabe wenden Sie diesen evidenzbasierten Ablauf ohne schrittweise Anleitung an.


