Das Problem: Manuelle Intervention
Klassisches Szenario:
- 3 AM: Pod crasht
- 3:05 AM: Monitoring alert
- 3:08 AM: SRE wacht auf
- 3:15 AM: SRE logged sich ein, prüft logs
- 3:20 AM: SRE restarts pod
- 3:21 AM: Service funktioniert wieder
18 Minuten Downtime für etwas, das Kubernetes hätte selbst machen können.
Mit Self-Healing Patterns machen sich deine Systeme selbst gesund – ohne menschliche Intervention. Das ist das Versprechen von Kubernetes, aber die meisten Teams implementieren diese Patterns falsch.
Pattern 1: Liveness Probe (Pod Gesundheit überwachen)
Ein Liveness Probe prüft, ob ein Container noch "lebt" oder "tot" ist. Wenn der Probe scheitert, startet Kubernetes den Container automatisch neu.
apiVersion: v1
kind: Pod
metadata:
name: api-service
spec:
containers:
- name: api
image: my-api:v1
ports:
- containerPort: 8080
# Liveness Probe: Prüfe alle 10 Sekunden ob /health OK ist
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30 # Gib dem Container Zeit zu starten
periodSeconds: 10 # Prüfe alle 10 Sekunden
timeoutSeconds: 2 # Timeout nach 2 Sekunden
failureThreshold: 3 # Erst nach 3 Failures restarten
Was der Probe macht:
- Alle 10 Sekunden macht Kubernetes einen HTTP GET auf
http://localhost:8080/health - Wenn der Container antwortet mit Status 200, alles gut
- Wenn die Probe 3x hintereinander fehlschlägt, startet Kubernetes den Container neu
- Das alles passiert automatisch ohne menschliches Zutun
Häufiger Fehler: Zu aggressiver Probe
# FALSCH: Restarts den Container sekündlich bei kleinen Problemen
livenessProbe:
periodSeconds: 1 # 🚫 Zu häufig!
failureThreshold: 1 # 🚫 Zu streng!
# RICHTIG: Gibt dem Container Zeit sich zu erholen
livenessProbe:
periodSeconds: 10 # ✅ Alle 10 Sekunden
failureThreshold: 3 # ✅ Nach 3 Fehlern restarten
initialDelaySeconds: 30 # ✅ 30s zum Startup
Real-World Beispiel:
# Node.js Express Service
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
# In deinem App Code:
app.get('/health', (req, res) => {
// Prüfe ob Database noch erreichbar ist
if (db.connection.isConnected) {
res.status(200).json({ status: 'alive' });
} else {
res.status(503).json({ status: 'unhealthy' });
}
});
Pattern 2: Readiness Probe (Ist der Service bereit für Traffic?)
Unterschied zu Liveness Probe:
- Liveness: "Lebt der Container noch?" → Restart ihn wenn nicht
- Readiness: "Kann dieser Container Traffic handlen?" → Gib ihm keinen Traffic wenn nicht
apiVersion: v1
kind: Pod
metadata:
name: api-service
spec:
containers:
- name: api
image: my-api:v1
# Readiness: Prüfe ob Service Traffic verarbeiten kann
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5 # Prüfe nach 5 Sekunden
periodSeconds: 5 # Alle 5 Sekunden
successThreshold: 1 # Mindestens 1 Success
failureThreshold: 2 # Nach 2 Fehlern: kein Traffic
Klassisches Scenario:
10:00 - Pod startet
10:00 - Kubernetes fragt Liveness Probe: "Lebst du?" → JA
10:00 - Kubernetes schickt Traffic hin
10:02 - Database Connection Pool wird aufgebaut (dauert 2 Min)
10:02 - Container kann noch keine Database Queries verarbeiten
10:02 - Error Rate 100% weil API noch nicht ready ist
Besser mit Readiness:
10:00 - Pod startet
10:00 - Kubernetes fragt Liveness: "Lebst du?" → JA
10:00 - Kubernetes fragt Readiness: "Kannst du Traffic?" → NEIN (DB Pool noch nicht ready)
10:00 - Kubernetes **schickt keinen Traffic hin**
10:02 - Readiness sagt JA (DB Pool ready)
10:02 - **Jetzt schickt Kubernetes Traffic hin**
10:02 - Error Rate 0% weil alles ready ist
In deinem Code:
// Readiness Endpoint
app.get('/ready', async (req, res) => {
const checks = {
database: await db.isConnected(),
cache: await redis.ping(),
externalAPI: await checkExternalServiceHealth(),
};
if (Object.values(checks).every(c => c === true)) {
res.status(200).json({ ready: true });
} else {
res.status(503).json({ ready: false, details: checks });
}
});
Pattern 3: Pod Disruption Budgets (PDB)
Wenn Kubernetes dein Cluster updatet, drains es Nodes – das heißt, es verschiebt Pods auf andere Nodes. Ohne PDB können alle deine Pods gleichzeitig runter gehen.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-service-pdb
spec:
minAvailable: 2 # Mindestens 2 Replicas müssen immer laufen
selector:
matchLabels:
app: api-service
Was das macht:
- Wenn dein Cluster 5 api-service Pods hat und Kubernetes updatet
- Kubernetes versucht, mindestens 2 Replicas laufen zu halten
- Es stoppt maximal 3 Pods zur gleichen Zeit
- Dadurch bleibt der Service verfügbar
Scenario ohne PDB:
Before: 5 API Pods laufen
Cluster Update startet
Kubernetes: "Ich stoppe alle 5 Pods, um sie zu updaten"
Alle 5 Pods down
Service hat 100% Downtime für 5 Minuten
Scenario mit PDB (minAvailable: 2):
Before: 5 API Pods laufen
Cluster Update startet
Kubernetes: "Ich muss mindestens 2 behalten"
Stoppt 3 Pods → 2 Pods laufen noch
Traffic geht an die 2 laufenden Pods
3 Pods updaten
3 Pods starten wieder
Deine 2 anderen Pods stoppen
Die anderen 2 updaten und starten
Service hatte ~0% Downtime
Best Practice:
# Für stateless Services
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2 # Mindestens 2 müssen laufen
selector:
matchLabels:
tier: api
---
# Für Datenbank oder stateful Services
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: db-pdb
spec:
minAvailable: 1 # Nur 1 muss laufen (wichtig für Quorum)
selector:
matchLabels:
tier: database
Pattern 4: Horizontal Pod Autoscaling (HPA)
Wenn Last ansteigt, skaliere mehr Pods. Wenn Last sinkt, skaliere weniger.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Skaliere wenn CPU > 70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80 # Skaliere wenn Memory > 80%
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 5 Min. bevor down-scaling
scaleUp:
stabilizationWindowSeconds: 0 # Sofort skalieren
Scenario:
10:00 - Traffic normal: 2 Pods laufen
10:05 - Black Friday Sale startet: Traffic 10x
10:06 - CPU erreicht 75%
10:07 - HPA sieht das, erstellt 2 neue Pods
10:08 - 4 Pods laufen, Traffic besser verteilt
10:09 - CPU sinkt auf 65%, alles gut
11:00 - Sale vorbei, Traffic normal
11:05 - CPU unter 70%, HPA startet Downscaling
11:10 - Nur noch 2 Pods (sparen Kosten)
Häufiger Fehler:
# FALSCH: Zu aggressive Thresholds
metrics:
- resource:
name: cpu
target:
averageUtilization: 30 # 🚫 Skaliert ständig rauf und runter
# RICHTIG: Vernünftige Thresholds
metrics:
- resource:
name: cpu
target:
averageUtilization: 70 # ✅ Gibt dir noch Buffer
Pattern 5: Resource Limits und Requests
Resource Limits verhindern, dass ein Pod alle Ressourcen des Nodes aufbraucht und andere Pods erstickt.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: my-api:v1
# Request: Was der Pod mindestens braucht
# Kubernetes schedulet auf einen Node mit genug Platz
resources:
requests:
cpu: 250m # 250 milliCPU = 1/4 CPU
memory: 256Mi # 256 MB
# Limit: Maximum was der Pod nutzen darf
# Wenn Pod über Limit geht, wird er killed
limits:
cpu: 500m # Maximum 500 milliCPU
memory: 512Mi # Maximum 512 MB
Scenario ohne Limits:
10:00 - Pod hat Memory Leak, nutzt immer mehr Memory
10:15 - Pod nutzt 90% des Node Memory
10:16 - Andere Pods können nicht starten
10:17 - Cluster ist praktisch down
10:30 - SRE findet das Leak
Scenario mit Limits (512Mi):
10:00 - Pod hat Memory Leak
10:05 - Pod nutzt 512Mi (= Limit)
10:06 - Kubernetes killed den Pod (OOMKilled)
10:07 - Kubernetes startet neuen Pod Replica
10:08 - Neue Instanz läuft, alte war weg
10:09 - SRE sieht die OOMKilled Events und findet das Problem
Downtime: ~1-2 Minuten statt 30 Minuten
Best Practices für Limits:
# Node hat 4 CPUs, 16GB RAM
# Du planst 5 Pods darauf
# Pro Pod
requests:
cpu: 700m # 5 × 700m = 3500m = 3.5 CPUs (unter 4)
memory: 2Gi # 5 × 2Gi = 10Gi (unter 16Gi)
limits:
cpu: 1000m # Gib etwas Buffer
memory: 3Gi
Wie KI-Ops damit hilft
KI-Ops kann detektieren, wenn deine Self-Healing Patterns falsch konfiguriert sind:
ki-ops validate --namespace production
# Output:
❌ api-service: Keine Liveness Probe
Empfehlung: Füge /health Endpoint mit Liveness Probe hinzu
❌ api-service: Memory Limit zu klein (256Mi)
Aktueller Verbrauch: ~450Mi durchschnittlich
Empfehlung: Erhöhe auf 512Mi um OOMKills zu vermeiden
⚠️ api-service: Nur 1 Replica, aber PDB sagt minAvailable: 1
Empfehlung: Erhöhe Replicas auf mindestens 2 für HA
✅ api-service: HPA und Resource Limits richtig konfiguriert
Messbare Vorteile
Mit allen 5 Patterns:
| Metrik | Vorher | Nachher | Verbesserung | |--------|--------|---------|------------| | Unerwarteter Downtime | 20h/Monat | 2h/Monat | 90% weniger | | Manuelles Restarten | 40x/Monat | 0x/Monat | Automatisch | | MTTR | 45 Min | 5 Min | 89% schneller | | SRE Time für Ops | 80h/Monat | 10h/Monat | 88% weniger Toil | | Cluster Update Downtime | 30 Min | 0 Min | Völlig seamless |
Das Checkliste
Für jeden Production Service:
- [ ] Liveness Probe (prüft ob Container lebt)
- [ ] Readiness Probe (prüft ob Container Traffic kan)
- [ ] Resource Requests gesetzt (scheduling)
- [ ] Resource Limits gesetzt (no noisy neighbors)
- [ ] HPA konfiguriert (auto-scale)
- [ ] PDB mit minAvailable (cluster updates)
- [ ] mindestens 2 Replicas (High Availability)
Wenn all das gesetzt ist, ist dein Service 99.9% selbst-heilend.
Nächster Schritt: Nutze KI-Ops um diese Patterns in deinem Cluster zu überprüfen.