- Authors

- Name
- Phillip Pham
- @ddppham
Kubernetes Bulkhead Pattern: Isolation und Fehlerbegrenzung fuer Services
TL;DR
- Das Bulkhead Pattern isoliert Fehler-Domaenen, sodass der Ausfall eines Service nicht das Gesamtsystem lahmlegt -- benannt nach den wasserdichten Schotten in Schiffsruempfen
- Kubernetes Resource Limits sind das natuerlichste Bulkhead: Jeder Pod bekommt ein festes CPU- und Memory-Budget
- Namespace-basierte Isolation mit ResourceQuotas verhindert, dass ein Team den gesamten Cluster beansprucht
- Istio Connection Pools begrenzen parallele Verbindungen pro Service und schuetzen vor Kaskaden-Ausfaellen
- Separate Node Pools fuer kritische Services bieten die staerkste Isolation auf Hardware-Ebene
Das Bulkhead-Konzept: Schotten dicht
Der Name stammt aus dem Schiffbau. Ein Schiffsrumpf ist in wasserdichte Abteilungen (Bulkheads) unterteilt. Wenn ein Leck in einer Abteilung entsteht, fliesst Wasser nur in diesen Bereich. Die anderen Abteilungen bleiben trocken und das Schiff bleibt schwimmfaehig.
In einer Microservices-Architektur ist das Prinzip identisch: Wenn ein Service ausfaellt oder langsam wird, darf das nur diesen Service betreffen -- nicht alle anderen.
Ohne Bulkhead:
Shared Thread Pool [||||||||||||||||]
Service A: 80% der Threads blockiert (langsam)
Service B: kann keine Threads mehr bekommen
Service C: kann keine Threads mehr bekommen
*/} Gesamtausfall
Mit Bulkhead:
Pool A [|||||___] Service A: langsam, nutzt seinen Pool aus
Pool B [||______] Service B: funktioniert normal
Pool C [|||_____] Service C: funktioniert normal
*/} Nur Service A ist betroffen
Ebene 1: Resource Limits und Requests
Kubernetes Resource Limits sind die Grundform des Bulkhead Patterns. Jeder Container erhaelt ein definiertes Budget an CPU und Memory, das er nicht ueberschreiten kann.
Deployment mit Resource Isolation
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
tier: critical
spec:
containers:
- name: payment
image: registry.example.com/payment-service:v3.2.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
failureThreshold: 2
Requests vs. Limits: Die Bulkhead-Mechanik
| Ressource | Request | Limit | Bulkhead-Effekt |
|---|---|---|---|
| CPU | Garantierte CPU-Zeit | Wird bei Ueberschreitung gedrosselt | Service wird langsamer, stiehlt aber keine CPU von Nachbarn |
| Memory | Garantierter Speicher | Wird bei Ueberschreitung terminiert (OOMKilled) | Service stirbt, aber andere Pods bleiben stabil |
Ohne Limits gibt es kein Bulkhead. Ein Container ohne Memory-Limit kann den gesamten Node-Speicher aufbrauchen und alle anderen Pods auf dem Node zum OOMKill fuehren.
Fuer die korrekte Dimensionierung von Resource Limits empfehlen wir den Beitrag zu Kubernetes Resource Management.
Ebene 2: Namespace-basierte Isolation mit ResourceQuotas
Namespaces sind das natuerliche Bulkhead auf Cluster-Ebene. Mit ResourceQuotas begrenzen Sie die Gesamtressourcen, die ein Namespace verbrauchen darf.
ResourceQuota fuer Team-Isolation
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-checkout-quota
namespace: team-checkout
spec:
hard:
requests.cpu: "8"
requests.memory: 16Gi
limits.cpu: "16"
limits.memory: 32Gi
pods: "40"
services: "10"
persistentvolumeclaims: "10"
LimitRange: Defaults und Grenzen pro Container
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-checkout
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 250m
memory: 256Mi
max:
cpu: "2"
memory: 4Gi
min:
cpu: 50m
memory: 64Mi
Wenn ein Entwickler ein Deployment ohne Resource-Angaben erstellt, greift der LimitRange als Sicherheitsnetz. Kein Container kann mehr als 2 CPU Cores und 4Gi Memory beanspruchen, unabhaengig davon was im Deployment-Manifest steht.
Fuer die umfassende Namespace-Verwaltung mit Policies und RBAC lesen Sie Kubernetes Namespace Management.
Ebene 3: Istio Connection Pool Isolation
Auf Netzwerk-Ebene implementiert Istio Bulkheads ueber separate Connection Pools pro Upstream-Service. Jeder Service erhaelt ein eigenes Budget fuer TCP-Verbindungen und HTTP-Requests.
Connection Pool pro Service
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: payment-service-bulkhead
namespace: production
spec:
host: payment-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 50
connectTimeout: 3s
http:
http1MaxPendingRequests: 25
http2MaxRequests: 100
maxRequestsPerConnection: 10
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: inventory-service-bulkhead
namespace: production
spec:
host: inventory-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 30
http:
http1MaxPendingRequests: 15
http2MaxRequests: 60
maxRequestsPerConnection: 5
Wie Connection Pool Bulkheads wirken
Wenn der inventory-service langsam antwortet und alle 30 TCP-Verbindungen belegt sind, werden neue Requests an den inventory-service mit einem 503 abgelehnt. Aber die 50 Verbindungen zum payment-service sind davon voellig unberuehrt.
Ohne separate Connection Pools wuerden beide Services denselben Verbindungspool teilen. Ein langsamer inventory-service koennte alle verfuegbaren Verbindungen blockieren und damit auch den payment-service unerreichbar machen.
Fuer die vollstaendige Circuit Breaker Konfiguration, die auf diesen Connection Pools aufbaut: Kubernetes Circuit Breaker.
Ebene 4: Separate Node Pools fuer kritische Services
Die staerkste Form der Isolation: Kritische Services laufen auf dedizierten Nodes, die fuer keine anderen Workloads verfuegbar sind.
Node Pool mit Taints und Tolerations
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
namespace: production
spec:
replicas: 4
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
spec:
tolerations:
- key: "tier"
operator: "Equal"
value: "critical"
effect: "NoSchedule"
nodeSelector:
tier: critical
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: payment-service
containers:
- name: payment
image: registry.example.com/payment-service:v3.2.0
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
Node Taints setzen Sie vorab per kubectl:
# Taint und Label auf die Nodes des kritischen Pools setzen
kubectl taint nodes -l nodepool=critical tier=critical:NoSchedule
kubectl label nodes -l nodepool=critical tier=critical
Durch den Taint tier=critical:NoSchedule koennen nur Pods mit der entsprechenden Toleration auf diesen Nodes laufen. Ein fehlerhafter Batch-Job oder ein Memory-Leak in einem anderen Service kann die Payment-Nodes nicht beeintraechtigen.
Ebene 5: PriorityClasses fuer Scheduling-Isolation
Wenn Cluster-Ressourcen knapp werden, entscheiden PriorityClasses, welche Pods ueberleben und welche verdraengt werden.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: critical-services
value: 1000000
globalDefault: false
preemptionPolicy: PreemptLowerPriority
description: "Fuer zahlungskritische Services wie Payment und Order"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: standard-services
value: 100000
globalDefault: true
preemptionPolicy: PreemptLowerPriority
description: "Standard-Prioritaet fuer regulaere Services"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: batch-workloads
value: 10000
globalDefault: false
preemptionPolicy: Never
description: "Niedrige Prioritaet fuer Batch-Jobs und Analytics"
Wenn der Cluster voll ist und ein neuer Payment-Pod gestartet werden muss, verdraengt Kubernetes automatisch Pods mit niedrigerer Prioritaet. Das ist ein zeitliches Bulkhead: Kritische Services haben immer Vorrang bei der Ressourcenvergabe.
Thread Pool Isolation auf Anwendungsebene
Neben der Infrastruktur-Ebene kann Bulkhead-Isolation auch innerhalb der Anwendung stattfinden. Separate Thread Pools pro Downstream-Service verhindern, dass ein langsamer Call alle Threads blockiert.
Java: Resilience4j Bulkhead
BulkheadConfig paymentBulkhead = BulkheadConfig.custom()
.maxConcurrentCalls(20)
.maxWaitDuration(Duration.ofMillis(500))
.build();
BulkheadConfig inventoryBulkhead = BulkheadConfig.custom()
.maxConcurrentCalls(10)
.maxWaitDuration(Duration.ofMillis(200))
.build();
Bulkhead paymentPool = Bulkhead.of("paymentService", paymentBulkhead);
Bulkhead inventoryPool = Bulkhead.of("inventoryService", inventoryBulkhead);
// Payment-Calls: max 20 parallel
Supplier<Payment> paymentCall = Bulkhead.decorateSupplier(
paymentPool, () -> paymentClient.charge(amount)
);
// Inventory-Calls: max 10 parallel
Supplier<Stock> inventoryCall = Bulkhead.decorateSupplier(
inventoryPool, () -> inventoryClient.checkStock(productId)
);
Praxis-Beispiel: E-Commerce mit Bulkhead-Architektur
Eine typische E-Commerce-Plattform mit vier Isolation-Ebenen:
| Service | Node Pool | Namespace | CPU Limit | Connection Pool |
|---|---|---|---|---|
| API Gateway | shared | platform | 2 CPU | 500 Connections |
| Payment | critical | team-payment | 1 CPU | 50 Connections |
| Order | critical | team-order | 1 CPU | 100 Connections |
| Catalog | shared | team-catalog | 500m | 200 Connections |
| Search | shared | team-search | 1 CPU | 150 Connections |
| Analytics | batch | team-analytics | 4 CPU | 30 Connections |
Payment und Order laufen auf dedizierten Nodes, damit ein Memory-Leak im Catalog-Service nicht den Payment-Node crashen kann. Separate Namespaces pro Team stellen sicher, dass kein Team versehentlich 80 Prozent der Cluster-CPU verbraucht.
Fuer Multi-Tenancy-Szenarien mit strikterer Isolation zwischen Mandanten lesen Sie Kubernetes Multi-Tenancy.
Monitoring: Bulkhead-Metriken ueberwachen
# Connection Pool Statistiken abfragen
kubectl exec -n production deploy/api-gateway -c istio-proxy -- \
curl -s localhost:15000/stats | grep overflow
# Wichtige Metriken:
# upstream_rq_pending_overflow -- Requests abgelehnt (Pending-Queue voll)
# upstream_cx_pool_overflow -- Neue Verbindungen abgelehnt (Pool voll)
# upstream_cx_active -- Aktuell aktive Verbindungen
Alert-Empfehlungen
| Metrik | Schwellwert | Aktion |
|---|---|---|
| upstream_rq_pending_overflow | Ueber 0 fuer 2 Minuten | Connection Pool vergroessern oder Service skalieren |
| upstream_cx_active vs. maxConnections | Ueber 80 Prozent | Warnung: Pool fast voll |
| Namespace CPU vs. Quota | Ueber 90 Prozent | ResourceQuota erhoehen oder Workloads optimieren |
Best Practices
- Resource Limits auf jeden Container setzen -- ohne Limits gibt es kein Bulkhead auf Pod-Ebene
- ResourceQuotas auf jeden Namespace -- verhindert, dass ein Team den Cluster beansprucht
- Connection Pools pro Service dimensionieren -- basierend auf gemessener Last, nicht auf Schaetzungen
- Kritische Services auf dedizierte Node Pools -- die staerkste Isolation erfordert physische Trennung
- PriorityClasses definieren -- mindestens drei Stufen: critical, standard, batch
- Bulkhead-Metriken monitoren -- ein voller Connection Pool ist ein Fruehindikator fuer Probleme
Fuer das Zusammenspiel aller Resilience Patterns empfehlen wir den Ueberblick in Kubernetes Resilience Patterns.
Sie planen die Isolation Ihrer Kubernetes-Services oder kaempfen mit Kaskadenausfaellen durch fehlende Bulkheads? Wir helfen bei der Architektur von Resource Limits, Namespace-Isolation, Connection Pools und Node-Pool-Strategien. Sprechen Sie uns an unter /kontakt.
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
Resilience Patterns für Kubernetes Microservices
Resilience Patterns für Kubernetes-Microservices: Retry, Circuit Breaker, Bulkhead, Timeout, Health Checks und PDB mit YAML-Konfigurationen.
Kubernetes Retry Pattern mit Istio konfigurieren
Retry-Strategien in Kubernetes mit Istio und Envoy: Exponential Backoff mit Jitter, Idempotenz und Circuit Breaker Integration praxisnah erklärt.
Kubernetes Deutschland: Retry Pattern für resiliente Systeme
Erfahren Sie, wie das Retry Pattern die Resilienz und Fehlertoleranz von Kubernetes-Anwendungen in Deutschland optimiert. Steigern Sie die Verfügbarkeit Ihrer Systeme und gewährleisten Sie mit intelligenten Wiederholungsstrategien eine robuste Performance auch in anspruchsvollen Umgebungen.
Kubernetes CQRS Pattern Microservices in Deutschland optimal nutzen
Optimieren Sie Skalierbarkeit und Performance Ihrer komplexen Microservices auf Kubernetes in Deutschland mit dem CQRS Pattern. Erfahren Sie, wie diese zukunftsweisende Architektur Compliance-Anforderungen erfüllt und digitale Souveränität für deutsche Unternehmen sichert.
Distributed Tracing: Jaeger auf Kubernetes einrichten
Jaeger für Distributed Tracing auf Kubernetes einrichten mit dem Jaeger Operator. OpenTelemetry-Instrumentation in Go und Python, Trace-Propagation zwischen Microservices und praktische Analyse von Latenz-Problemen.