Das Symptom
Der Pod steht in der Übersicht auf CrashLoopBackOff, die Spalte mit den Neustarts
zählt munter weiter, und die Anwendung ist nie erreichbar. Wer kubectl logs
aufruft, bekommt entweder gar nichts oder eine Fehlermeldung, dass der Container
gerade nicht läuft. Und je länger man zusieht, desto träger wird das Ganze: Anfangs
versucht es der Pod im Sekundentakt, später vergehen Minuten zwischen zwei
Versuchen.
Die „Layer 8“-Wahrheit
CrashLoopBackOff ist keine Fehlfunktion von Kubernetes. Es ist ein Lagebericht,
und zwar ein erstaunlich präziser: Dein Container startet, bricht ab, startet wieder
und bricht wieder ab – und weil das kubelet nicht bereit ist, den Knoten deswegen mit
tausend Startversuchen pro Minute zu beschäftigen, wartet es zwischen den Versuchen
jedes Mal länger. Die Dokumentation nennt die Reihe ausdrücklich: 10 Sekunden, 20,
40, und so weiter, gedeckelt bei 300 Sekunden, also fünf Minuten. Läuft der Container
zehn Minuten am Stück fehlerfrei, wird der Zähler zurückgesetzt. Das ist kein Defekt,
das ist eine Bremse.
Der eigentliche Denkfehler sitzt eine Ebene höher, nämlich beim Wort „Logs“. Der
naheliegende Griff nach kubectl logs zeigt den laufenden Container – und der ist im
Wartefenster schlicht nicht vorhanden. Es sieht also so aus, als schwiege die
Anwendung, dabei hat sie bei jedem einzelnen Todesfall etwas hinterlassen. Man muss
nur den vorherigen Durchlauf anfordern. Genau dafür existiert --previous, und genau
dieser eine Schalter trennt die Leute, die eine Stunde ratlos auf ein leeres Terminal
starren, von denen, die nach dreißig Sekunden die fehlende Umgebungsvariable in der
Hand halten.
Der Rest ist unspektakulär. Die Dokumentation zählt die üblichen Verdächtigen
nüchtern auf: Fehler in der Anwendung, falsche oder fehlende Konfiguration, zu knapp
bemessener Speicher, fehlschlagende Health-Checks, überempfindliche Liveness- oder
Startup-Probes. Kubernetes hat keinen dieser Fehler gemacht. Es meldet ihn nur
hartnäckiger, als es einem lieb ist – und in wachsenden Abständen, damit man Zeit hat,
darüber nachzudenken.
Die echte, verifizierte Lösung
1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:
1.Status und Zahl der Neustarts ablesen
Zuerst die nüchterne Übersicht. Die Spalte RESTARTS zeigt, wie oft es das kubelet schon versucht hat, die Spalte STATUS den Zustand. Eine dreistellige Zahl bedeutet: Das Problem besteht seit Stunden und niemand hat hingesehen.
kubectl get pods
2.Zustand und Ereignisse des Pods lesen
Der eine Befehl, mit dem laut Kubernetes-Dokumentation jede Pod-Fehlersuche beginnt. Er zeigt den Zustand jedes Containers samt Grund sowie die letzten Ereignisse – dort steht, ob überhaupt gestartet wurde, wann und mit welchem Ergebnis er endete.
kubectl describe pod POD-NAME
3.Die Logs des vorherigen Durchlaufs holen
Hier steht in aller Regel die Antwort: die letzte Ausgabe, bevor der Container starb. Ohne diesen Schalter sieht man den aktuellen – oft noch gar nicht existierenden – Durchlauf und damit nichts. Bei mehreren Containern im Pod den gewünschten mit -c benennen.
kubectl logs POD-NAME --previous
kubectl logs POD-NAME -c CONTAINER-NAME --previous
4.Grund und Exit-Code des letzten Laufs auslesen
Der vollständige Zustand als YAML enthält unter lastState.terminated den Grund und den Exit-Code der letzten Beendigung. Steht dort OOMKilled, ist es kein Programmfehler, sondern das Speicherlimit. Steht dort ein Exit-Code ungleich null, hat sich die Anwendung selbst beendet.
kubectl get pod POD-NAME -o yaml
5.Die Ereignisse gezielt nach Warnungen filtern
Ereignisse sind flüchtig, aber aussagekräftig: fehlgeschlagene Probes, nicht einhängbare Volumes, abgelehnte Images. Der Befehl zeigt sie für genau diesen Pod, bei Bedarf beschränkt auf Warnungen.
kubectl events --for pod/POD-NAME
kubectl events --types=Warning
6.Die gefundene Ursache beheben, nicht den Zustand
Fehlende Umgebungsvariable, falscher Pfad, nicht erreichbare Datenbank, zu kleines Speicherlimit, zu strenge Liveness-Probe: Erst wenn der Container einen vollen Durchlauf übersteht, hört die Schleife auf. Nach zehn Minuten fehlerfreiem Lauf setzt das kubelet die Wartezeit ohnehin selbst zurück – ein Löschen des Pods ist dafür nicht nötig.