- Authors

- Name
- Phillip Pham
- @ddppham
Kubernetes High CPU Troubleshooting: Performance-Probleme diagnostizieren und loesen
TL;DR
- CPU-Throttling ist die haeufigste Ursache fuer langsame Anwendungen in Kubernetes -- Pods werden gedrosselt, sobald sie ihr CPU-Limit erreichen.
kubectl top podszeigt die aktuelle CPU-Nutzung, aber nicht das Throttling. Dafuer brauchen Sie Prometheus-Metriken oder cgroup-Dateien.- CPU Requests bestimmen die Scheduling-Garantie, CPU Limits die harte Obergrenze. Falsch gesetzte Limits verursachen mehr Probleme als sie loesen.
- QoS-Klassen (Guaranteed, Burstable, BestEffort) beeinflussen, welche Pods bei Node-Ueberlastung zuerst verdraengt werden.
- Starten Sie mit grosszuegigen Limits, beobachten Sie die tatsaechliche Nutzung, und optimieren Sie dann -- nicht umgekehrt.
Wie CPU-Zuweisung in Kubernetes funktioniert
Bevor Sie CPU-Probleme debuggen koennen, muessen Sie verstehen, wie Kubernetes CPU verwaltet. CPU wird in "Millicores" gemessen: 1 CPU = 1000m. Ein Container mit cpu: 500m bekommt die Haelfte eines CPU-Kerns.
Es gibt zwei Einstellungen:
resources:
requests:
cpu: 250m # Garantierte CPU -- Scheduler reserviert das
limits:
cpu: 500m # Harte Obergrenze -- Container wird gedrosselt
Requests: Der Scheduler stellt sicher, dass der Node mindestens so viel CPU frei hat. Ihr Pod wird nur auf Nodes platziert, die genug freie Requests haben.
Limits: Die harte Obergrenze. Erreicht der Container sein Limit, wird er vom Linux-Kernel gedrosselt (Throttling). Er wird nicht gekillt (das passiert nur bei Memory), sondern einfach langsamer.
Symptom 1: Anwendung ist langsam (CPU Throttling)
Das Problem erkennen
Ihre Anwendung antwortet langsam, obwohl kubectl top niedrige CPU-Werte zeigt. Das ist das typische Zeichen fuer CPU-Throttling: Der Container wird regelmaessig gedrosselt, aber die durchschnittliche CPU-Nutzung sieht normal aus.
# Aktuelle CPU-Nutzung pruefen
kubectl top pods -n production --sort-by=cpu
# Ausgabe:
# NAME CPU(cores) MEMORY(bytes)
# api-server-7f8b9-x4k2l 245m 128Mi
# worker-5d4f8b-n9m3p 180m 256Mi
245m sieht harmlos aus, wenn das Limit bei 500m liegt. Aber: kubectl top zeigt den Durchschnitt der letzten Minute. Wenn Ihr Container in Bursts arbeitet (z.B. bei jedem Request kurz 500m braucht), wird er staendig gedrosselt, obwohl der Durchschnitt niedrig ist.
Throttling mit Prometheus erkennen
Die entscheidende Metrik heisst container_cpu_cfs_throttled_periods_total:
# Throttling-Rate pro Container (letzte 5 Minuten)
sum(rate(container_cpu_cfs_throttled_periods_total{namespace="production"}[5m])) by (pod, container)
/
sum(rate(container_cpu_cfs_periods_total{namespace="production"}[5m])) by (pod, container)
* 100
Das Ergebnis ist ein Prozentwert. Alles ueber 25% ist problematisch und bedeutet, dass Ihr Container in einem Viertel aller Scheduling-Perioden gedrosselt wird.
# Konkrete CPU-Nutzung vs. Limit
sum(rate(container_cpu_usage_seconds_total{namespace="production", pod=~"api-server.*"}[5m])) by (pod)
/
sum(kube_pod_container_resource_limits{namespace="production", pod=~"api-server.*", resource="cpu"}) by (pod)
* 100
Throttling ohne Prometheus pruefen
Wenn Sie kein Prometheus haben, koennen Sie die cgroup-Dateien direkt lesen:
# In den Container exec'en
kubectl exec -it api-server-7f8b9-x4k2l -n production -- /bin/sh
# cgroup v2: Throttling-Statistiken lesen
cat /sys/fs/cgroup/cpu.stat
# usage_usec 1234567890
# user_usec 1000000000
# system_usec 234567890
# nr_periods 50000
# nr_throttled 12500 <-- Anzahl gedrosselter Perioden
# throttled_usec 30000000 <-- Gesamte gedrosselte Zeit in Mikrosekunden
# cgroup v1 (aeltere Cluster):
cat /sys/fs/cgroup/cpu/cpu.stat
Wenn nr_throttled staendig steigt, wird Ihr Container aktiv gedrosselt.
Fix: CPU-Limits erhoehen oder entfernen
# Vorher: Zu enges Limit
resources:
requests:
cpu: 250m
limits:
cpu: 500m
# Nachher: Grosszuegigeres Limit
resources:
requests:
cpu: 250m
limits:
cpu: "1" # 1 voller CPU-Kern
Viele Teams entscheiden sich inzwischen dafuer, CPU-Limits ganz zu entfernen und nur Requests zu setzen. Der Grund: CPU-Throttling schadet mehr als es nuetzt. Ein Container ohne CPU-Limit kann bursten, wenn der Node freie Kapazitaet hat, und wird nur gedrosselt, wenn andere Container ihre Requests brauchen.
# Nur Requests, kein Limit
resources:
requests:
cpu: 250m
# Kein CPU-Limit -- Container kann bursten
Beachten Sie: Ohne CPU-Limits erhaelt der Pod die QoS-Klasse "Burstable" statt "Guaranteed". Mehr dazu im Abschnitt zu QoS-Klassen.
Symptom 2: Pods werden evicted (Node-Ueberlastung)
Das Problem erkennen
Wenn ein Node ueberlastet ist, beginnt Kubernetes Pods zu verdraengen (Eviction). Das sehen Sie in den Events:
# Node-Auslastung pruefen
kubectl top nodes
# Ausgabe:
# NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
# node-01 3800m 95% 12Gi 85%
# node-02 3200m 80% 10Gi 71%
# node-03 3900m 97% 14Gi 99%
# Events fuer Evictions pruefen
kubectl get events --field-selector reason=Evicted -A --sort-by='.lastTimestamp'
Ursache: Noisy Neighbors
In einem Shared Cluster koennen einzelne Pods andere verdraengen. Ein Pod mit requests: 100m und limits: 4000m (oder ohne Limit) kann theoretisch 4 CPU-Kerne beanspruchen. Wenn mehrere solche Pods auf dem gleichen Node landen, wird der Node ueberlastet.
# Die groessten CPU-Verbraucher finden
kubectl top pods -A --sort-by=cpu | head -20
# Resource Requests vs. tatsaechliche Nutzung vergleichen
kubectl get pods -n production -o custom-columns=\
NAME:.metadata.name,\
CPU_REQ:.spec.containers[0].resources.requests.cpu,\
CPU_LIM:.spec.containers[0].resources.limits.cpu
Fix: Resource Quotas und Limit Ranges
Schuetzen Sie Namespaces mit ResourceQuotas:
apiVersion: v1
kind: ResourceQuota
metadata:
name: cpu-quota
namespace: production
spec:
hard:
requests.cpu: "8" # Maximal 8 CPU-Kerne Requests im Namespace
limits.cpu: "16" # Maximal 16 CPU-Kerne Limits
LimitRange setzt Defaults fuer Container, die keine Resources angeben:
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-defaults
namespace: production
spec:
limits:
- type: Container
default:
cpu: 500m
defaultRequest:
cpu: 100m
max:
cpu: "2"
min:
cpu: 50m
Symptom 3: Pods im Pending-Status (nicht schedulebar)
Wenn Ihre CPU Requests hoeher sind als die verfuegbare Kapazitaet, bleiben Pods im Pending-Status:
kubectl get pods -n production | grep Pending
kubectl describe pod pending-pod-xyz -n production
# Events:
# Warning FailedScheduling Insufficient cpu
Fix: Cluster skalieren oder Requests anpassen
# Verfuegbare CPU pro Node anzeigen
kubectl describe nodes | grep -A 5 "Allocated resources"
# Oder detaillierter:
kubectl get nodes -o custom-columns=\
NAME:.metadata.name,\
CPU_CAP:.status.capacity.cpu,\
CPU_ALLOC:.status.allocatable.cpu
Wenn alle Nodes voll sind, brauchen Sie entweder mehr Nodes (Cluster Autoscaler) oder muessen die Requests reduzieren. Lesen Sie dazu den Artikel zu Kubernetes Autoscaling.
QoS-Klassen verstehen
Kubernetes ordnet jeden Pod einer Quality-of-Service-Klasse zu. Diese bestimmt die Eviction-Reihenfolge bei Node-Ueberlastung:
| QoS-Klasse | Bedingung | Eviction-Prioritaet |
|---|---|---|
| Guaranteed | Requests = Limits (fuer alle Container) | Zuletzt evicted |
| Burstable | Requests gesetzt, aber ungleich Limits | Mittlere Prioritaet |
| BestEffort | Keine Requests/Limits gesetzt | Zuerst evicted |
# Guaranteed QoS
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: 500m # Gleich wie Request
memory: 256Mi # Gleich wie Request
# Burstable QoS
resources:
requests:
cpu: 250m
memory: 128Mi
limits:
cpu: 500m # Hoeher als Request
memory: 256Mi
# BestEffort QoS (nicht empfohlen fuer Production)
# Keine resources-Sektion
# QoS-Klasse eines Pods anzeigen
kubectl get pod my-pod -n production -o jsonpath='{.status.qosClass}'
Fuer produktionskritische Workloads empfehlen wir mindestens Burstable, besser Guaranteed. BestEffort-Pods werden bei der ersten Node-Ueberlastung verdraengt.
cgroup v2: Was sich aendert
Neuere Kubernetes-Versionen (ab 1.25+) nutzen cgroup v2 statt v1. Das aendert einige Details beim CPU-Management:
# Pruefen, ob cgroup v2 aktiv ist
kubectl get nodes -o jsonpath='{.items[0].status.nodeInfo.containerRuntimeVersion}'
# Auf dem Node selbst:
stat -f /sys/fs/cgroup/
# cgroup v2: "cgroup2fs"
# cgroup v1: "tmpfs"
Wesentliche Aenderungen mit cgroup v2:
- CPU-Burst: Container koennen kurzfristig ueber ihr Limit bursten, wenn der Node Kapazitaet hat (Feature Gate
CPUManagerPolicyBeta). - PSI (Pressure Stall Information): Genauere Metriken fuer CPU-Druck auf dem Node.
- Unified Hierarchy: CPU und Memory werden in einer gemeinsamen Hierarchie verwaltet.
Right-Sizing: Die richtige Groesse finden
Schritt 1: Aktuelle Nutzung beobachten
Lassen Sie Ihre Workloads mindestens eine Woche mit grosszuegigen Limits laufen und beobachten Sie die tatsaechliche Nutzung:
# 95. Perzentil der CPU-Nutzung (letzte 7 Tage)
quantile_over_time(0.95,
rate(container_cpu_usage_seconds_total{namespace="production", pod=~"api-server.*"}[5m])[7d:]
) * 1000
Schritt 2: Requests auf das 95. Perzentil setzen
# Empfehlung: Request = P95, Limit = P99 oder 2x Request
# P95 CPU-Nutzung: 180m
# P99 CPU-Nutzung: 350m
# Ergebnis:
# requests.cpu: 200m (P95 aufgerundet)
# limits.cpu: 400m (P99 aufgerundet oder 2x Request)
Schritt 3: VPA nutzen (optional)
Der Vertical Pod Autoscaler kann Empfehlungen automatisch berechnen:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Off" # Nur Empfehlungen, kein automatisches Update
resourcePolicy:
containerPolicies:
- containerName: api
minAllowed:
cpu: 100m
maxAllowed:
cpu: "2"
# VPA-Empfehlungen abrufen
kubectl get vpa api-server-vpa -n production -o yaml | grep -A 20 recommendation
Fuer eine detailliertere Anleitung zum Zusammenspiel von VPA, HPA und Cluster Autoscaler lesen Sie den Autoscaling-Guide.
App-Level CPU-Probleme finden
Nicht immer liegt das Problem bei Kubernetes. Manchmal ist die Anwendung selbst ineffizient:
Java: GC-Overhead
# Heap-Dump erstellen
kubectl exec api-server-7f8b9-x4k2l -n production -- jcmd 1 GC.heap_info
# Thread-Dump fuer CPU-Hotspots
kubectl exec api-server-7f8b9-x4k2l -n production -- jcmd 1 Thread.print
# JVM-Flags fuer Container setzen
env:
- name: JAVA_OPTS
value: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:ActiveProcessorCount=2"
Node.js: Event Loop Blocking
# CPU-Profiling mit Node.js
kubectl exec api-server-7f8b9-x4k2l -n production -- node --prof /app/index.js
Allgemein: Process-Profiling im Container
# Top-Prozesse im Container
kubectl exec api-server-7f8b9-x4k2l -n production -- top -bn1 -o %CPU | head -15
# Oder mit ps
kubectl exec api-server-7f8b9-x4k2l -n production -- ps aux --sort=-%cpu | head -10
Monitoring-Setup fuer CPU-Probleme
Richten Sie diese Prometheus-Alerts ein, um CPU-Probleme fruehzeitig zu erkennen:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cpu-alerts
namespace: monitoring
spec:
groups:
- name: cpu-alerts
rules:
- alert: HighCPUThrottling
expr: |
sum(rate(container_cpu_cfs_throttled_periods_total{namespace="production"}[5m])) by (pod, container)
/
sum(rate(container_cpu_cfs_periods_total{namespace="production"}[5m])) by (pod, container)
> 0.25
for: 15m
labels:
severity: warning
annotations:
summary: "Container {{ $labels.pod }}/{{ $labels.container }} wird zu mehr als 25% gedrosselt"
- alert: HighNodeCPU
expr: |
instance:node_cpu_utilisation:rate5m > 0.90
for: 30m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.instance }} hat ueber 90% CPU-Auslastung"
- alert: CPURequestsOvercommit
expr: |
sum(kube_pod_container_resource_requests{resource="cpu"}) by (node)
/
sum(kube_node_status_allocatable{resource="cpu"}) by (node)
> 1.0
for: 5m
labels:
severity: warning
annotations:
summary: "Node {{ $labels.node }}: CPU Requests uebersteigen die verfuegbare Kapazitaet"
Fuer ein umfassendes Monitoring-Setup lesen Sie den Artikel zu Kubernetes Monitoring und Observability.
Checkliste: Kubernetes High CPU Troubleshooting
# 1. Ueberblick verschaffen
kubectl top nodes
kubectl top pods -A --sort-by=cpu | head -20
# 2. Betroffenen Pod identifizieren
kubectl top pods -n production --sort-by=cpu
# 3. Resource-Konfiguration pruefen
kubectl get pod my-pod -n production -o jsonpath='{.spec.containers[0].resources}'
# 4. QoS-Klasse pruefen
kubectl get pod my-pod -n production -o jsonpath='{.status.qosClass}'
# 5. Node-Kapazitaet pruefen
kubectl describe node my-node | grep -A 10 "Allocated resources"
# 6. Events pruefen (Evictions, FailedScheduling)
kubectl get events -n production --sort-by='.lastTimestamp' | grep -i cpu
# 7. Throttling pruefen (im Container)
kubectl exec my-pod -n production -- cat /sys/fs/cgroup/cpu.stat
Verwandte Artikel
- Kubernetes Autoscaling: HPA, VPA und Cluster Autoscaler -- Resources automatisch anpassen
- Kubernetes Performance Optimization -- Umfassende Performance-Optimierung
- Kubernetes OOMKilled Troubleshooting -- Memory-Probleme diagnostizieren
- Kubernetes Monitoring und Observability -- Metriken und Alerting einrichten
- Kubernetes Kosten optimieren -- Right-Sizing fuer Kosteneffizienz
CPU-Probleme kosten Performance und Geld. Wenn Sie Unterstuetzung beim Right-Sizing Ihrer Kubernetes-Cluster brauchen oder wiederkehrende Performance-Probleme haben, helfen wir Ihnen gerne. Kontaktieren Sie uns fuer eine kostenlose Erstberatung.
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
Kubernetes Performance Probleme systematisch lösen
Performance-Bottlenecks in Kubernetes diagnostizieren und lösen: CPU-Throttling, Memory, Netzwerk, Storage, etcd und API-Server-Probleme beheben.
Kubernetes Incident Management: Runbooks erstellen
Effektive Runbooks für Kubernetes-Incidents erstellen: Vorlagen für CrashLoopBackOff, OOMKilled und Node-Ausfälle mit konkreten Debugging-Befehlen.
Prometheus Recording Rules für Kubernetes optimieren
Prometheus Recording Rules beschleunigen PromQL-Abfragen in Kubernetes. Konfiguration, Naming Conventions und praktische Beispiele.
Java Kubernetes OOMKilled: JVM Heap richtig setzen
Java-Pods werden auf Kubernetes mit OOMKilled (Exit Code 137) beendet. So setzt ihr JVM Heap, MaxRAMPercentage und Container-Limits korrekt für stabile Java-Workloads.
Kubernetes GPU-Auslastung optimieren: MIG und Time-Slicing
GPU-Auslastung in Kubernetes von 30% auf über 75% steigern mit MIG und Time-Slicing. NVIDIA GPU Operator Setup und DCGM Monitoring Anleitung.