Veröffentlicht am

Kubernetes Bulkhead Pattern: Service-Isolation einrichten

Teilen:
Authors

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

RessourceRequestLimitBulkhead-Effekt
CPUGarantierte CPU-ZeitWird bei Ueberschreitung gedrosseltService wird langsamer, stiehlt aber keine CPU von Nachbarn
MemoryGarantierter SpeicherWird 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:

ServiceNode PoolNamespaceCPU LimitConnection Pool
API Gatewaysharedplatform2 CPU500 Connections
Paymentcriticalteam-payment1 CPU50 Connections
Ordercriticalteam-order1 CPU100 Connections
Catalogsharedteam-catalog500m200 Connections
Searchsharedteam-search1 CPU150 Connections
Analyticsbatchteam-analytics4 CPU30 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

MetrikSchwellwertAktion
upstream_rq_pending_overflowUeber 0 fuer 2 MinutenConnection Pool vergroessern oder Service skalieren
upstream_cx_active vs. maxConnectionsUeber 80 ProzentWarnung: Pool fast voll
Namespace CPU vs. QuotaUeber 90 ProzentResourceQuota erhoehen oder Workloads optimieren

Best Practices

  1. Resource Limits auf jeden Container setzen -- ohne Limits gibt es kein Bulkhead auf Pod-Ebene
  2. ResourceQuotas auf jeden Namespace -- verhindert, dass ein Team den Cluster beansprucht
  3. Connection Pools pro Service dimensionieren -- basierend auf gemessener Last, nicht auf Schaetzungen
  4. Kritische Services auf dedizierte Node Pools -- die staerkste Isolation erfordert physische Trennung
  5. PriorityClasses definieren -- mindestens drei Stufen: critical, standard, batch
  6. 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

kubernetesresilience

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.

Weiterlesen →