Um 1:49 Uhr riss das Alarmsystem der Zahlungsdienstleistung auf: Drei Pods stürzten gleichzeitig ab und gingen in einen CrashLoopBackOff-Zustand über. Der Bereitschaftsingenieur trat umgehend dem Vorfalls-Meeting bei – nur vier Minuten nach dem ersten Alarm. Dreiundvierzig Minuten später war die Störung behoben. Doch die eigentliche Analyse dauerte ganze 47 Minuten. Nicht wegen technischer Komplexität, sondern aufgrund falscher Priorisierung der Fehlersuche.
Der erste Fehler: Logik folgt Routine statt Diagnose
In Kubernetes gilt bei gleichzeitigen Pod-Abstürzen nach einem Deployment die Faustregel: Die letzte Änderung ist schuld – bis das Gegenteil bewiesen ist. Diese Annahme ist meist berechtigt. Doch im vorliegenden Fall war die letzte Änderung bereits sechs Stunden zuvor um 19:52 Uhr erfolgt. In dieser Zeit hatten die Pods ohne Probleme funktioniert. Dennoch verbrachten die Techniker die ersten zwölf Minuten damit, die Deployment-Historie und Konfigurationsdifferenzen zu prüfen.
kubectl rollout history deployment/payments-service -n production
kubectl describe deployment/payments-service -n productionTrotz gründlicher Prüfung fand sich kein Hinweis auf eine fehlerhafte Konfiguration. Die Pods waren stabil – bis zu dem Zeitpunkt, an dem sie plötzlich und ohne erkennbaren Grund abstürzten. Ein klassischer Fall von Overthinking unter Zeitdruck.
Logs verrieten nur das Symptom, nicht die Ursache
Nach der Entlastung des Deployments konzentrierte sich das Team auf die Analyse der Pod-Logs. Die Befehle zeigten, wie der Dienst normal startete, eine Verbindung zu Redis aufbaute und dann abrupt beendete wurde – ohne Fehlermeldung, Panik oder Stacktrace.
kubectl logs payments-service-7d9f8b-xkp2q --previous -n production
kubectl logs payments-service-7d9f8b-mn4lw --previous -n productionZunächst vermutete das Team einen OutOfMemory-Kill, da der Dienst nur 180 MiB von 512 MiB Speicher nutzte. Doch die Speicherbegrenzungen lagen weit unter dem Limit. Auch Metriken aus Prometheus zeigten keine ungewöhnlichen Spitzen. Nach 15 Minuten intensiver Log-Analyse blieb die Ursache weiterhin unklar.
Der entscheidende Hinweis kam zu spät: K8s-Events
Erst nach 27 Minuten fiel einem Teammitglied ein, die Kubernetes-Events zu prüfen – ein Schritt, der eigentlich am Anfang der Fehlersuche stehen sollte. Die Ausgabe war jedoch aufschlussreich:
kubectl get events -n production --sort-by='.lastTimestamp' | tail -30Darin stand:
Liveness probe failed: context deadline exceededBack-off restarting failed container
Die Liveness-Prüfung des Pods hatte den Timeout überschritten. Doch das Team ging fälschlicherweise davon aus, dass der Health-Endpunkt selbst defekt sein könnte. Zehn weitere Minuten vergingen damit, manuell auf den Endpunkt zuzugreifen oder Pods zu betreten, um die Antwort zu testen. Der Endpunkt funktionierte jedoch einwandfrei.
Die eigentliche Ursache: Ein unscheinbarer Node-Reset
Erst nach 37 Minuten fiel einer der Ingenieure eine entscheidende Beobachtung: „Der Vorfall begann um 1:49 Uhr. Gab es um diese Zeit irgendeine Node-Änderung?“ Ein kurzer Blick in die Node-Events bestätigte den Verdacht:
kubectl get events -n kube-system --sort-by='.lastTimestamp' | grep -E "Node|node"Darin fand sich:
- Um 1:47 Uhr: Node
ip-10-0-1-5wechselte vonNodeReadyzuNodeNotReadyund startete neu. - Redis lief auf dieser Node und wurde neu gestartet.
- Redis benötigt etwa drei bis vier Sekunden, bis es Verbindungen annimmt.
- Die Liveness-Prüfung des Zahlungsdienstes hat jedoch eine Timeout-Einstellung von nur zwei Sekunden.
Das Ergebnis: Der Dienst startete, versuchte, eine Verbindung zu Redis herzustellen, scheiterte aufgrund des Timeouts und wurde vom Kubernetes-System neu gestartet. Dieser Zyklus wiederholte sich endlos.
Die Lösung: Anpassung der Liveness-Prüfung
Die Korrektur bestand aus zwei minimalen Änderungen in der YAML-Konfiguration:
livenessProbe:
httpGet:
path: /healthz
port: 8080
timeoutSeconds: 10 # vorher: 2
initialDelaySeconds: 15 # vorher: 0Mit diesen Einstellungen erhielt Redis ausreichend Zeit, um hochzufahren, bevor die Liveness-Prüfung aktiv wurde. Um 2:36 Uhr war der Dienst wieder stabil.
Warum dauerte die Fehlersuche 47 Minuten?
Die Analyse zeigt, dass die Zeit nicht an technischer Komplexität scheiterte, sondern an der falschen Reihenfolge der Fehlerbehebung:
- Deployment-Untersuchung (12 Minuten): Richtiger Ansatz, aber zu lange festgehalten.
- Log-Analyse (15 Minuten): Symptome wurden untersucht, nicht die Ursache.
- Health-Endpunkt-Test (10 Minuten): Mechanismus bestätigt, ohne die Wurzel zu finden.
- Node-Event-Entdeckung (7 Minuten): Der entscheidende Hinweis kam zuletzt.
- Diagnose und Korrektur (3 Minuten): Einfach, sobald die Ursache bekannt war.
Die wahren Indizien lagen bereits zu Beginn vor:
- Die Liveness-Probe zeigte einen Timeout an.
- Die Node-Events wiesen auf einen Neustart hin.
- Die Redis-Restartzeit korrelierte mit dem Vorfall.
- Die Deployment-Historie entlastete die letzte Änderung frühzeitig.
Hätte das Team diese Signale in der richtigen Reihenfolge analysiert, wäre der Vorfall in fünf Minuten behoben gewesen. Doch unter Druck tendieren Menschen dazu, bei der vertrautesten Methode zu bleiben – in diesem Fall der Log-Analyse – statt breiter zu denken.
Lehren für die Zukunft: Automatisierung als Game-Changer
Dieser Vorfall war der Auslöser für die Entwicklung eines neuen Tools namens Causa. Das System soll solche Analysen künftig automatisieren:
- Empfang des Alarms von PagerDuty.
- Automatische Abfrage der Kubernetes-Events für betroffene Pods.
- Korrelation mit Node-Events.
- Identifikation der Ursache innerhalb weniger Minuten.
Mit solchen Tools lassen sich menschliche Fehler in der Krisenkommunikation minimieren und die Reaktionszeit deutlich verkürzen. Denn am Ende zählt nicht nur die Lösung, sondern auch, wie schnell sie gefunden wird.
KI-Zusammenfassung
Ödeme servisi arızasının 47 dakika içinde çözülmesini sağlayan factors ve soruşturma süreci