Veröffentlicht am

Kubernetes Performance Probleme systematisch lösen

Teilen:
Authors

Kubernetes Performance Probleme: Diagnose und Loesung komplexer Bottlenecks

TL;DR

  • Kubernetes Performance Probleme lassen sich in sechs Kategorien einteilen: CPU, Memory, Network, Storage, etcd und API Server
  • CPU-Throttling ist der haeufigste unsichtbare Performance-Killer -- Pods werden gedrosselt, ohne dass ein OOMKill sichtbar wird
  • Prometheus-Queries und kubectl-Befehle liefern die noetige Datenbasis fuer systematische Diagnose
  • Oft liegt das Problem nicht an Kubernetes selbst, sondern an falschen Resource Requests, ueberladenen Nodes oder ineffizientem Application Code
  • Eine Performance Baseline zu definieren ist die Voraussetzung, um Abweichungen ueberhaupt erkennen zu koennen

Warum Performance-Diagnose in Kubernetes komplex ist

In einer traditionellen VM-Umgebung laeuft eine Anwendung auf einem Server mit bekannten Ressourcen. Ist sie langsam, schaut man auf CPU, RAM, Disk I/O und Netzwerk dieses einen Servers.

In Kubernetes ist das anders. Eine Anwendung laeuft als Pod auf einem Node, der sich Ressourcen mit Dutzenden anderen Pods teilt. Dazwischen liegt ein CNI-Plugin fuer Netzwerk, ein CSI-Treiber fuer Storage, ein kubelet fuer die Container-Verwaltung und der API-Server fuer jede Steuerungsoperation. Jede dieser Schichten kann zum Bottleneck werden.

Hinzu kommt: Kubernetes Performance Probleme aeussern sich oft indirekt. Eine langsame Antwortzeit kann an CPU-Throttling liegen, an DNS-Aufloesung, an einem ueberlasteten Service Mesh, an IOPS-Limits des PersistentVolumes oder an einem etcd, der unter Compaction-Last steht.

Systematisches Vorgehen ist der Schluessel. Wir gehen die sechs Hauptkategorien durch und zeigen fuer jede die Diagnose-Werkzeuge und Loesungen.

1. CPU-Bottlenecks

Das Problem

CPU-Probleme in Kubernetes haben zwei Ursachen: Throttling durch zu niedrige CPU-Limits und Node-Overcommitment durch zu niedrige CPU-Requests.

CPU-Throttling passiert, wenn ein Container sein CPU-Limit erreicht. Der Linux CFS Scheduler (Completely Fair Scheduler) drosselt den Container dann aktiv. Das ist oft unsichtbar, weil kein Error-Log und kein OOMKill entsteht -- die Anwendung wird einfach langsamer.

Node-Overcommitment passiert, wenn die Summe aller CPU-Requests auf einem Node die verfuegbare CPU uebersteigt. Das sollte der Scheduler eigentlich verhindern, kann aber durch Burstable QoS-Klassen (Requests kleiner als Limits) entstehen.

Diagnose

# CPU-Throttling pro Container anzeigen
kubectl top pods -n production --sort-by=cpu

# Detaillierte CPU-Auslastung eines Pods
kubectl exec -n production my-app-pod -- cat /sys/fs/cgroup/cpu/cpu.stat

Prometheus-Queries fuer CPU-Throttling:

# Throttling-Rate pro Container (Anteil der gedrosselten Perioden)
sum by (pod, container) (
  rate(container_cpu_cfs_throttled_periods_total[5m])
) /
sum by (pod, container) (
  rate(container_cpu_cfs_periods_total[5m])
) * 100

# CPU-Auslastung vs. Request (sollte unter 100% liegen)
sum by (pod, container) (
  rate(container_cpu_usage_seconds_total[5m])
) /
sum by (pod, container) (
  kube_pod_container_resource_requests{resource="cpu"}
) * 100

# Nodes mit hohem CPU-Overcommitment
sum by (node) (kube_pod_container_resource_requests{resource="cpu"})
/
sum by (node) (kube_node_status_allocatable{resource="cpu"}) * 100

Loesung

  • Throttling reduzieren: CPU-Limits erhoehen oder ganz entfernen. Viele Teams setzen mittlerweile nur noch CPU-Requests und verzichten auf CPU-Limits, weil CPU im Gegensatz zu Memory eine kompressible Ressource ist.
  • Requests korrekt dimensionieren: P95-Wert der tatsaechlichen Nutzung aus Prometheus nehmen. Mehr dazu unter Kubernetes Capacity Planning.
  • HPA aktivieren: Bei variabler Last ist horizontale Skalierung oft besser als groessere Limits.
  • Application-Code optimieren: Manchmal ist nicht Kubernetes das Problem, sondern ineffizienter Code (N+1 Queries, fehlende Caches, synchrone Calls).

2. Memory-Probleme

Das Problem

Memory-Probleme in Kubernetes aeussern sich als OOMKilled-Pods, als langsame Anwendungen wegen Swap-Nutzung (falls aktiviert) oder als Evictions auf ueberlasteten Nodes.

Im Gegensatz zu CPU ist Memory eine nicht-kompressible Ressource. Wenn ein Container sein Memory-Limit ueberschreitet, wird er sofort vom OOM-Killer beendet. Es gibt keine Drosselung.

Diagnose

# OOMKilled-Pods finden
kubectl get pods -A -o json | \
  jq -r '.items[] |
  select(.status.containerStatuses[]?.lastState.terminated.reason == "OOMKilled") |
  "\(.metadata.namespace)/\(.metadata.name)"'

# Memory-Nutzung der Nodes pruefen
kubectl top nodes

# Memory-Details eines spezifischen Pods
kubectl top pod my-app-pod -n production --containers

Prometheus-Queries:

# Working Set Memory vs. Limit (nah an 100% = OOMKill-Gefahr)
sum by (pod, container) (
  container_memory_working_set_bytes
) /
sum by (pod, container) (
  kube_pod_container_resource_limits{resource="memory"}
) * 100

# OOMKill-Events pro Namespace in der letzten Stunde
sum by (namespace) (
  increase(kube_pod_container_status_restarts_total[1h])
)

# Node Memory Pressure
kube_node_status_condition{condition="MemoryPressure", status="true"}

Loesung

  • Memory-Limits basierend auf Monitoring setzen: Nicht raten, sondern container_memory_working_set_bytes ueber mindestens eine Woche beobachten. Limit auf P99 plus 20% Buffer.
  • Memory Leaks identifizieren: Stetig steigendes container_memory_working_set_bytes ueber Stunden oder Tage deutet auf ein Leak hin. Das ist kein Kubernetes-Problem, sondern ein Application-Problem.
  • Vertical Pod Autoscaler (VPA): Passt Requests und Limits automatisch an die tatsaechliche Nutzung an. Empfehlenswert im Recommendation-Modus als Orientierung.
  • Java-Anwendungen: JVM-Heap-Size explizit setzen und auf Container-Memory abstimmen. -XX:MaxRAMPercentage=75 statt fester -Xmx-Werte.

3. Netzwerk-Latenz

Das Problem

Netzwerk-Performance in Kubernetes wird durch mehrere Schichten beeinflusst: das CNI-Plugin, DNS-Aufloesung, Service-Routing (iptables/IPVS) und optional ein Service Mesh.

Typische Symptome: Erhoehte Antwortzeiten zwischen Services, DNS-Timeouts, Connection Timeouts bei Service-zu-Service-Kommunikation.

Diagnose

# DNS-Aufloesung testen (sollte unter 5ms sein)
kubectl exec -n production my-app-pod -- \
  nslookup kubernetes.default.svc.cluster.local

# Netzwerk-Latenz zwischen Pods messen
kubectl exec -n production pod-a -- \
  curl -w "DNS: %{time_namelookup}s, Connect: %{time_connect}s, Total: %{time_total}s\n" \
  -s -o /dev/null http://service-b:8080/health

# CoreDNS-Pods pruefen
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

Prometheus-Queries:

# CoreDNS Latenz (P99)
histogram_quantile(0.99,
  sum by (le) (
    rate(coredns_dns_request_duration_seconds_bucket[5m])
  )
)

# CoreDNS Error-Rate
sum(rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m]))
/
sum(rate(coredns_dns_responses_total[5m])) * 100

# Service-Mesh-Latenz (Istio-Beispiel)
histogram_quantile(0.99,
  sum by (destination_service, le) (
    rate(istio_request_duration_milliseconds_bucket[5m])
  )
)

Loesung

  • DNS-Caching: NodeLocal DNSCache deployen, um DNS-Lookups zu beschleunigen und CoreDNS zu entlasten.
  • ndots-Einstellung optimieren: Der Standard ndots: 5 fuehrt dazu, dass jede DNS-Abfrage bis zu fuenf Suchdomains durchprobiert, bevor die tatsaechliche Domain aufgeloest wird. Fuer externe Domainnamen ein . am Ende anhaengen oder ndots: 2 setzen.
  • CNI-Plugin evaluieren: Cilium mit eBPF ist messbar schneller als iptables-basierte Loesungen bei hoher Pod-Anzahl.
  • Service Mesh Overhead: Wenn Istio oder Linkerd eingesetzt wird, den Sidecar-Overhead messen. Fuer latenz-kritische Pfade Direct-Pod-to-Pod-Kommunikation in Betracht ziehen.

Vertiefende Informationen zu DNS-Problemen finden Sie in unserem CoreDNS Troubleshooting Guide.

4. Storage I/O

Das Problem

Persistent Volumes (PVs) in Kubernetes unterliegen den IOPS- und Throughput-Limits des zugrunde liegenden Storage-Systems. Cloud-basierte Block-Storage-Volumes (EBS, Azure Disk, Persistent Disk) haben oft niedrigere Default-IOPS als erwartet.

Typische Symptome: Langsame Datenbankabfragen, hohe Write-Latenz, Timeouts bei dateiintensiven Operationen.

Diagnose

# I/O-Statistiken eines Pods anzeigen
kubectl exec -n production db-pod -- iostat -x 1 5

# PV-Kapazitaet und Auslastung pruefen
kubectl get pv -o custom-columns=\
NAME:.metadata.name,\
CAPACITY:.spec.capacity.storage,\
STORAGECLASS:.spec.storageClassName,\
STATUS:.status.phase

# StorageClass-Details (IOPS, Throughput)
kubectl get storageclass -o yaml

Prometheus-Queries:

# Disk I/O Latenz pro Pod (Write)
rate(container_fs_writes_total[5m])

# Disk Throughput pro Pod
sum by (pod) (
  rate(container_fs_writes_bytes_total[5m])
)

# PV-Auslastung (Prozent)
kubelet_volume_stats_used_bytes
/
kubelet_volume_stats_capacity_bytes * 100

Loesung

  • StorageClass mit hoeheren IOPS verwenden: In AWS z.B. gp3 statt gp2, mit explizit konfigurierten IOPS.
  • Local SSDs fuer latenz-kritische Workloads: Fuer Datenbanken wie PostgreSQL oder Elasticsearch bringen lokale NVMe-SSDs dramatische Verbesserungen.
  • ReadWriteMany vermeiden: NFS-basierte RWX-Volumes sind deutlich langsamer als Block-Storage. Wo moeglich, auf ReadWriteOnce umstellen.
  • I/O-Scheduling optimieren: fio-Benchmarks auf dem PV ausfuehren, um die tatsaechlichen IOPS zu ermitteln und mit den Anforderungen abzugleichen.

5. etcd-Performance

Das Problem

etcd ist die zentrale Datenbank von Kubernetes. Wenn etcd langsam wird, wird der gesamte Cluster langsam. Typische Ursachen: Zu viele Objects im Cluster, fehlende Compaction, langsame Disk-I/O oder Netzwerk-Latenz zwischen etcd-Membern.

Diagnose

# etcd-Metriken direkt abfragen
kubectl exec -n kube-system etcd-master-0 -- \
  etcdctl endpoint status --write-out=table \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# Anzahl Keys in etcd
kubectl exec -n kube-system etcd-master-0 -- \
  etcdctl get / --prefix --keys-only | wc -l

Prometheus-Queries:

# etcd Disk Sync Duration (sollte unter 10ms liegen)
histogram_quantile(0.99,
  sum by (le) (
    rate(etcd_disk_wal_fsync_duration_seconds_bucket[5m])
  )
)

# etcd Backend Commit Duration
histogram_quantile(0.99,
  sum by (le) (
    rate(etcd_disk_backend_commit_duration_seconds_bucket[5m])
  )
)

# Slow Requests (ueber 100ms)
rate(etcd_server_slow_apply_total[5m])

# etcd Database Size
etcd_mvcc_db_total_size_in_bytes

Loesung

  • Dedizierte SSDs fuer etcd: etcd ist extrem latenz-sensitiv. SATA-SSDs reichen oft nicht, NVMe ist empfohlen.
  • Compaction und Defragmentierung: Automatische Compaction aktivieren und regelmaessige Defragmentierung einplanen.
  • Anzahl Objects reduzieren: Abgelaufene Leases, alte ConfigMaps, verwaiste Endpoints bereinigen.
  • etcd von Workload-Nodes trennen: In Produktionsclustern sollte etcd auf dedizierten Nodes laufen, nicht zusammen mit dem API-Server.

6. API-Server-Bottlenecks

Das Problem

Der API-Server wird zum Bottleneck, wenn zu viele Clients gleichzeitig teure Operationen ausfuehren: List-Requests ohne Label-Selector auf grosse Resourcen, zu viele Watch-Connections, oder Controller, die in einer Retry-Schleife haengen.

Diagnose

# API-Server Audit Logs auf langsame Requests pruefen
# (falls Audit Logging aktiviert ist)
kubectl logs -n kube-system kube-apiserver-master-0 --tail=100

# Aktuelle API-Server-Metriken
kubectl get --raw /metrics | grep apiserver_request_duration

Prometheus-Queries:

# API-Request-Latenz P99 nach Verb und Resource
histogram_quantile(0.99,
  sum by (verb, resource, le) (
    rate(apiserver_request_duration_seconds_bucket[5m])
  )
)

# Anzahl aktiver Watch-Connections
apiserver_registered_watchers

# API-Server Request Rate
sum by (verb) (
  rate(apiserver_request_total[5m])
)

# Abgelehnte Requests (Rate Limiting)
sum by (client) (
  rate(apiserver_dropped_requests_total[5m])
)

Loesung

  • List-Requests optimieren: Label-Selectors und Field-Selectors verwenden, um die Ergebnismenge einzuschraenken. kubectl get pods -l app=frontend statt kubectl get pods.
  • Watch-Connections konsolidieren: Shared Informers in Custom Controllern verwenden statt individuelle Watches.
  • API Priority and Fairness konfigurieren: Kubernetes bietet FlowSchemas und PriorityLevelConfigurations, um wichtige API-Calls zu priorisieren.
  • Client-seitige Caching: Controller und Operatoren sollten Informer-Caches nutzen statt bei jedem Reconcile-Loop den API-Server abzufragen.

Weitere Troubleshooting-Tipps finden Sie in unserem Kubernetes Troubleshooting Guide.

Performance-Baseline-Checkliste

Bevor Sie Probleme diagnostizieren koennen, muessen Sie wissen, was normal ist. Diese Baseline-Checkliste hilft dabei:

MetrikGesunder WertWarnungKritisch
CPU-Throttling-RateUnter 5%5-25%Ueber 25%
Memory Working Set vs. LimitUnter 70%70-85%Ueber 85%
CoreDNS P99-LatenzUnter 5ms5-50msUeber 50ms
etcd WAL Sync P99Unter 10ms10-50msUeber 50ms
API-Server P99-LatenzUnter 200ms200ms-1sUeber 1s
Node CPU-Auslastung40-70%70-85%Ueber 85%
PV IOPS-AuslastungUnter 70%70-90%Ueber 90%

Richten Sie Alerting auf diese Metriken ein. Ein guter Startpunkt ist das Kubernetes Monitoring und Observability Setup.

Systematisches Vorgehen bei Performance-Problemen

Wenn ein Kubernetes Performance Problem gemeldet wird, gehen Sie in dieser Reihenfolge vor:

Schritt 1: Symptom eingrenzen. Ist das Problem global (alle Services betroffen) oder lokal (ein Service, ein Namespace)? Seit wann tritt es auf? Korreliert es mit einem Deployment oder einer Konfigurationsaenderung?

Schritt 2: Ressourcen-Layer identifizieren. Pruefen Sie CPU, Memory, Network und Storage in dieser Reihenfolge. Die meisten Probleme liegen bei CPU-Throttling oder Memory-Knappheit.

Schritt 3: Infrastructure vs. Application. Ist das Problem auf Kubernetes-Ebene (Node-Overcommitment, CNI, Storage) oder auf Application-Ebene (ineffiziente Queries, fehlende Caches, Memory Leaks)?

Schritt 4: Fix implementieren und validieren. Aenderungen einzeln vornehmen und die Auswirkung messen. Nicht mehrere Aenderungen gleichzeitig.

Schritt 5: Baseline aktualisieren. Nach dem Fix die Performance-Baseline neu definieren und Alerting anpassen.

Fazit

Kubernetes Performance Probleme sind loesbar, wenn man systematisch vorgeht. Die sechs Kategorien CPU, Memory, Network, Storage, etcd und API-Server decken die grosse Mehrheit aller Bottlenecks ab. Prometheus-basiertes Monitoring ist die Grundlage fuer jede Diagnose.

Der wichtigste Rat: Definieren Sie eine Performance Baseline, bevor das erste Problem auftritt. Ohne Baseline wissen Sie nicht, was normal ist, und koennen Abweichungen nicht zuverlaessig erkennen.

Verwandte Artikel


Sie haben Performance-Probleme in Ihrem Kubernetes-Cluster und kommen mit der Diagnose nicht weiter? Wir analysieren Ihre Infrastruktur und identifizieren die Bottlenecks systematisch. Kontaktieren Sie uns 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