Methode: erst einordnen, dann graben
Fehlersuche unter Druck scheitert selten am fehlenden Wissen, sondern an der Reihenfolge. Der Einstieg ist immer derselbe: Status abfragen, Symptom einem Zweig zuordnen, erst dann Logs öffnen.
Einstiegsbefehl je Ressource
| Symptom | Einstieg | Abschnitt |
|---|---|---|
| Anwendung startet nicht | kubectl get pods | 1 · Pods |
| Pods verschwinden oder starten nirgends | kubectl get nodes | 2 · Nodes |
| Service antwortet nicht | kubectl get svc, EndpointSlices | 3 · Services |
| Von außen nicht erreichbar | kubectl get ingress | 4 · Ingress |
| Pod hängt beim Volume | kubectl get pvc | 5 · PVC |
| kubectl selbst zickt | kubectl get --raw '/readyz?verbose' | 6 · Control Plane |
Fünf Grundregeln
- Einfach anfangen
getunddescribezeigen den Zustand oft schon vollständig. - Den Logs folgenLogs und Events erzählen, was passiert ist – meist auch, was zu tun ist.
- Konfiguration vor KomplexitätDie Mehrheit der Störungen sind Fehlkonfigurationen, keine Bugs: YAML, Labels, Selectors, Limits.
- Tiefer graben, wenn nötig
top,port-forward,execunddebugfür Laufzeitverhalten. - VorbeugenRequests/Limits, Probes und Monitoring verhindern die Wiederholung.
Die immer gleiche Kette
- get – welcher Status steht da wirklich
- describe – Events und Conditions am Objekt
- events – Zeitleiste im Namespace
- logs – nur wenn der Container überhaupt gestartet ist
- exec / debug – zuletzt, wenn die Sicht von innen fehlt
kubectl apply bestätigt nur die Annahme des Manifests, Running sagt nichts über die Fachfunktion, und get svc zeigt einen Service auch dann, wenn kein einziger Pod dahintersteht. Jeder der folgenden Abschnitte hat genau so einen Fall.Pods: fünf Zweige nach Status
Der häufigste Einstieg. Die Spalte STATUS entscheidet, welcher Zweig überhaupt sinnvoll ist – und schließt gleichzeitig die anderen vier aus.
Einstieg und Verzweigung
Ohne diesen ersten Blick ist jede weitere Suche Raten. Wichtig sind beide Spalten: STATUS und READY.
kubectl get pods -n <namespace> -o wide
| Status | Bedeutung | Zweig |
|---|---|---|
| Pending | nicht platziert oder Volume fehlt | A |
| ImagePullBackOff / ErrImagePull | Image kommt nicht auf den Node | B |
| CrashLoopBackOff | Container startet und stirbt wiederholt | C |
| Running, aber READY 0/1 | Probe schlägt fehl oder Anwendung antwortet nicht | D |
| Error / Unknown | Node oder kubelet meldet nicht sauber | E |
Zweige auf einen Blick
Links die Ressource, in der Mitte der Zweig nach Status, rechts die Ursachen, die dieser Zweig abdeckt. Ein Klick auf einen Zweig öffnet die Langfassung mit den Befehlen.
Zweig A · Pending: der Pod findet keinen Platz
Pending heißt fast immer: Der scheduler hat keine Entscheidung getroffen. Der Grund steht als Event am Pod, nicht im Log – ein Log gibt es noch gar nicht, weil kein Container existiert.
kubectl describe pod <pod> -n <namespace>
kubectl get events -n <namespace> --field-selector reason=FailedScheduling
Zu wenig CPU oder Memory: Entscheidend ist nicht die Auslastung, sondern die Summe der Requests. Ein Node kann bei 20 % CPU-Last voll belegt sein.
# tatsächliche Last (benötigt metrics-server)
kubectl top nodes
# reservierte Requests pro Node
kubectl describe node <node> | grep -A 8 "Allocated resources"
Lösung: Requests senken oder Kapazität schaffen (Node hinzufügen, andere Workloads verschieben).
nodeSelector oder Affinity passt nicht: Der Pod verlangt Labels, die kein Node trägt.
kubectl get pod <pod> -n <namespace> -o jsonpath='{.spec.nodeSelector}'
kubectl get nodes --show-labels
Lösung: Node-Labels ergänzen oder den Selector korrigieren.
Taints ohne passende Toleration: Der Node wehrt den Pod aktiv ab.
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
Lösung: Toleration am Pod ergänzen oder den Taint entfernen, wenn er nicht gewollt war.
Volume fehlt: Wenn describe auf ein ungebundenes PVC zeigt, liegt die Ursache in Abschnitt 5, nicht beim scheduler.
Zweig B · ImagePullBackOff und ErrImagePull
Die genaue Fehlermeldung der Registry steht in den Events des Pods – sie unterscheidet die drei möglichen Ursachen.
kubectl describe pod <pod> -n <namespace>
„not found": Name oder Tag stimmen nicht. Prüfen und im Deployment korrigieren, nicht am laufenden Pod.
„unauthorized": Pull-Secret fehlt, ist falsch oder liegt im falschen Namespace.
kubectl get secrets -n <namespace>
kubectl get pod <pod> -n <namespace> -o jsonpath='{.spec.imagePullSecrets}'
„connection refused" oder Timeout: Die Registry ist vom Node aus nicht erreichbar – Netz, Proxy, DNS oder Firewall. Der Test gehört auf den Node, nicht auf den Laptop.
# Debug-Pod auf dem Node, Host-Dateisystem liegt unter /host
kubectl debug node/<node> -it --image=busybox
wget -S --spider https://<registry-host>/v2/
# auf OpenShift stattdessen:
oc debug node/<node>
Zweig C · CrashLoopBackOff: der Container stirbt immer wieder
Hier gibt es bereits ein Log – und zwar zwei: das des aktuellen und das des vorherigen Versuchs. Das interessante ist meist das vorherige, weil der aktuelle Container oft noch gar nicht gestartet ist.
kubectl logs <pod> -n <namespace>
kubectl logs <pod> -n <namespace> --previous
Anwendungsfehler: Environment-Variablen, ConfigMaps, Abhängigkeiten prüfen und im Deployment beziehungsweise in ConfigMap/Secret korrigieren.
OOMKilled: Der Container hat sein Memory-Limit gerissen. Der Beweis steht im letzten Terminierungsgrund, nicht in den Logs.
kubectl get pod <pod> -n <namespace> \
-o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'
kubectl describe pod <pod> -n <namespace> | grep -A 5 "Limits:"
Lösung: Memory-Limit anheben oder den Speicherverbrauch der Anwendung begrenzen (Heap-Größe, Worker-Anzahl).
Exit Code 137 oder 143: 137 ist 128+9 (SIGKILL), 143 ist 128+15 (SIGTERM). Das sind Signale, keine Diagnosen – sie sagen, dass jemand beendet hat, nicht wer.
lastState.terminated.reason – OOMKilled oder Error – und in den Events. Wer stattdessen das Memory-Limit erhöht, verschiebt den Neustart nur nach hinten.Zweige D und E · Running ohne Funktion, Error, Unknown
READY 0/1 bei Running: Der Container läuft, die Readiness-Probe ist nicht erfüllt. Pfad, Port oder Startzeit der Probe passen nicht zur Anwendung.
kubectl describe pod <pod> -n <namespace> | grep -A 3 Readiness
READY 1/1, aber fachlich kaputt: Am Service vorbei direkt auf den Pod testen. Antwortet er hier korrekt, liegt der Fehler im Service oder Ingress – nicht in der Anwendung.
kubectl port-forward pod/<pod> 8080:<container-port> -n <namespace>
curl -sv localhost:8080
Error oder Unknown: Meist meldet der Node nicht mehr. Erst die Objektsicht, dann weiter in Abschnitt 2.
kubectl describe pod <pod> -n <namespace>
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod>
Nodes: NotReady oder unauffällig überlastet
Zwei Zweige: Der Node meldet sich gar nicht mehr sauber, oder er meldet Ready und die Pods scheitern trotzdem. Der zweite Fall ist der unangenehmere.
Einstieg
Die Spalte STATUS teilt den Baum, -o wide liefert gleich Kernel und Runtime-Version für den Fall, dass ein einzelner Node aus der Reihe fällt.
kubectl get nodes -o wide
- NotReady – Zweig A: Conditions und kubelet
- Ready, aber Pods scheitern – Zweig B: Ressourcen und Belegung
Zweige auf einen Blick
Links die Ressource, in der Mitte der Zweig nach Status, rechts die Ursachen, die dieser Zweig abdeckt. Ein Klick auf einen Zweig öffnet die Langfassung mit den Befehlen.
Zweig A · Node NotReady
Die Conditions am Node benennen die Ursache direkt – Speicher, Platte, PID-Druck oder fehlende Netzwerkkonfiguration.
kubectl describe node <node> | grep -A 10 Conditions
DiskPressure oder MemoryPressure: Der Blick gehört auf den Node selbst. Ohne SSH führt der Weg über einen Debug-Pod mit Zugriff auf das Host-Dateisystem.
kubectl debug node/<node> -it --image=busybox
# im Debug-Pod liegt das Host-Dateisystem unter /host
chroot /host df -h
chroot /host free -h
Lösung: Platte aufräumen (Images, Logs), Workloads verschieben, im Zweifel kubectl drain und den Node warten.
kubelet läuft nicht: Auf dem Node prüfen und die Logs mitlesen.
chroot /host systemctl status kubelet
chroot /host journalctl -u kubelet -f
Lösung: kubelet neu starten. Kommt es danach erneut hoch und fällt wieder aus, liegt die Ursache eine Ebene tiefer – Zertifikate, Container-Runtime oder Netzwerk-Plugin.
Zweig B · Node Ready, Pods scheitern trotzdem
Ready heißt nur, dass das kubelet meldet. Ob noch Platz für neue Pods ist, steht in der Belegung – und die berechnet sich aus Requests, nicht aus Auslastung.
kubectl describe node <node> | grep -A 8 "Allocated resources"
kubectl top node <node>
Auch das Pod-Limit pro Node ist eine harte Grenze, die nichts mit CPU oder Memory zu tun hat.
kubectl get node <node> -o jsonpath='{.status.allocatable.pods}'
Lösung: Pods abziehen, Requests korrigieren oder Kapazität ergänzen.
kubectl top 30 % Auslastung zeigt, die Summe der Requests aber bei 100 % liegt. Die beiden Zahlen messen Verschiedenes: Auslastung ist Ist, Allocated ist reserviert. Wer nur auf top schaut, sucht die Ursache im Cluster-Netz statt im Sizing.Services: Selector, Endpunkte, Erreichbarkeit
Ein Service ist nur eine Regel mit einem Label-Selector. Ob dahinter etwas steht, sagt nicht das Service-Objekt, sondern die Liste seiner Endpunkte.
Einstieg
EndpointSlices sind der aktuelle Weg, die Zuordnung von Pods zum Service zu prüfen. Die alte Endpoints-API gilt als veraltet und schneidet bei sehr vielen Pods still ab.
kubectl get svc <service> -n <namespace>
kubectl get endpointslices -n <namespace> \
-l kubernetes.io/service-name=<service> -o wide
- Keine Endpunkte – Zweig A: Selector oder Pod-Zustand
- Endpunkte vorhanden, trotzdem keine Antwort – Zweig B: Netz, Ports, DNS
Zweige auf einen Blick
Links die Ressource, in der Mitte der Zweig nach Status, rechts die Ursachen, die dieser Zweig abdeckt. Ein Klick auf einen Zweig öffnet die Langfassung mit den Befehlen.
Zweig A · Keine Endpunkte
Entweder passt der Selector nicht zu den Pod-Labels, oder die Pods sind nicht Ready – nur Ready-Pods landen in der Endpunktliste.
kubectl describe svc <service> -n <namespace>
kubectl get pods -n <namespace> --show-labels
kubectl get pods -n <namespace> -l <key>=<value>
Liefert der letzte Befehl Pods, aber die EndpointSlice bleibt leer, ist der Selector richtig und die Readiness das Problem – zurück zu Abschnitt 1, Zweig D.
Lösung: Labels im Deployment oder den Selector im Service korrigieren. Der Selector eines bestehenden Service lässt sich ändern, der Selector eines Deployments nicht – dort ist ein Neuanlegen nötig.
Zweig B · Endpunkte da, Service antwortet nicht
Der Test gehört in den Cluster hinein, weil DNS und ClusterIP von außen gar nicht existieren. Ein kurzlebiger Debug-Pod reicht.
# Image mit Netzwerkwerkzeugen; in getrennten Umgebungen gespiegelt vorhalten
kubectl run debug --rm -it --restart=Never -n <namespace> \
--image=nicolaka/netshoot -- sh
In der Shell zuerst die Namensauflösung, dann die Verbindung prüfen. telnet fehlt in den meisten Images – nc oder wget tun dasselbe.
nslookup <service>.<namespace>.svc.cluster.local
nc -zv <service>.<namespace> <port>
wget -qO- http://<service>.<namespace>:<port>/
Schlägt schon die Auflösung fehl, liegt es an CoreDNS – siehe Abschnitt 6. Antwortet DNS, aber die Verbindung nicht, sind NetworkPolicies oder ein falscher targetPort die üblichen Ursachen.
kubectl get networkpolicies -n <namespace>
kubectl get svc <service> -n <namespace> \
-o jsonpath='{.spec.ports[*].targetPort}'
Von außen: Ein Service vom Typ ClusterIP ist von außen grundsätzlich nicht erreichbar – das ist kein Fehler, sondern die Definition.
kubectl get svc <service> -n <namespace> -o jsonpath='{.spec.type}'
port und targetPort werden gern verwechselt. Stimmt der targetPort nicht mit dem Container-Port überein, ist der Service trotzdem grün, die EndpointSlice gefüllt und die Pods Ready – die Verbindung läuft nur in einen Timeout. Kein einziger Statuswert zeigt diesen Fehler an; er fällt erst beim Verbindungstest auf.Ingress: 404, Zertifikat oder Name
Ein Ingress ist nur eine Regel für einen Controller. Ohne passenden Controller passiert schlicht nichts – und zwar geräuschlos.
Einstieg
Zuerst prüfen, ob das Objekt überhaupt bedient wird: Eine leere Spalte ADDRESS heißt, dass kein Controller die Regel übernommen hat.
kubectl get ingress -n <namespace>
kubectl describe ingress <ingress> -n <namespace>
| Symptom | Wahrscheinliche Ursache | Zweig |
|---|---|---|
| 404 Not Found | Pfad, Servicename oder Port stimmen nicht | A |
| Zertifikatswarnung, TLS-Fehler | Secret fehlt, falscher Typ oder falscher Host | B |
| Host löst nicht auf | DNS zeigt nicht auf den Ingress | C |
Zweige auf einen Blick
Links die Ressource, in der Mitte der Zweig nach Status, rechts die Ursachen, die dieser Zweig abdeckt. Ein Klick auf einen Zweig öffnet die Langfassung mit den Befehlen.
Zweig A · 404 Not Found
Der Ingress verweist auf Service und Port. Beides muss existieren und muss Endpunkte haben – sonst antwortet der Controller mit dem Default-Backend.
kubectl get svc <service> -n <namespace>
kubectl get endpointslices -n <namespace> -l kubernetes.io/service-name=<service>
Häufigster Konfigurationsfehler ist der pathType: Exact trifft nur exakt, Prefix trifft Unterpfade. Wer /api als Exact anlegt, bekommt bei /api/v1 ein 404.
kubectl get ingress <ingress> -n <namespace> -o yaml
Lösung: Pfad, pathType, Servicename und Port angleichen.
Zweig B · TLS-Zertifikat
Das im Ingress referenzierte Secret muss im selben Namespace liegen, vom Typ kubernetes.io/tls sein und den angefragten Hostnamen abdecken.
kubectl get secret <tls-secret> -n <namespace> \
-o jsonpath='{.type}'
Ob das Zertifikat wirklich passt, zeigt nur der Blick auf den Inhalt – Gültigkeit und die Namen im SAN-Feld.
kubectl get secret <tls-secret> -n <namespace> \
-o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates -text | grep -A 1 "Subject Alternative Name"
Lösung: Secret neu erzeugen oder erneuern. Bei cert-manager stattdessen Certificate und Order prüfen, statt das Secret von Hand zu überschreiben – es wird sonst wieder ersetzt.
Zweig C · Host oder DNS löst nicht auf
Getestet wird von dort, wo der Nutzer steht – nicht aus dem Cluster. Die Antwort muss auf die Adresse des Ingress-Controllers zeigen.
nslookup <hostname>
dig +short <hostname>
Zum Abgleich die Adresse, die der Controller veröffentlicht:
kubectl get ingress <ingress> -n <namespace> \
-o jsonpath='{.status.loadBalancer.ingress[*]}'
Lösung: DNS-Eintrag korrigieren. Bei Wildcard-Domains prüfen, ob der Eintrag den konkreten Host wirklich abdeckt.
ingressClassName und ist keine Default-IngressClass gesetzt, fühlt sich kein Controller zuständig. Das Objekt existiert, kubectl get ingress zeigt es an, es gibt keine Fehlermeldung und keine Events – nur die Spalte ADDRESS bleibt leer und alle Anfragen laufen ins Nichts. Der erste Blick bei jedem Ingress-Problem gehört deshalb dieser Spalte.PVC und Storage: Pending, Mount, Terminating
Ein PVC ist ein Anspruch, kein Speicher. Zwischen Anspruch und tatsächlichem Volume liegen StorageClass, Provisioner und – im Fehlerfall – ein Finalizer.
Einstieg
kubectl get pvc -n <namespace>
- Pending – Zweig A: kein passendes Volume
- Bound, aber Pod mountet nicht – Zweig B: Attach oder Rechte
- Terminating – Zweig C: Finalizer
Zweige auf einen Blick
Links die Ressource, in der Mitte der Zweig nach Status, rechts die Ursachen, die dieser Zweig abdeckt. Ein Klick auf einen Zweig öffnet die Langfassung mit den Befehlen.
Zweig A · PVC bleibt Pending
Der Grund steht als Event am PVC. Typisch sind: keine StorageClass angegeben und keine Default-Klasse gesetzt, ein Provisioner, der nicht läuft, oder eine angeforderte Größe, für die kein PV existiert.
kubectl describe pvc <pvc> -n <namespace>
kubectl get storageclass
Lösung: passende StorageClass setzen, Default-Klasse definieren oder bei statischem Provisioning ein PV anlegen, das Größe und Access-Mode erfüllt.
volumeBindingMode: WaitForFirstConsumer ist Pending der vorgesehene Zustand, bis ein Pod das PVC anfordert – so wird das Volume in der richtigen Zone erzeugt. Wer hier „repariert" und auf Immediate umstellt, produziert Volumes in Zonen, in denen später kein Pod mehr laufen kann.Zweig B · Bound, aber der Pod mountet nicht
Der Fehler steht nicht am PVC, sondern in den Events des Pods – dort meldet das kubelet, woran das Mounten scheitert.
kubectl describe pod <pod> -n <namespace>
Häufig: Der Access-Mode passt nicht zur Situation. Ein Volume mit ReadWriteOnce kann nur von Pods auf einem einzigen Node genutzt werden – bei einem Rolling Update wartet der neue Pod, bis der alte das Volume freigibt.
kubectl get pvc <pvc> -n <namespace> -o jsonpath='{.spec.accessModes}'
kubectl get volumeattachments | grep <pv-name>
Lösung: Volume-Attachment lösen lassen (alten Pod beenden), Rechte am Dateisystem über fsGroup setzen oder für parallelen Zugriff auf ReadWriteMany mit passendem Backend wechseln.
Zweig C · PVC hängt in Terminating
Ein Finalizer verhindert das Löschen, solange das Volume noch benutzt wird. Das ist eine Schutzfunktion, kein Defekt.
kubectl get pvc <pvc> -n <namespace> -o jsonpath='{.metadata.finalizers}'
kubectl get pods -n <namespace> \
-o custom-columns=POD:.metadata.name,PVC:.spec.volumes[*].persistentVolumeClaim.claimName
Lösung: die nutzenden Pods beenden. Der Finalizer löst sich dann von selbst.
Control Plane: apiserver, etcd, CoreDNS
Wenn kubectl selbst hängt, langsam wird oder widersprüchliche Antworten liefert, liegt der Fehler nicht mehr im Workload.
Einstieg
Die Health-Endpunkte des apiservers listen jede Teilprüfung einzeln auf, etcd inbegriffen. Sie sind der Ersatz für den früher üblichen Blick auf die Componentstatuses.
kubectl get --raw '/livez?verbose'
kubectl get --raw '/readyz?verbose'
kubectl get pods -n kube-system
Auf OpenShift kommt die Sicht über die Cluster-Operatoren dazu – sie sagt in einer Zeile, welcher Teil der Plattform degradiert ist.
oc get clusteroperators
kubectl get componentstatuses stammt aus der Vorlage, ist seit Kubernetes 1.19 als veraltet markiert und liefert auf verwalteten Clustern häufig gar nichts oder irreführende Werte. Die livez- und readyz-Endpunkte ersetzen es vollständig.Zweige auf einen Blick
Links die Ressource, in der Mitte der Zweig nach Status, rechts die Ursachen, die dieser Zweig abdeckt. Ein Klick auf einen Zweig öffnet die Langfassung mit den Befehlen.
Zweig A · apiserver
Erreichbarkeit und Antwortzeit prüfen, bevor Komponenten neu gestartet werden. Ein langsamer apiserver sieht aus wie ein defektes Netzwerk.
kubectl cluster-info
time kubectl get --raw '/healthz'
Bei selbstverwalteten Clustern läuft der apiserver als statischer Pod; sein Log liegt beim kubelet des Control-Plane-Nodes.
kubectl logs -n kube-system -l component=kube-apiserver --tail=200
Lösung: Ursache im Log suchen – Zertifikatsablauf, Speicherdruck und volle Audit-Log-Partitionen sind die üblichen Kandidaten. Ein Neustart hilft nur, wenn die Ursache behoben ist.
Zweig B · etcd
Erst der Zustand der Pods, dann die Sicht aus etcd selbst. Die Gesundheit aller Mitglieder ist wichtiger als die eines einzelnen.
kubectl get pods -n kube-system -l component=etcd -o wide
kubectl exec -n kube-system <etcd-pod> -- \
etcdctl endpoint health --cluster --write-out=table
Auf OpenShift liegen die etcd-Pods in einem eigenen Namespace und der Zugriff läuft über den Debug-Wrapper.
oc get pods -n openshift-etcd -l app=etcd
oc rsh -n openshift-etcd <etcd-pod> etcdctl endpoint status --write-out=table
Lösung: Bei einem einzelnen ausgefallenen Mitglied das Mitglied ersetzen, nicht den ganzen Cluster neu starten. Bei Latenzproblemen sind die Datenträger die Ursache, nicht etcd selbst.
Zweig C · CoreDNS
DNS-Probleme äußern sich als Service-Probleme. Erst die Pods, dann eine Auflösung aus dem Cluster heraus testen.
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100
Die eigentliche Prüfung gehört in einen Pod – der Weg dorthin steht in Abschnitt 3, Zweig B.
Running melden und keine Komponente rot wird. Wer nur auf Ready-Status schaut, findet die Ursache nie.Der Ablauf einer Störung, chronologisch
Dieselben sechs Abschnitte, diesmal als Reihenfolge: von der Meldung bis zur Vorbeugung. Die Phasen verhindern, dass man mitten im Baum die Ebene wechselt.
Die vier Phasen auf einen Blick
- Phase A · EinordnenSchritte 1–2: Welche Ebene ist betroffen, welcher Status steht da.
- Phase B · EingrenzenSchritte 3–4: Zweig wählen, Diagnosebefehl des Zweigs ausführen.
- Phase C · BehebenSchritt 5: an der Quelle korrigieren, nicht am laufenden Objekt.
- Phase D · Verifizieren und vorbeugenSchritte 6–7: Wirkung nachweisen, Wiederholung verhindern.
Phase A · Einordnen
Schritt 1 · Ebene bestimmen
Die Frage lautet nicht „was ist kaputt", sondern „auf welcher Ebene fängt es an". Ein nicht erreichbarer Service kann an Pods, Service, Ingress oder DNS liegen – der Test von innen nach außen entscheidet, welcher Abschnitt zuständig ist.
# von innen nach außen: Pod, dann Service, dann Ingress
kubectl get pods,svc,ingress -n <namespace>
Schritt 2 · Status wörtlich nehmen
Pending, ImagePullBackOff, CrashLoopBackOff, NotReady, Terminating: Diese Wörter sind keine Beschreibung des Unwohlseins, sondern Zweigadressen. Wer den Status überliest und direkt zu den Logs springt, öffnet in der Hälfte der Fälle ein Log, das es noch gar nicht gibt.
Phase B · Eingrenzen
Schritt 3 · Events vor Logs
Events zeigen, was Kubernetes selbst getan oder verweigert hat – Scheduling, Pull, Mount, Probe. Logs zeigen, was die Anwendung getan hat. Vor dem ersten Containerstart existieren nur Events.
kubectl describe <ressource> <name> -n <namespace>
kubectl get events -n <namespace> --sort-by=.metadata.creationTimestamp
Schritt 4 · Den Zweig zu Ende gehen
Jeder Zweig endet mit einer konkreten Ursache: falsches Label, fehlendes Secret, zu kleines Limit, falscher Port. Solange nur ein Verdacht besteht, ist der Zweig nicht zu Ende – ein Fix auf Verdacht erzeugt zwei Fehler statt einem.
Phase C · Beheben
Schritt 5 · An der Quelle korrigieren
Änderungen gehören in das Manifest, aus dem das Objekt entsteht – Deployment, ConfigMap, Helm-Werte, Git-Repository. Ein kubectl edit am Pod überlebt den nächsten Neustart nicht, in einer GitOps-Umgebung nicht einmal die nächste Synchronisation.
# nach dem Rollout: läuft die neue Generation wirklich?
kubectl rollout status deployment/<name> -n <namespace>
Phase D · Verifizieren und vorbeugen
Schritt 6 · Wirkung nachweisen
Nachweis heißt: derselbe Test, der den Fehler gezeigt hat, ist jetzt grün – nicht „der Pod ist Running". Bei Netzwerkfehlern also erneut der Verbindungstest, bei OOM ein Blick auf die Neustartzähler über eine gewisse Zeit.
kubectl get pod <pod> -n <namespace> \
-o jsonpath='{.status.containerStatuses[*].restartCount}'
Schritt 7 · Wiederholung verhindern
Die meisten Vorfälle in diesem Baum haben eine vorbeugende Maßnahme: Requests und Limits gegen Verdrängung und OOM, Readiness- und Liveness-Probes gegen stillen Ausfall, Monitoring auf Neustartzähler und Pending-Pods gegen späte Entdeckung. Ohne diesen Schritt wird derselbe Zweig in zwei Wochen erneut durchlaufen.