Veröffentlicht am

Kubernetes Performance Tuning: Bottlenecks finden

Teilen:
Authors

Kubernetes Performance Tuning: Vom Bottleneck zur optimalen Auslastung

TL;DR

  • 90% der Performance-Probleme in Kubernetes lassen sich auf falsch gesetzte Resource Requests und Limits zurueckfuehren
  • CPU-Limits sind in den meisten Faellen kontraproduktiv -- CPU-Requests reichen aus, um faire Verteilung zu garantieren
  • Der Cluster Autoscaler skaliert Nodes, nicht Pods. Der HPA skaliert Pods. Beide muessen zusammenspielen
  • Messen vor Optimieren: Ohne Prometheus-Baseline ist jede Aenderung Blindflug
  • VPA im Recommendation-Mode liefert bessere Insights als manuelles Raten bei Requests

Das Resource-Modell verstehen

Kubernetes verwaltet Compute-Ressourcen ueber zwei Mechanismen: Requests und Limits. Dieses Modell ist einfach zu beschreiben, aber die Auswirkungen sind subtil und oft missverstanden.

Requests sind Garantien. Der Scheduler platziert einen Pod nur auf einem Node, der die angeforderten Ressourcen frei hat. Sind 250m CPU requested, muessen 250m auf dem Node verfuegbar sein. Requests bestimmen also, wo ein Pod landet.

Limits sind Obergrenzen. Bei CPU fuehren Limits zu Throttling (der Kernel drosselt den Prozess via CFS Bandwidth Control). Bei Memory fuehren Limits zu OOMKill, wenn der Container mehr als das Limit allokiert.

Der entscheidende Punkt: Zu hohe Requests verschwenden Kapazitaet (Nodes sind "voll" laut Scheduler, aber real unterausgelastet). Zu niedrige Requests fuehren zu Overcommitment und Instabilitaet unter Last.

CPU Limits: Warum man sie oft weglassen sollte

Ein kontroverses Thema, aber durch Daten gut belegt: CPU-Limits verursachen in vielen Faellen mehr Probleme als sie loesen.

Das Problem liegt im Linux CFS (Completely Fair Scheduler). CPU-Limits werden ueber CFS Quota implementiert. Wenn ein Container sein CPU-Budget in einer Periode verbraucht hat, wird er fuer den Rest der Periode gedrosselt -- auch wenn die CPU idle ist.

Das Ergebnis: Latency-Spikes, die schwer zu diagnostizieren sind. Die CPU-Auslastung sieht niedrig aus (weil der Container ja gedrosselt wird), aber die Anwendung ist langsam.

Die Empfehlung: CPU-Requests setzen (fuer faire Scheduling-Verteilung), CPU-Limits in den meisten Faellen weglassen. Ausnahmen sind Multi-Tenant-Cluster, in denen Isolation kritisch ist.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-service
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-service
  template:
    metadata:
      labels:
        app: api-service
    spec:
      containers:
      - name: api
        image: registry.example.com/api-service:3.1.0
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
          limits:
            # Kein CPU-Limit -- vermeidet CFS Throttling
            memory: "1Gi"
        ports:
        - containerPort: 8080
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 15
          failureThreshold: 3

Beachte: Memory-Limits sollten immer gesetzt werden. Ein Memory-Leak ohne Limit killt den gesamten Node via OOM-Killer auf Kernel-Ebene.

Bottlenecks finden: Die richtigen Metriken

Bevor man optimiert, muss man messen. Die folgenden PromQL-Queries decken die haeufigsten Performance-Probleme auf.

CPU Throttling erkennen:

# Container mit CPU-Throttling in den letzten 5 Minuten
# Werte ueber 25% sind problematisch
kubectl exec -n monitoring prometheus-0 -- \
  promtool query instant http://localhost:9090 \
  'sum by (namespace, pod, container) (
    rate(container_cpu_cfs_throttled_periods_total[5m])
    /
    rate(container_cpu_cfs_periods_total[5m])
  ) > 0.25'

Memory-Ueberallokation finden:

# Verhaeltnis von tatsaechlicher Nutzung zu Requests
# Werte unter 0.5 bedeuten: Requests sind zu hoch
kubectl exec -n monitoring prometheus-0 -- \
  promtool query instant http://localhost:9090 \
  'sum by (namespace, pod) (
    container_memory_working_set_bytes
  )
  /
  sum by (namespace, pod) (
    kube_pod_container_resource_requests{resource="memory"}
  ) < 0.5'

Diese Queries sind der Ausgangspunkt. Fuer ein vollstaendiges Monitoring-Setup verweisen wir auf Kubernetes Monitoring mit Open Source.

Horizontal Pod Autoscaler richtig konfigurieren

Der HPA ist das primaere Werkzeug fuer Workload-Skalierung. Die Default-Konfiguration (Skalierung bei 80% CPU) ist fuer viele Anwendungen nicht optimal.

Wichtige Parameter, die oft uebersehen werden:

ParameterDefaultEmpfehlungWarum
behavior.scaleDown.stabilizationWindowSeconds300s300-600sVerhindert Flapping bei schwankender Last
behavior.scaleUp.stabilizationWindowSeconds0s0-60sScale-Up sollte schnell sein
behavior.scaleDown.policies[].periodSeconds60s60-120sWie schnell runterskaliert wird
metrics[].resource.target.averageUtilization80%Abhaengig vom WorkloadIO-bound Apps brauchen andere Werte als CPU-bound
minReplicas1Mindestens 2 fuer HA1 Replica = kein Rolling Update moeglich

Ein praxistaugliches HPA-Manifest fuer einen API-Service:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-service-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api-service
  minReplicas: 3
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 25
        periodSeconds: 120

Die behavior-Section ist entscheidend. Ohne sie skaliert der HPA mit Default-Werten, die fuer Burst-Traffic oft zu langsam reagieren (Scale-Up) oder bei normaler Varianz zu aggressiv runterskalieren (Scale-Down).

Cluster Autoscaler: Nodes nachziehen

Der HPA skaliert Pods, aber wenn keine Node-Kapazitaet vorhanden ist, bleiben Pods im Status Pending. Hier kommt der Cluster Autoscaler (CA) ins Spiel.

Haeufige Fehler bei der CA-Konfiguration:

Zu kleine Node-Pools. Wenn der CA nur zwischen 2 und 4 Nodes skalieren darf, ist der Spielraum zu gering. Besser: min: 3, max: 15 und ueber Budget-Alerts absichern.

Nur einen Node-Pool verwenden. Unterschiedliche Workloads haben unterschiedliche Anforderungen. Ein Node-Pool mit 4 vCPU/8 GB fuer allgemeine Workloads und ein separater Pool mit 8 vCPU/32 GB fuer speicherintensive Anwendungen ist effizienter als ein Einheits-Pool.

Scale-Down nicht testen. Der CA entfernt Nodes nur, wenn alle Pods auf dem Node woanders Platz finden. DaemonSets, Pods mit lokalen Persistent Volumes oder Pods ohne Controller (bare Pods) blockieren das Scale-Down. Wer Kosten sparen will, muss das Scale-Down-Verhalten regelmaessig validieren.

Eine tiefergehende Analyse zum Kapazitaetsmanagement bietet Kubernetes Capacity Planning.

VPA: Automatische Empfehlungen fuer Requests

Der Vertical Pod Autoscaler (VPA) kann Requests automatisch anpassen. In der Praxis empfiehlt sich der "Recommendation"-Modus: Der VPA analysiert die tatsaechliche Nutzung und gibt Empfehlungen, ohne automatisch Aenderungen vorzunehmen.

So nutzt man den VPA als Analyse-Tool:

  1. VPA im Mode Off deployen (nur Empfehlungen, keine Aenderungen)
  2. Nach 7 Tagen die Empfehlungen auswerten
  3. Empfehlungen manuell in die Deployment-Manifeste uebernehmen
  4. Nach der Anpassung erneut validieren

Die VPA-Empfehlungen sind besonders wertvoll fuer Workloads, die schon lange laufen und deren Requests nie angepasst wurden. Oft findet man 3-5x ueberallokierte Deployments, die erhebliche Cluster-Kapazitaet blockieren.

Netzwerk-Performance: CNI-Wahl und Service Mesh Overhead

Die CNI-Wahl hat direkten Einfluss auf die Netzwerk-Latenz. Ein Vergleich der gaengigsten Optionen:

CNIThroughputLatencyeBPF SupportNetworkPolicyKomplexitaet
CiliumHochNiedrigJa (nativ)Ja (erweitert)Mittel
CalicoHochNiedrigJa (optional)JaMittel
FlannelMittelMittelNeinNein (nativ)Niedrig
WeaveMittelMittelNeinJaNiedrig

Cilium mit eBPF-basiertem Datapath umgeht iptables komplett, was bei Clustern mit vielen Services (500+) einen messbaren Latenz-Vorteil bringt. Fuer kleinere Setups ist der Unterschied marginal.

Service Meshes (Istio, Linkerd) fuegen Sidecar-Proxies hinzu, die pro Hop 1-3ms Latenz addieren. Das klingt wenig, kann aber bei tief verschachtelten Microservice-Calls (10+ Hops) relevant werden. Linkerd ist hier schlanker als Istio.

Storage-Performance: Die vergessene Dimension

Storage ist oft der groesste Bottleneck, wird aber zuletzt untersucht. Typische Probleme:

Falsche StorageClass. Standard-StorageClasses bei Cloud-Providern liefern oft nur 3.000 IOPS. Fuer Datenbank-Workloads (PostgreSQL, MySQL) braucht man 10.000+ IOPS. Pruefe die StorageClass-Parameter und waehle SSD-basierte Optionen.

ReadWriteOnce-Limitierung. PVCs mit ReadWriteOnce koennen nur von einem Node gemountet werden. Wenn der Pod auf einen anderen Node verschoben wird (z.B. durch Node-Drain), muss der PV erst detached und reattached werden. Das kann Minuten dauern. Fuer Details zur Storage-Konfiguration siehe Kubernetes Storage: DSGVO-konforme Loesungen.

CSI-Driver-Konfiguration. Viele CSI-Treiber unterstuetzen Volume-Expansion, Snapshots und Topology-Awareness. Diese Features muessen oft explizit aktiviert werden.

Container-Image-Optimierung

Grosse Container-Images sind ein versteckter Performance-Killer. Jedes Deployment, jedes Scale-Up-Event erfordert einen Image-Pull. Bei einem 2 GB Image und 10 neuen Pods sind das 20 GB Download-Traffic.

Praktische Massnahmen:

  • Multi-Stage Builds verwenden: Build-Dependencies gehoeren nicht ins finale Image
  • Alpine oder Distroless als Base-Image statt ubuntu/debian
  • Layer-Caching optimieren: Selten aendernde Layers (OS-Pakete) oben, haeufig aendernde (App-Code) unten
  • Image-Registry im selben Netzwerk/Region betreiben

Ein gut optimiertes Go-Service-Image ist 15-30 MB gross. Ein typisches Python-Service-Image mit pip-Installations liegt bei 200-500 MB. Der Unterschied ist bei Scale-Up-Events direkt spuerbar.

Probes: Fein konfigurieren statt Defaults

Falsch konfigurierte Probes sind eine haeufige Ursache fuer unnoetige Pod-Restarts und Traffic-Verlust.

Liveness Probes: Prueft, ob der Prozess noch lebt. Wichtig: Die Liveness-Probe sollte NICHT gegen externe Dependencies pruefen (Datenbank, Cache). Wenn die Datenbank kurz nicht erreichbar ist, soll der Pod nicht neugestartet werden, denn der Neustart loest das Problem nicht.

Readiness Probes: Prueft, ob der Pod Traffic empfangen kann. Hier duerfen externe Dependencies geprueft werden, da ein unready Pod einfach keinen Traffic bekommt, aber weiterlaeuft.

Startup Probes: Fuer Anwendungen mit langer Startzeit. Verhindert, dass Liveness-Probes die App toeten, bevor sie gestartet ist. Besonders wichtig fuer JVM-basierte Anwendungen.

Ein haeufiger Fehler: initialDelaySeconds auf 5 Sekunden bei einer Java-App, die 30 Sekunden zum Starten braucht. Resultat: Die Liveness-Probe killt den Pod in einer Endlosschleife.

Deployment-Strategien fuer Performance

Rolling Updates sind der Standard, aber die Default-Konfiguration ist konservativ. maxSurge: 25% und maxUnavailable: 25% bedeuten bei 4 Replicas, dass maximal 1 Pod gleichzeitig ersetzt wird.

Fuer schnellere Rollouts bei nicht-kritischen Services kann man maxSurge erhoehen. Fuer Zero-Downtime bei kritischen Services sollte maxUnavailable: 0 gesetzt werden, was bedeutet, dass immer die volle Kapazitaet verfuegbar ist.

Canary Deployments eignen sich, wenn man Performance-Regressions frueh erkennen will. 5% des Traffics auf die neue Version routen, Response-Zeiten vergleichen, dann stufenweise erhoehen. Unser Guide zu Kubernetes Canary Deployments beschreibt den Prozess im Detail.

Checkliste: Die 10 wichtigsten Quick Wins

  1. Alle Deployments auf korrekte Requests und Memory-Limits pruefen
  2. CPU-Limits bei latenz-sensitiven Services entfernen
  3. VPA im Recommendation-Mode fuer die Top-20-Deployments aktivieren
  4. HPA mit benutzerdefinierten Scaling-Behaviors fuer kritische Services
  5. Container-Images auf unnoetige Layers pruefen (dive-Tool)
  6. Liveness-Probes auf externe Dependencies pruefen (und entfernen)
  7. Node-Pool-Strategie: Workload-spezifische Node-Typen statt Einheits-Nodes
  8. Storage-IOPS der verwendeten StorageClasses validieren
  9. Pod Topology Spread Constraints fuer HA-kritische Services
  10. Prometheus-Dashboards fuer CPU-Throttling und OOMKill-Events einrichten

Fazit

Kubernetes-Performance-Optimierung ist kein einmaliges Projekt, sondern ein kontinuierlicher Prozess. Der wichtigste Schritt ist, Sichtbarkeit zu schaffen: Wer seine Cluster-Auslastung nicht kennt, kann nicht optimieren.

Die gute Nachricht: Die meisten Performance-Probleme haben einfache Ursachen. Falsche Requests, fehlende Autoscaler-Konfiguration, uebergrosse Images. Die hier beschriebenen Massnahmen lassen sich innerhalb weniger Wochen umsetzen und liefern messbare Ergebnisse.

Wer Unterstuetzung bei der Analyse oder Implementierung braucht, kann sich gerne an uns wenden. Ein initiales Cluster-Audit ist oft der effizienteste Einstiegspunkt: Kontakt aufnehmen.

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

kubernetesobservability

Prometheus PCA Zertifizierung für Kubernetes Monitoring

Die Prometheus Certified Associate (PCA) Zertifizierung bietet eine fundierte Validierung von Kubernetes Monitoring-Kenntnissen mit Prometheus. Erfahren Sie, wie diese offizielle CNCF-Zertifizierung deutschen Unternehmen hilft, ihre Observability-Strategien zu professionalisieren und die Betriebsstabilität ihrer Cloud-nativen Infrastrukturen zu gewährleisten – essenziell für ein zuverlässiges Kubernetes Monitoring in Deutschland.

Weiterlesen →