Das Symptom
kubectl get pods zeigt in der Spalte STATUS seit Minuten oder Stunden dasselbe
Wort: Pending. Die Spalte mit den Neustarts steht auf null, denn es gab nichts
neu zu starten. Logs gibt es keine, weil kein Container existiert, der etwas
hätte sagen können. Der Pod ist nicht abgestürzt, nicht abgelehnt, nicht kaputt —
er steht einfach da. Besonders gern passiert das direkt nach dem Ausrollen einer
neuen Anwendung, nach dem Hochsetzen der Ressourcenanforderungen oder nachdem
jemand einen Knoten in Wartung genommen hat.
Die „Layer 8“-Wahrheit
Pending ist keine Fehlermeldung, sondern eine Wartemarke. Die Dokumentation
definiert die Phase ausdrücklich als „vom Cluster angenommen, aber noch nicht
eingerichtet und lauffähig gemacht“ und rechnet dabei zwei Dinge zusammen: die
Zeit, die der Pod auf seine Zuteilung wartet, und die Zeit, in der Images
heruntergeladen werden. Der zweite Fall löst sich von selbst. Der erste nicht.
Denn der Zuteiler arbeitet in zwei Schritten — erst filtert er alle Knoten heraus,
auf denen der Pod überhaupt laufen könnte, dann bewertet er die Übriggebliebenen.
Bleibt nach dem Filtern nichts übrig, ist der Pod schlicht nicht zuteilbar, und
die Dokumentation sagt klar, was dann geschieht: Er bleibt unzugeteilt, bis der
Zuteiler ihn unterbringen kann. Es läuft keine Uhr. Es kommt kein Abbruch. Es
passiert nichts.
Der eigentliche Fehler auf Schicht 8 ist deshalb nicht die Konfiguration, sondern
das Raten. Fast jeder öffnet an dieser Stelle das Manifest und dreht wahllos an
Zahlen, obwohl die Antwort bereits vorliegt: Der Zuteiler schreibt seine
Begründung in die Ereignisse des Pods, meistens als FailedScheduling, und dort
steht dann nüchtern, wie viele Knoten aus welchem Grund ausgeschieden sind — zu
wenig Prozessor, zu wenig Speicher, eine Markierung ohne passende Duldung, ein
Volume, das sich nicht binden lässt. Ein einziger kubectl describe pod ersetzt
eine halbe Stunde Vermutungen.
Und dann die drei Klassiker, die nichts mit Platzmangel zu tun haben und trotzdem
zu Pending führen. Erstens Anforderungen, die nie zu einem Knoten passen können:
Zugeteilt wird nach requests, nicht nach dem, was ein Programm tatsächlich
verbraucht — wer vier Kerne anfordert, findet auf einem Zwei-Kern-Knoten niemals
Platz, egal wie leer der ist. Zweitens Markierungen, die einen Knoten Pods
abstoßen lassen, solange der Pod keine passende Duldung mitbringt — der Knoten
ist dann sichtbar frei und trotzdem nicht zu haben. Drittens ein Volume, das
keinen Partner findet — Ansprüche
bleiben laut Dokumentation unbegrenzt ungebunden, wenn kein passendes Volume
existiert, und mit der Bindungsart WaitForFirstConsumer sieht das dann aus, als
warteten Pod und Volume höflich aufeinander. Kubernetes hat in keinem dieser Fälle
einen Fehler gemacht. Es hat nur getan, worum es gebeten wurde, und wartet
geduldig darauf, dass jemand die Bitte für erfüllbar erklärt.
Die echte, verifizierte Lösung
1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:
1.Den Zustand und die Ereignisse des Pods ansehen
Der Einstieg in jede Pod-Fehlersuche, und die Dokumentation nennt ihn genau so: Der erste Schritt beim Untersuchen eines Pods ist, ihn sich anzusehen. Die Übersicht zeigt die Phase, der zweite Befehl den aktuellen Zustand und die letzten Ereignisse — dort steht die Begründung des Zuteilers im Klartext, meist als Ereignis mit dem Grund FailedScheduling.
kubectl get pods
kubectl describe pod POD-NAME
2.Die Meldungen des Zuteilers gezielt herausfiltern
Ereignisse sind flüchtig und gehen in langen describe-Ausgaben unter. Der erste Befehl zeigt die Ereignisse zu genau diesem Pod, der zweite alle Zuteilungsfehlschläge im Namensraum. In der Meldung steht regelmäßig, wie viele Knoten aus welchem Grund ausgeschieden sind — das ist die Antwort, die man sonst zu erraten versucht.
kubectl events --for pod/POD-NAME
kubectl get events --field-selector reason=FailedScheduling
3.Die angeforderten Ressourcen gegen den freien Platz halten
Anforderungen sind der Maßstab der Zuteilung, nicht der tatsächliche Verbrauch. Die Dokumentation sagt es unmissverständlich: Wenn du die Ressourcenanforderung für Container in einem Pod angibst, benutzt der kube-scheduler diese Angabe, um zu entscheiden, auf welchem Knoten der Pod platziert wird. Der erste Befehl zeigt, was der Pod verlangt, der zweite unter Allocated resources, was auf dem Knoten schon vergeben ist. Zur Einordnung der Einheiten: Laut Dokumentation entspricht 1 CPU-Einheit einem physischen oder virtuellen Prozessorkern, Speicher wird in Bytes gemessen und darf Suffixe wie Mi oder Gi tragen.
kubectl get pod POD-NAME -o yaml
kubectl describe node NODE-NAME
4.Markierungen und Duldungen nebeneinanderlegen
Der erste Befehl listet alle Knoten mit ihren Markierungen, der zweite zeigt die Duldungen des Pods. Die Dokumentation beschreibt das Zusammenspiel so: Eine oder mehrere Markierungen werden auf einen Knoten angewandt; das kennzeichnet, dass der Knoten keine Pods annehmen soll, die die Markierungen nicht dulden. Und weiter: Duldungen erlauben die Zuteilung, garantieren sie aber nicht — der Zuteiler bewertet im Rahmen seiner Aufgabe auch andere Größen.
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
kubectl get pod POD-NAME -o yaml
5.Das Volume prüfen, sofern der Pod eines verlangt
Steht der Anspruch nicht auf Bound, startet der Pod nicht. Die Dokumentation beschreibt den Vorgang als Regelkreis in der Steuerungsebene, der neue Ansprüche beobachtet, ein passendes Volume sucht — sofern möglich — und beide aneinander bindet. Bleibt STATUS auf Pending, fehlt schlicht ein passendes Volume. Der dritte Befehl zeigt die Bindungsart der Speicherklasse: Bei WaitForFirstConsumer ist ein wartender Anspruch normal, bei Immediate ist er ein Befund.
kubectl get pvc
kubectl describe pvc PVC-NAME
kubectl get storageclass
6.Die Ursache beheben, statt den Pod neu zu erzeugen
Je nach Befund: Anforderungen auf ein realistisches Maß senken, überflüssige Pods entfernen oder einen Knoten hinzufügen — die Dokumentation nennt genau diese drei Wege, wenn Prozessor- oder Speichervorrat im Cluster erschöpft sind. Bei Markierungen entweder eine passende Duldung in den Pod schreiben oder die Markierung entfernen. Bei hostPort empfiehlt die Dokumentation, stattdessen ein Service-Objekt zu verwenden, weil hostPort in den meisten Fällen unnötig ist. Löschen und neu anlegen hilft nicht: Der neue Pod trifft auf denselben Cluster.
kubectl get pods --watch