- Authors

- Name
- Phillip Pham
- @ddppham
Exit Code 137: OOMKilled von einem Probe-Kill unterscheiden
TL;DR
137 ist 128 plus 9. Der Prozess hat SIGKILL bekommen und konnte sich nicht mehr sauber beenden. Auf Kubernetes gibt es dafür zwei Alltagsursachen. OOMKilled steht als Reason, wenn der cgroup das Memory-Limit überschritten hat. Error bei demselben Code ist meist die Liveness-Probe. Beides sehen Sie nur im letzten Zustand:
kubectl describe pod POD
Suche nach Last State → Reason und Exit Code. Liegt der Pod schon wieder im CrashLoopBackOff, ist 137 der Grund für den Loop, nicht ein zweites Problem.
Reason entscheidet, nicht die Zahl allein
| Last State | Bedeutung | Fix |
|---|---|---|
| Reason OOMKilled, Exit 137 | Speicherlimit gerissen | Limit anheben oder Verbrauch senken |
| Reason Error, Exit 137 | SIGKILL ohne OOM, oft Liveness | Probe entschärfen, startupProbe setzen |
| Reason Error, Exit 143 | SIGTERM, Grace-Period lief ab | Prozess fängt SIGTERM, Grace erhöhen |
Ein manuelles kubectl delete schickt zuerst SIGTERM. Antwortet der Prozess nicht innerhalb terminationGracePeriodSeconds (Default 30s), folgt SIGKILL und der Code kann 137 sein. Das ist ein Stopp, kein OOM. OOM erkennen Sie am Wort OOMKilled, nicht an der 137.
Limit, Request und das, was die Anwendung glaubt
Das Limit ist eine harte cgroup-Grenze. Der Kernel killt den Prozess, sobald der Verbrauch darüber liegt, inklusive Page Cache je nach Zählung. Der Request ist nur die Scheduler-Reservierung. Ein Request von 512Mi bei einem Limit von 256Mi ist ungültig. Ein Request von 128Mi bei einem Limit von 256Mi schedult leicht und killt, sobald die Anwendung über 256Mi geht.
Java, Node und .NET kennen diese Grenze nicht, solange Sie sie nicht durchreichen. Eine JVM mit -Xmx2g in einem Limit von 1Gi wird zuverlässig OOMKilled. Das Heap-Limit muss unter dem Container-Limit liegen, mit Luft für Metaspace, Threads und Direct Buffers. Faustregel: Heap bei etwa 75 Prozent des Memory-Limits, dann messen.
resources:
requests:
memory: 512Mi
limits:
memory: 1Gi
Ohne Limit killt der Node irgendwann den größten Verbraucher über den System-OOM, nicht über OOMKilled am Pod. In describe fehlt dann der Reason, auf dem Node steht ein Kernel-OOM. Für Produktion ein Limit setzen. Sonst debuggen Sie den falschen Prozess.
Messen, bevor das Limit verdoppelt wird
kubectl top pod POD --containers
kubectl top zeigt den aktuellen Verbrauch, nicht den Peak, der den Kill ausgelöst hat. Liegt der Wert schon knapp unter dem Limit, ist die Richtung klar. Liegt er weit darunter, war es eine Spitze: Startup, ein Report, ein Cache. Dann hilft ein höheres Limit oder weniger Gleichzeitigkeit, nicht ein dauerhaft dreifacher Request. Der Request entscheidet, ob der Pod noch einen Node findet. Wer nur das Limit anhebt und den Request mitzieht, landet als Nächstes bei Pod Pending, weil kein Node die Reservierung frei hat.
Liveness, die wie OOM aussieht
Die Probe schlägt fehl, das Kubelet wartet failureThreshold ab und killt den Container. Exit 137, Reason Error, kein OOMKilled. Typisch, wenn der Check schneller ist als der Start oder auf einen Pfad geht, der unter Last blockiert. startupProbe für den Start, Liveness nur für „der Prozess ist tot“. Ein HTTP-Check auf einen Endpoint, der die Datenbank anfasst, killt den Pod genau dann, wenn die Datenbank langsam ist.
Quellen
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
Pod Pending: keine Nodes, Quota oder Taint
Pod Pending beheben: FailedScheduling lesen. Zu wenig CPU oder RAM, Taints, Node-Selector, ResourceQuota und ein PVC, der nicht auf den Status Bound geht.
kubectl connection refused 6443 beheben
kubectl connection refused auf Port 6443: der API-Server antwortet nicht. kubeconfig, Kontext, Firewall, API-Prozess. Der Pod-Status kommt erst danach.
Error from server Forbidden: RBAC-Recht
kubectl Forbidden: Error from server (Forbidden) nennt User, Verb und Ressource. kubectl auth can-i zeigt das Recht, bevor jemand cluster-admin vergibt.
ContainerCreating hängt: Volume, Sandbox, CNI
ContainerCreating hängt: der Pod hat einen Node, der Container startet nicht. FailedMount, Pod-Sandbox und CNI stehen in den Events, nicht in den Logs.
CreateContainerConfigError: Secret und ConfigMap
CreateContainerConfigError: fehlendes Secret, falscher Key oder Namespace. Der Container startet nicht, logs --previous bleibt leer. Das Event nennt das Objekt.
Kubernetes DNS fehlgeschlagen: CoreDNS, ndots
Kubernetes DNS fehlgeschlagen: CoreDNS-Pods, das Service-ClusterIP und ndots:5. Wann ein Name im Cluster auflöst und wann die NetworkPolicy UDP 53 schluckt.
Exit Code 1: die erste Zeile im Previous-Log
Exit Code 1 auf Kubernetes: die Anwendung beendet sich selbst. kubectl logs --previous zeigt die Zeile. Speicher, Image-Pull und SIGKILL sind andere Codes.
Exit Code 143: SIGTERM und Graceful Shutdown
Exit Code 143 ist SIGTERM: 128 plus 15. Beim Rollout ist das normal. Im Loop fehlt ein Handler oder die Grace-Period ist kürzer als der Stopp der Anwendung.