- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
- OOMKilled (Exit Code 137) bedeutet, dass der Linux Kernel einen Container wegen Speicherüberschreitung beendet hat
- Die 5 häufigsten Ursachen: zu niedriges Memory Limit, Memory Leaks, JVM/Container Mismatch, Sidecar-Verbrauch und Burst-Traffic
- Faustregel: Memory Limit auf 1,5x bis 2x des normalen Verbrauchs setzen; bei Java: Container Limit = Xmx + ca. 150 MB
- Prometheus-Query
container_memory_usage_bytes / container_spec_memory_limit_byteszeigt die Memory-Auslastung relativ zum Limit - Immer mindestens Burstable QoS verwenden; für kritische Workloads Guaranteed (Request = Limit)
Kubernetes OOMKilled - Kompletter Troubleshooting Guide
OOMKilled (Out of Memory Killed) bedeutet, dass Ihr Container mehr Speicher verbraucht hat als erlaubt und vom Linux Kernel beendet wurde. Dieser Guide zeigt die Ursachen, Diagnose und Lösungen.
Was bedeutet OOMKilled?
kubectl describe pod my-pod
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
- Exit Code 137 = Container wurde mit SIGKILL (9) beendet
- Formel: 128 + 9 = 137
- Linux Kernel beendet Prozess, der Memory Limit überschreitet
Schnelle Diagnose
# 1. OOMKilled bestätigen
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
# 2. Memory Limit prüfen
kubectl get pod <pod-name> -o jsonpath='{.spec.containers[0].resources.limits.memory}'
# 3. Aktueller Memory-Verbrauch (wenn Pod läuft)
kubectl top pod <pod-name>
# 4. Node Memory-Status
kubectl top nodes
Die 5 häufigsten Ursachen
1. Memory Limit zu niedrig
Problem: Anwendung braucht mehr Memory als konfiguriert.
Diagnose:
# Aktuelles Limit
kubectl get pod <pod> -o yaml | grep -A 3 limits
# Historischer Verbrauch (wenn Prometheus vorhanden)
# container_memory_usage_bytes{pod="<pod>"}
Lösung:
spec:
containers:
- name: app
resources:
requests:
memory: "256Mi" # Garantierter Speicher
limits:
memory: "512Mi" # Maximum (erhöhen!)
Empfehlung: Limit = 1.5x bis 2x des normalen Verbrauchs.
2. Memory Leak in der Anwendung
Problem: Anwendung gibt Speicher nicht frei.
Symptome:
- Memory steigt kontinuierlich
- OOMKilled nach Stunden/Tagen
- Neustart "behebt" Problem temporär
Diagnose:
# Memory-Verlauf über Zeit beobachten
kubectl top pod <pod> --containers
# Alle 30 Sekunden
watch -n 30 "kubectl top pod <pod>"
Java-Anwendungen:
spec:
containers:
- name: java-app
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx384m -XX:+HeapDumpOnOutOfMemoryError"
resources:
limits:
memory: "512Mi" # Muss größer als Xmx sein!
Formel für Java: Container Limit = Xmx + ~150MB (für Non-Heap)
Node.js-Anwendungen:
env:
- name: NODE_OPTIONS
value: "--max-old-space-size=384"
3. Container Memory vs. Limit Mismatch
Problem: Anwendung kennt ihr Limit nicht und allokiert zu viel.
Java ohne Limits:
# JVM sieht Node-Memory, nicht Container-Limit!
java -jar app.jar # Allokiert 25% des Node-RAM
Lösung - Container-aware JVM (Java 11+):
env:
- name: JAVA_OPTS
value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
Oder explizite Heap-Größe:
env:
- name: JAVA_OPTS
value: "-Xms256m -Xmx384m"
4. Sidecar/Init-Container verbrauchen Memory
Problem: Gesamtes Pod-Memory wird von allen Containern geteilt.
Diagnose:
# Memory aller Container im Pod
kubectl top pod <pod> --containers
POD NAME CPU MEMORY
my-pod app 10m 256Mi
my-pod sidecar 5m 128Mi # <-- Zusätzlicher Verbrauch!
Lösung: Limits für jeden Container setzen:
spec:
containers:
- name: app
resources:
limits:
memory: "256Mi"
- name: sidecar
resources:
limits:
memory: "64Mi"
5. Burst-Traffic verursacht Spike
Problem: Kurzzeitig hoher Traffic führt zu Memory-Spike.
Diagnose:
# Events während OOMKill-Zeit prüfen
kubectl get events --field-selector reason=OOMKilling
Lösungen:
- Limit erhöhen für Peak-Zeiten
- HPA einrichten für Auto-Scaling:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
Memory Requests vs. Limits
┌─────────────────────────────────────────────────┐
│ NODE MEMORY │
├───────────────────────┬─────────────────────────┤
│ REQUESTS │ LIMITS │
│ (Guaranteed) │ (Maximum) │
├───────────────────────┴─────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Pod A │ │ Pod A │ │
│ │ 256Mi │ ───────▶│ kann bis 512Mi │ │
│ │ Request │ │ nutzen (Limit) │ │
│ └──────────┘ └──────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Pod B │ │ Pod B │ │
│ │ 128Mi │ ───────▶│ kann bis 256Mi │ │
│ │ Request │ │ nutzen (Limit) │ │
│ └──────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────┘
Best Practices:
| Szenario | Request | Limit |
|---|---|---|
| Stabile Workloads | Gleich wie Limit | = Request |
| Variable Workloads | 50% des Limits | 2x Request |
| Batch Jobs | Niedrig | Hoch |
Memory richtig dimensionieren
Schritt 1: Baseline ermitteln
# 1. Pod ohne Limits deployen (nur in Dev!)
# 2. Last simulieren
# 3. Verbrauch messen
kubectl top pod <pod> --containers
# Über Zeit messen
for i in {1..60}; do
kubectl top pod <pod> >> memory-log.txt
sleep 60
done
Schritt 2: Limits berechnen
Request = P95 Memory (95. Perzentil des normalen Verbrauchs)
Limit = Peak Memory + 20% Buffer
Schritt 3: Monitoring einrichten
# Prometheus Alert für Memory > 80%
- alert: PodMemoryHigh
expr: |
container_memory_usage_bytes / container_spec_memory_limit_bytes > 0.8
for: 5m
labels:
severity: warning
QoS-Klassen verstehen
Kubernetes hat 3 Quality of Service Klassen:
| QoS Klasse | Bedingung | Eviction-Priorität |
|---|---|---|
| Guaranteed | Request = Limit (für alle Container) | Niedrig (zuletzt) |
| Burstable | Request < Limit | Mittel |
| BestEffort | Keine Requests/Limits | Hoch (zuerst) |
Empfehlung für Production: Immer mindestens Burstable, besser Guaranteed.
# Guaranteed QoS
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "256Mi" # Gleich wie Request!
cpu: "100m" # Gleich wie Request!
Debugging mit Tools
1. kubectl-top
kubectl top pod <pod> --containers
2. Prometheus + Grafana
# Memory-Verbrauch pro Pod
container_memory_usage_bytes{namespace="production"}
# Memory vs. Limit
container_memory_usage_bytes / container_spec_memory_limit_bytes
# OOMKill Events
kube_pod_container_status_last_terminated_reason{reason="OOMKilled"}
3. Java JVM Analyse
# Heap Dump erstellen
kubectl exec <pod> -- jcmd 1 GC.heap_dump /tmp/heap.hprof
# Kopieren
kubectl cp <pod>:/tmp/heap.hprof ./heap.hprof
# Analysieren mit Eclipse MAT oder VisualVM
4. Debug Container
# Ephemeral Debug Container (K8s 1.25+)
kubectl debug -it <pod> --image=busybox --target=app
# Memory des Host-Systems prüfen
cat /proc/meminfo
Häufige Fehler
Fehler 1: Nur Limit setzen, kein Request
# SCHLECHT
resources:
limits:
memory: "512Mi"
# Request wird automatisch = Limit
# Überschätzt den Bedarf, verschwendet Ressourcen
Fehler 2: Request zu hoch
# SCHLECHT - Pod kann nicht geschedult werden
resources:
requests:
memory: "4Gi" # Kein Node hat so viel frei
Fehler 3: JVM Heap = Container Limit
# SCHLECHT - JVM braucht auch Non-Heap!
env:
- name: JAVA_OPTS
value: "-Xmx512m" # Gleich wie Limit
resources:
limits:
memory: "512Mi" # OOMKilled garantiert!
Korrekt:
env:
- name: JAVA_OPTS
value: "-Xmx384m" # 75% des Limits
resources:
limits:
memory: "512Mi"
Checkliste bei OOMKilled
# 1. ✅ OOMKilled bestätigen
kubectl describe pod <pod> | grep -A 5 "Last State"
# 2. ✅ Aktuelles Limit prüfen
kubectl get pod <pod> -o yaml | grep -A 5 resources
# 3. ✅ Memory-Verbrauch analysieren
kubectl top pod <pod> --containers
# 4. ✅ Memory Leak ausschließen
# - Verbrauch über Zeit beobachten
# - Heap Dump analysieren
# 5. ✅ Limit angemessen erhöhen
kubectl set resources deployment/<name> --limits=memory=512Mi
# 6. ✅ Monitoring einrichten
# - Alert bei 80% Memory-Nutzung
CKA-Prüfungstipp
In der CKA-Prüfung werden Sie:
- OOMKilled-Pods identifizieren müssen
- Memory Limits anpassen
- Resource Quotas verstehen
Wichtige Befehle:
kubectl top pod
kubectl describe pod | grep -A 5 "Last State"
kubectl set resources deployment/<name> --limits=memory=512Mi
Mehr zur Prüfung: CKA Zertifizierung Guide
Zusammenfassung
| Problem | Diagnose | Lösung |
|---|---|---|
| Limit zu niedrig | kubectl top pod | Limit erhöhen |
| Memory Leak | Memory steigt über Zeit | App fixen, Heap begrenzen |
| JVM zu groß | Xmx ≥ Container Limit | Xmx = 75% des Limits |
| Sidecar Memory | top --containers | Limits pro Container |
| Burst Traffic | Events prüfen | HPA einrichten |
Wichtigster Tipp: Container Limit muss immer größer sein als der Peak-Verbrauch der Anwendung!
Dieser Troubleshooting-Guide wird regelmäßig aktualisiert. Letzte Aktualisierung: Januar 2026.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
Java Kubernetes OOMKilled: JVM Heap richtig setzen
Java-Pods werden auf Kubernetes mit OOMKilled (Exit Code 137) beendet. So setzt ihr JVM Heap, MaxRAMPercentage und Container-Limits korrekt für stabile Java-Workloads.
CoreDNS Debugging: Kubernetes-DNS Probleme lösen
DNS-Probleme in Kubernetes systematisch debuggen: CoreDNS-Logs, ndots-Konfiguration, NXDOMAIN-Fehler und Corefile-Optimierung für schnellere Auflösung.
Ephemeral Containers: Live-Debugging in Kubernetes
Ephemeral Containers fuer Live-Debugging in Kubernetes nutzen. Mit kubectl debug laufende Pods analysieren, Distroless-Images debuggen und Netzwerkprobleme loesen.
Kubernetes Incident Management: Runbooks erstellen
Effektive Runbooks für Kubernetes-Incidents erstellen: Vorlagen für CrashLoopBackOff, OOMKilled und Node-Ausfälle mit konkreten Debugging-Befehlen.
Kubernetes Troubleshooting: Systematisch debuggen
Kubernetes-Probleme systematisch debuggen mit kubectl describe, logs und debug. Lösungen für ImagePullBackOff, CrashLoopBackOff, Pending Pods und DNS-Fehler.