PerfectScale

Feingranulares Pod-Scaling in Kubernetes: jenseits von HPA und VPA

Diese Seite ist auch in English, Español, Français, Italiano, 日本語 und Português verfügbar.

Jun 25, 20264 min read
Joshua Fox

About Joshua Fox

Joshua Fox has been a software architect in innovative technology companies for 20 years. Now, he advises tech startups and growth companies on architecture and cost optimization for Google Cloud Platform and Amazon Web Services; also publishing articles, and speaking to cloud engineers.

He has a PhD from Harvard University and a BA in math from Brandeis.

My personal page

1. Einleitung

Kubernetes-Autoscaling soll Robustheit gewährleisten und gleichzeitig Kosten senken. Autoscaler passen entweder die Anzahl der Nodes über den Cluster Autoscaler an oder Anzahl und Größe der Pods über einen Vertical bzw. Horizontal Pod Autoscaler.

Doch das hat seine Grenzen: Standard-Autoscaler stützen sich auf grobe Metriken wie die durchschnittliche CPU-Auslastung. Das ist zu ungenau – entweder bleiben überflüssige Ressourcen reserviert, oder das System wird unter Last instabil. In diesem Artikel konzentriere ich mich auf Pod-Autoscaler und zeige, wie feingranulare Telemetrie aus cAdvisor, Kube-State-Metrics und eBPF präzisere, workload-orientierte Scaling-Entscheidungen ermöglicht. Diese Entscheidungen werden anschließend über die Kubernetes-API InPlacePodVerticalScaling auf laufende Pods angewendet.

2. Die Schwächen bisheriger Autoscaling-Ansätze

Standard-Autoscaling hat drei zentrale Schwachstellen:

  • Unzureichende Signalgranularität: Einfache CPU-Mittelwerte bilden das vollständige Performance-Profil komplexer Anwendungen nicht ab und führen zu verzögerten oder fehlerhaften Scaling-Entscheidungen.
  • HPA/VPA-Konflikt: HPA skaliert auf Basis der angeforderten Ressourcen, während VPA genau diese Anforderungen verändert. Arbeiten beide unabhängig voneinander, können sie widersprüchliche Anweisungen erteilen und Oszillationen verursachen.
  • Resizing per Eviction: Der klassische VPA benötigt Pod-Evictions, um Ressourcenänderungen anzuwenden – ein Problem für stateful oder cache-sensitive workloads, die keine Cold-Start-Latenz vertragen.

3. Die mehrschichtige Telemetrie-Pipeline

Wir starten mit Node-Metriken als Basis und ergänzen vier weitere Telemetrie-Schichten für feinere Signale:

  • Layer 0 (Basis): CPU- und Speicherauslastung auf Node-Ebene aus dem Kubernetes Metrics Server – die grobgranulare Grundlage.
  • Layer 1: Container-Telemetrie über cAdvisor. Diese Schicht erfasst Micro-Burst-Latenzen – kurze CPU-Spitzen, die Durchschnittswerte verzerren, ohne anhaltende Last widerzuspiegeln – sowie Memory Working Sets und verhindert so Scaling-Entscheidungen auf Basis harmlosen Wachstums im Kernel Page Cache.
  • Layer 2: Kube-State-Metrics (KSM) liefert den Kontext zum Workload-Zustand, etwa Pending Replicas und HPA-Sättigungssignale. So kann der Controller vor weiteren Aktionen prüfen, ob Skalierung überhaupt nötig oder bereits im Gange ist.
  • Layer 3: eBPF greift tief liegende Anwendungssignale direkt im Kernel ab, etwa JVM-Heap-Allokationsraten und Druck durch Garbage Collection (GC). So lassen sich Ressourcen präventiv anpassen, bevor OOM-Events auftreten. Eine bpftrace-Probe mit HotSpot-USDT-Markern wie usdt:/path/to/libjvm.so:hotspot:mem__pool__gc__begin erkennt etwa GC-Frequenzspitzen schon Sekunden, bevor sie auf die Latenz durchschlagen – ein wertvoller Vorsprung für den Controller.
  • Layer 4: GPUs sind heute eine kritische Ressource für AI/ML-Kosten und -Performance. GPU-spezifische Metriken über NVIDIA DCGM ermöglichen Scaling auf Basis von GPU-Engpässen, etwa SM-Auslastung oder Sättigung der Speicherbandbreite.

4. Aufbau eines einheitlichen Scaling-Controllers

Ein einheitlicher Controller bringt diese Signale als State Machine zusammen: Er führt explizite Zustände (stable, scaling-horizontal, scaling-vertical, cooldown) und wechselt nur unter klar definierten Bedingungen zwischen ihnen. Das unterscheidet ihn von bisherigen Scalern, die meist als Regel-Engines arbeiten, jedes Signal isoliert auswerten und so widersprüchliche Anweisungen produzieren können.

Der Controller arbeitet in drei Phasen:

Phase 1: Signal-Aggregation

Jede Telemetrie-Schicht liefert pro Workload einen gewichteten Beitrag zu einer einzigen Pressure-Metrik. Signale aus der Application Layer wie Layer 3 (eBPF/GC-Druck) erhalten ein höheres Gewicht.

Phase 2: Axis Arbitration

Der Controller vermeidet Konflikte, indem er erkennt, dass horizontales Scaling bereits läuft; eine laufende Skalierung zeigt sich an AbleToScale=True zusammen mit einer Differenz ungleich null zwischen currentReplicas und desiredReplicas.

Ist ein Workload umgekehrt als Singleton oder stateful gekennzeichnet (per Annotation oder StatefulSet-Ownership), routet der Controller ausschließlich auf vertikales Scaling – niemals horizontal.

Phase 3: Reconciliation und Cooldown

Nach jeder Scaling-Aktion verhindert eine Cooldown-Phase Thrashing. Der Controller bewertet erst dann neu, wenn er mindestens zwei aufeinanderfolgende Metrik-Fenster oberhalb oder unterhalb des Schwellwerts beobachtet hat – so lösen kurzzeitige Spitzen keine wiederholten Skalierungen aus.

5. In-Place Pod Vertical Scaling

Die API InPlacePodVerticalScaling, die im Dezember 2025 mit Kubernetes v1.35 General Availability erreicht hat, erlaubt Ressourcenänderungen ohne Pod-Eviction. Der Controller patcht die Pod-Spezifikation, und das Kubelet aktualisiert die cgroups des Containers dynamisch – ohne Verbindungsabbruch, ohne Prozess-Neustart.

Ein konkretes Beispiel: Erkennt die eBPF-Schicht anhaltenden Java-GC-Druck und damit, dass innerhalb der nächsten 30 Sekunden eine Heap-Erschöpfung droht, sendet der Controller ein PATCH /api/v1/namespaces/{ns}/pods/{name} mit aktualisierten Werten für resources.requests.memory und resources.limits.memory. Das Kubelet wendet die Änderung an, indem es das cgroup-Memory-Limit des Containers in place anpasst.

Das ist besonders wertvoll für Services mit langen Cold-Start-Zeiten – etwa JVM-Anwendungen, Datenbank-Sidecars und In-Memory-Caches –, bei denen Eviction-basiertes Resizing Latenzspitzen von mehreren Dutzend Sekunden verursachen würde.

6. Fazit

Standard-Konfigurationen von HPA und VPA scheitern oft daran, hohe Verfügbarkeit und Kosteneffizienz gleichzeitig zu liefern. Wer feingranulare Observability aus eBPF und cAdvisor mit koordiniertem horizontalen und vertikalen Scaling über eine einheitliche State Machine kombiniert und Änderungen per In-Place-Pod-Resizing anwendet, schafft selbstheilende, passgenau dimensionierte Cluster bei minimalen Störungen.

PerfectScale ist eine Implementierung, die von den hier beschriebenen Architekturmustern inspiriert ist. Ich arbeite im Forward Deployed Engineering bei DoiT und berate Kunden zu AWS und Google Cloud Platform. Bei Fragen können Sie sich jederzeit an mich wenden.