Zurück zum Blog
DevOps
Experten-LevelFürPlatform EngineersSREKubernetes Operators
11 min

Kubernetes Autoscaling: Drei Controller, die nicht miteinander reden

HPA, VPA und der Node-Autoscaler lesen alle die Resource Requests, keiner koordiniert sich mit den anderen, und jeder scheitert stillschweigend, wenn die Requests falsch sind. Die Mechanik, die entscheidet, ob Ihr Cluster skaliert oder nur flattert.

kubernetes-autoscalinghpa-kuberneteshorizontal-pod-autoscalervertical-pod-autoscalercluster-autoscalerkarpenterin-place-pod-resize
Titelbild: Kubernetes Autoscaling: Drei Controller, die nicht miteinander reden
Inhalt

Die meisten Teams installieren alle drei Kubernetes-Autoscaler und nehmen an, daraus ergebe sich ein System. Tut es nicht. HPA, VPA und der Node-Autoscaler sind drei unabhängige Controller ohne gemeinsamen Zustand und ohne Abstimmungsprotokoll.

Was sie teilen, ist ein Input: Resource Requests. Jeder von ihnen liest Requests, um zu entscheiden. Keiner liest, was Ihre Anwendung tatsächlich verbraucht — außer indirekt. Sind die Requests falsch, degradieren alle drei gleichzeitig. Leise, ohne ein einziges Fehler-Event.

Dies ist eine Tour durch die Mechanik, die wirklich darüber entscheidet, ob Ihr Cluster skaliert oder nur flattert.

Die drei Ebenen des Kubernetes-Autoscalings — HPA ändert die Replica-Anzahl, VPA die Pod-Größe, der Node-Autoscaler die Maschinenanzahl — alle lesen dieselben Resource Requests, ohne sich abzustimmen
Drei Controller, drei Objekte, ein gemeinsamer Input und null Koordination.

Was jede Ebene tatsächlich ändert

Was jede Ebene tatsächlich ändert
Ändert Lebt in Liest
HPA Replica-Anzahl Kubernetes-Kern-API Metrics API, Requests
VPA CPU-/Memory-Requests Add-on-CRD Metrics API, Historie, OOM-Events
Node-Autoscaler Anzahl Maschinen Add-on + Cloud-API Ausstehende Pods, Requests

Nur HPA gehört zum Lieferumfang von Kubernetes. VPA ist eine separate Installation mit drei eigenen Komponenten. Node-Autoscaling braucht eine Cloud-Anbieter-Integration. Diese Asymmetrie zählt bei der Fehlersuche: Zwei der drei haben Sie selbst hinzugefügt.

HPA: Die Formel ist einfacher als gedacht

Der Controller läuft in einer Schleife — standardmäßig alle 15 Sekunden — und rechnet:

desiredReplicas = ceil[ currentReplicas × ( currentMetricValue / desiredMetricValue ) ]

Das ist die ganze Idee. Aktueller Wert doppelt so hoch wie das Ziel? Doppelte Replica-Zahl. Halb so hoch? Halbieren.

Drei Eigenschaften dieser Formel verursachen die meisten Überraschungen im Betrieb.

Auslastung wird gegen Requests gemessen, nicht gegen Limits. Mit averageUtilization: 60 sagen Sie: „Halte den Durchschnittsverbrauch bei 60% dessen, was die Pods angefordert haben.“ Der Request ist der Nenner. Deshalb sind Requests kein bloßer Scheduling-Hinweis — sie sind die Kalibrierung Ihres Autoscalers.

Kleine Änderungen werden übersprungen. Liegt das Verhältnis nahe genug bei 1,0, passiert nichts. Die Standardtoleranz beträgt clusterweit 10%. Ein Workload bei 105% seines Ziels skaliert nicht — und kein Event erklärt die Untätigkeit. Kubernetes 1.35 hat ein tolerance-Feld pro HPA als Beta ergänzt, sodass Sie es für einen einzelnen Workload verschärfen können:

behavior:
scaleUp:
tolerance: 0.05

Vorher ging das nur über das Flag --horizontal-pod-autoscaler-tolerance am Controller Manager — mit Wirkung auf jeden Workload im Cluster.

Bei mehreren Metriken gewinnt das Maximum. Geben Sie CPU, Memory und eine eigene Queue-Metrik an, berechnet HPA aus jeder eine gewünschte Replica-Zahl und nimmt die größte. Ein ODER, keine Mischung. Eine zusätzliche Metrik kann Ihren Workload nur vergrößern.

Der stille Fehler: kein Request, kein Autoscaling

Das ist die häufigste Ursache für ein totes HPA und verdient einen eigenen Abschnitt — wegen der Art, wie es scheitert.

Fehlt einem Container im Pod der relevante Resource Request, ist die CPU-Auslastung für diesen Pod undefiniert, und der Autoscaler unternimmt für diese Metrik nichts.

Kein Fehler. Kein Warn-Event. kubectl get hpa zeigt das Objekt an. In der Spalte TARGETS steht <unknown> — leicht zu lesen als „sammelt noch Metriken“ statt als „dauerhaft kaputt“.

Die Behebung ist trivial, sobald man es weiß. Das Herausfinden ist der teure Teil:

Terminal window
kubectl get hpa -A -o custom-columns=\
NS:.metadata.namespace,NAME:.metadata.name,TARGETS:.status.currentMetrics

Was nach einigen Minuten keine aktuellen Metriken zeigt, wärmt sich nicht auf. Es ist falsch konfiguriert.

Der zweite stille Fehler: Ihr Sidecar verwässert das Signal

HPA summiert den Ressourcenverbrauch über alle Container im Pod. Die Dokumentation weist ausdrücklich darauf hin, dass dies den Verbrauch einzelner Container nicht abbildet — ein Container kann heißlaufen, während der Pod-Durchschnitt entspannt aussieht und HPA nie hochskaliert.

Service Meshes und Logging-Sidecars machen das zum Regelfall. Ein Anwendungscontainer bei 95% seines Requests, gepaart mit einem untätigen Sidecar bei 5%, ergibt einen unauffälligen Durchschnitt.

Seit Kubernetes 1.30 gibt es dafür eine stabile Lösung — auf einen benannten Container skalieren statt auf den Pod:

metrics:
- type: ContainerResource
containerResource:
name: cpu
container: application
target:
type: Utilization
averageUtilization: 60

Eine Feinheit: Benennen Sie diesen Container um, müssen Sie das HPA vor dem Rollout auf beide Namen einstellen — sonst wird die Empfehlung mitten im Rollout blind.

Schnell hoch, langsam runter — mit Absicht

Das Standardverhalten ist bewusst asymmetrisch, und Missverständnisse darüber produzieren viele falsche Fehlermeldungen.

Schnell hoch, langsam runter — mit Absicht
Stabilisierungsfenster Ratenbegrenzung
Hochskalieren 0 Sekunden 100% der Replicas oder 4 Pods je 15s, je nachdem was größer ist
Herunterskalieren 300 Sekunden 100% je 15s, nach dem Fenster

Hochskalieren erfolgt sofort. Beim Herunterskalieren betrachtet der Algorithmus alle Empfehlungen der letzten fünf Minuten und nimmt die höchste — ein gleitendes Maximum, das verhindert, dass Pods entfernt werden, die kurz darauf wieder gebraucht würden.

Wenn also jemand meldet, „das HPA skaliert nicht herunter“, arbeitet es meist exakt wie entworfen und die Person schaut auf ein Fünf-Minuten-Fenster. Wenn Sie es wirklich schneller brauchen:

behavior:
scaleDown:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 10
periodSeconds: 60

Auch im Deployment lauert eine Falle. Behalten Sie spec.replicas in einem Manifest, das ein HPA verwaltet, setzt jedes kubectl apply die Zahl auf den Wert aus der Datei zurück — und das HPA zieht sie wieder hin. Genau dieser Kampf sieht auf dem Dashboard wie Flattern aus. Entfernen Sie das Feld, sobald das HPA den Workload besitzt.

VPA: anderes Problem, andere Fehlerart

Der VerticalPodAutoscaler beantwortet eine Frage, die HPA nicht kann: Wie groß sollte jeder Pod sein? Er besteht aus drei Komponenten — einem Recommender, der Verbrauchshistorie, Spitzen und OOM-Events auswertet, einem Updater, der Änderungen anwendet, und einem Admission-Controller-Webhook, der die empfohlenen Requests in neue Pods injiziert.

Der Update-Modus ist die eigentliche Entscheidung:

VPA: anderes Problem, andere Fehlerart
Modus Verhalten
Off Nur empfehlen. Nichts wird angewendet.
Initial Requests bei Pod-Erstellung setzen, danach nie wieder.
Recreate Pods evakuieren, wenn Requests von der Empfehlung abweichen.
InPlaceOrRecreate Wenn möglich in-place ändern, sonst evakuieren.
InPlace In-place ändern, nie evakuieren — stattdessen aufschieben und erneut versuchen.
Auto Seit VPA 1.4.0 veraltet. Alias für Recreate.

Beginnen Sie mit Off. Das ist für sich genommen wertvoll: Der Recommender schreibt Zielwert, untere und obere Schranke nach .status.recommendation — ein kostenloses Rightsizing-Audit ohne Produktionsrisiko.

Die große Änderung 2026 — und ein Widerspruch in der Doku

Jahrelang lautete der ehrliche Einwand gegen VPA, vertikales Skalieren bedeute, den Pod zu töten. In-Place Pod Resize ist seit Kubernetes 1.35 stable und standardmäßig aktiv. CPU und Memory eines laufenden Containers lassen sich jetzt ändern:

Terminal window
kubectl patch pod my-pod --subresource resize --patch \
'{"spec":{"containers":[{"name":"app",
"resources":{"requests":{"cpu":"800m"},"limits":{"cpu":"800m"}}}]}}'

Eine Policy je Ressource steuert, ob ein Neustart nötig ist — bei Memory meist ja, weil die wenigsten Runtimes ihren Heap spontan verkleinern:

resizePolicy:
- resourceName: cpu
restartPolicy: NotRequired # Standard
- resourceName: memory
restartPolicy: RestartContainer

Hier muss ich etwas kennzeichnen. Die Kubernetes-Dokumentation widerspricht sich, ob VPA das nutzen kann. Die Autoscaling-Übersichtsseite hält fest, VPA unterstütze In-Place-Resizing ab 1.36 nicht. Die VPA-Seite dokumentiert InPlaceOrRecreate und InPlace vollständig und vermerkt, InPlace sei Alpha in VPA 1.7.0 und erfordere Kubernetes 1.33+ mit dem Gate InPlacePodVerticalScaling sowie InPlace-Gates auf Updater und Admission Controller.

Die VPA-Seite wurde später überarbeitet, die Übersicht ist also vermutlich veraltet. Auflösen ließ sich das aus der Doku allein nicht — prüfen Sie Ihre installierte VPA-Version und testen Sie außerhalb der Produktion, bevor Sie jemandem neustartfreies Rightsizing versprechen.

Unabhängig vom Modus wichtig: Ein Resize kann die QoS-Klasse des Pods nicht ändern. Ein Guaranteed-Pod muss danach weiter Requests gleich Limits haben. Ein Burstable-Pod kann nicht zu Guaranteed werden. Windows-Pods werden gar nicht unterstützt. Passt ein Resize nicht auf den Node, erhält der Pod die Condition PodResizePending mit reason: Infeasible — darauf sollten Sie alarmieren, denn sonst sagt es Ihnen nichts.

Warum HPA und VPA sich in die Quere kommen

Setzen Sie beide auf CPU an und folgen Sie der Arithmetik.

HPA berechnet die Auslastung als Verbrauch / Request. Die gesamte Aufgabe von VPA ist es, Request zu ändern.

  1. VPA beobachtet niedrigen Verbrauch und senkt den Request.
  2. Dieselbe unveränderte Anwendung zeigt nun deutlich höhere Auslastung — der Nenner ist geschrumpft.
  3. HPA sieht die Auslastung über dem Ziel und fügt Replicas hinzu.
  4. Die Last verteilt sich auf mehr Pods, der Verbrauch pro Pod sinkt.
  5. VPA beobachtet niedrigen Verbrauch und senkt den Request erneut.

Kein Controller ist defekt. Beide bewegen Zähler und Nenner desselben Bruchs, ohne voneinander zu wissen.

Kombinationen, die in der Praxis funktionieren:

  • VPA auf Memory, HPA auf CPU. Verschiedene Ressourcen, kein gemeinsamer Bruch. Die verbreitetste Paarung.
  • VPA im Modus Off. Empfehlungen gehen an einen Menschen oder eine Pipeline, nicht an den laufenden Workload.
  • VPA per controlledResources eingegrenzt, sodass es die Ressource von HPA nie anfasst:
resourcePolicy:
containerPolicies:
- containerName: "application"
controlledResources: ["memory"]
controlledValues: RequestsOnly

controlledValues: RequestsOnly verdient eine Anmerkung. Der Standard ist RequestsAndLimits — dabei skaliert das Limit proportional mit, um Ihr Verhältnis von Request zu Limit zu wahren. Haben Sie bewusst einen großen Abstand für Burst-Reserven gesetzt, wandert dieser Abstand mit. Das kann genau richtig sein — oder Ihre Limits still weit über das Gewollte hinauswachsen lassen.

Die Rückkopplung zwischen HPA und VPA auf derselben CPU-Ressource: VPA senkt den Request, die gemessene Auslastung steigt, HPA fügt Replicas hinzu, der Verbrauch pro Pod sinkt, VPA senkt den Request erneut
Nichts ist kaputt. Beide Controller bearbeiten korrekt gegenüberliegende Hälften desselben Bruchs.

Die Node-Ebene: Provisioning und Konsolidierung

Unter beiden Pod-Autoscalern sitzt der Node-Autoscaler, der zwei Dinge tut, welche die Kubernetes-Doku inzwischen ausdrücklich benennt:

  • Provisioning — Nodes hinzufügen, damit ausstehende Pods geplant werden können. Früher Scale-up.
  • Konsolidierung — unterausgelastete Nodes entfernen. Früher Scale-down.

Zwei Implementierungen werden von der SIG Autoscaling getragen, und der Unterschied liegt nicht wirklich in der Skalierungsqualität:

Die Node-Ebene: Provisioning und Konsolidierung
Cluster Autoscaler Karpenter
Node-Typen Vorab konfigurierte Node-Gruppen Aus Constraints automatisch bestimmt
Umfang Nur Node-Autoscaling Kompletter Node-Lebenszyklus
Erneuerung / Upgrade Nein Ja — Nodes werden nach fester Laufzeit neu erstellt
Cloud-Anbieter Viele, auch kleinere Wenige — AWS, Azure
Auslieferung Integrationen im Kubernetes-Projekt Als Bibliothek für Anbieter veröffentlicht

Cluster Autoscaler fragt: „Welche meiner konfigurierten Gruppen passt zu diesen ausstehenden Pods?“ Karpenter fragt: „Welche Maschine sollte für diese Pods existieren?“ — und übernimmt anschließend auch die Verantwortung, sie wieder außer Dienst zu stellen.

Der dritte stille Fehler: Konsolidierung ignoriert die reale Nutzung

Dieser kostet am meisten Geld und folgt direkt aus dem gemeinsamen Input.

Konsolidierung berücksichtigt wie Provisioning ausschließlich die Resource Requests der Pods — nicht die reale Nutzung.

Ein Node, dessen Pods 90% der Kapazität anfordern und tatsächlich 5% verbrauchen, ist für den Autoscaler ein voll belegter Node. Er wird nie zurückgegeben. Ihre Dashboards zeigen ein nahezu untätiges Cluster, Ihre Rechnung ein volles, und kein Controller meldet dazu etwas — aus Sicht des Schedulers ist ja alles in Ordnung.

Deshalb ist Rightsizing eine Voraussetzung für Kosteneffizienz auf Node-Ebene und keine spätere Zusatzoptimierung. Die Kubernetes-Doku sagt es deutlich: Requests korrekt zu setzen ist für die Kosteneffizienz genauso wichtig wie die Node-Auslastung selbst.

Eine verwandte Warnung: Setzen Sie VPA nicht auf DaemonSet-Pods ein, wenn Sie Node-Autoscaling nutzen. Der Autoscaler muss vorhersagen, was DaemonSet-Pods auf einem hypothetischen neuen Node verbrauchen werden, um dessen nutzbare Kapazität zu schätzen. Ein VPA, das diese Requests laufend ändert, macht die Vorhersage unzuverlässig — und falsche Vorhersagen ergeben falsche Skalierungsentscheidungen.

Wo KEDA hineinpasst

HPA skaliert auf Ressourcenauslastung, ein nachlaufendes Signal — die CPU steigt erst, wenn Arbeit bereits angekommen ist und sich staut. Für queue-getriebene Workloads ist das die falsche Richtung.

KEDA, ein CNCF-graduiertes Projekt, skaliert auf die Ereignisquelle selbst: Nachrichten in einer Queue, Lag auf einem Topic, wartende Zeilen. Es deckt auch den Fall ab, den CPU gar nicht ausdrücken kann — auf null skalieren, wenn die Queue leer ist. Der Cron-Scaler übernimmt geplantes Skalieren, die ehrliche Antwort für vorhersehbare Tagesmuster, bei denen Reagieren schlechter ist als Wissen.

KEDA ersetzt HPA nicht. Es erzeugt und steuert eines.

Eine funktionierende Reihenfolge

  1. Requests bewusst setzen. Alles andere liest sie. Raten korrumpiert hier alle drei Ebenen gleichzeitig.
  2. VPA im Modus Off installieren und eine Woche beobachten lassen. Vergleichen Sie .status.recommendation mit dem, was Sie angefordert haben. Die Lücke ist Ihr reales Rightsizing-Potenzial.
  3. Requests korrigieren auf Basis dieser Daten — von Hand oder über die Pipeline. Node-Konsolidierung beginnt zu wirken, sobald Requests die Realität abbilden.
  4. HPA ergänzen auf der Metrik, die Ihre Last abbildet. Bei Sidecars ContainerResource verwenden.
  5. HPA und VPA von derselben Ressource fernhalten. Memory-VPA mit CPU-HPA ist die sichere Standardpaarung.
  6. Node-Autoscaling zuletzt aktivieren. Es verstärkt, was Ihre Requests aussagen — zutreffend oder nicht.
  7. Auf PodResizePending mit reason: Infeasible alarmieren, wenn Sie In-Place-Resize nutzen, und auf HPA ScalingActive: false. Beides sind sonst stille Fehler.

Der Punkt

„Kubernetes-Autoscaling einschalten“ gibt es nicht. Es gibt drei Controller an drei verschiedenen Objekten, die sich über nichts abstimmen außer über die Resource Requests, die Sie geschrieben haben.

Jede Fehlerart in diesem Artikel führt auf dieselbe Wurzel zurück: Ein Controller hat einen Request gelesen, ihm geglaubt und korrekt auf schlechter Information gehandelt. HPA skaliert nicht, weil ein Request fehlt. HPA flattert, weil VPA den Request darunter verändert. Nodes konsolidieren nie, weil Requests ein Cluster beschreiben, das es nicht gibt.

Autoscaling macht die Dimensionierung nicht zum Problem von jemand anderem. Es macht sie zum einzigen Problem.


Weiterlesen: Zur Ebene, in der diese Controller laufen, siehe Platform Engineering auf Kubernetes. Wenn Sie zuerst eine Distribution wählen, siehe k3s vs k0s vs MicroK8s vs RKE2. Zum Abwägen zwischen verwaltet und selbstverwaltet auf GKE siehe GKE Autopilot vs Standard. Um durchzusetzen, dass Requests überhaupt gesetzt sind, siehe OPA vs Kyverno.

Quellen: Horizontal Pod Autoscaling, Vertical Pod Autoscaling (überarbeitet am 31. Mai 2026), Autoscaling Workloads, Node Autoscaling und Resize CPU and Memory Resources assigned to Containers, sämtlich Kubernetes-Dokumentation, CC BY 4.0.

Häufig gestellte Fragen

Was ist der Unterschied zwischen HPA, VPA und Cluster Autoscaler?

Sie arbeiten an drei verschiedenen Objekten. Der HorizontalPodAutoscaler ändert die Replica-Anzahl eines Workloads — mehr Pods, gleiche Größe. Der VerticalPodAutoscaler ändert die CPU- und Memory-Requests der Pods selbst — gleiche Anzahl, andere Größe. Der Node-Autoscaler (Cluster Autoscaler oder Karpenter) ändert, wie viele Maschinen darunter existieren: Er stellt Nodes bereit, wenn Pods nicht geplant werden können, und konsolidiert unterausgelastete Nodes. Es sind drei getrennte Controller ohne Koordination untereinander — deshalb erfordert ihre Kombination Nachdenken und nicht bloß drei Installationen.

Warum skaliert mein HPA nicht?

Die häufigste Ursache sind fehlende Resource Requests. HPA berechnet die Auslastung als Prozentsatz des CPU- oder Memory-Requests. Fehlt einem Container im Pod der entsprechende Request, ist die Auslastung für diesen Pod undefiniert und der Controller unternimmt für diese Metrik nichts — ohne Fehlermeldung. Zweithäufigste Ursache: Die Standardtoleranz beträgt 10%, ein Wert von 105% des Ziels löst also nichts aus. Drittens summiert HPA den Verbrauch über alle Container im Pod, sodass ein ausgelasteter Anwendungscontainer von einem untätigen Sidecar verwässert wird — dafür gibt es die ContainerResource-Metrik.

Kann ich HPA und VPA zusammen einsetzen?

Nicht unbedacht auf derselben Ressource. HPA berechnet die CPU-Auslastung als Verbrauch geteilt durch Request. Die Aufgabe von VPA ist es, genau diesen Request zu ändern. Senkt VPA den Request, springt die gemessene Auslastung nach oben, obwohl sich an der Anwendung nichts geändert hat — und HPA fügt Replicas hinzu, was den Verbrauch pro Pod senkt, was wiederum VPA beeinflusst. Beide Controller bewegen Zähler und Nenner desselben Bruchs unabhängig voneinander. Praktikabel sind: VPA auf Memory bei HPA auf CPU, VPA im Modus Off als reine Empfehlungsquelle, oder VPA per controlledResources so eingeschränkt, dass es die von HPA genutzte Ressource nie anfasst.

Bedeutet In-Place Pod Resize, dass VPA keine Pods mehr neu startet?

In-Place Pod Resize ist seit Kubernetes 1.35 stable und standardmäßig aktiviert, die Plattformfähigkeit ist also vorhanden. VPA bietet dafür den Update-Modus InPlaceOrRecreate sowie einen InPlace-Modus, der nie auf Eviction zurückfällt — verfügbar als Alpha-Feature in VPA 1.7.0 und mit Kubernetes 1.33 oder neuer samt der nötigen Feature Gates. Beachten Sie: Die Kubernetes-Dokumentation ist an dieser Stelle derzeit widersprüchlich — die Autoscaling-Übersichtsseite hält fest, dass VPA In-Place-Resizing ab 1.36 nicht unterstütze, während die VPA-Seite die Modi ausführlich dokumentiert. Prüfen Sie Ihre installierte VPA-Version, statt einer der beiden Seiten zu vertrauen.

Cluster Autoscaler oder Karpenter?

Cluster Autoscaler fügt Nodes innerhalb vorab konfigurierter Node-Gruppen hinzu und entfernt sie wieder; er integriert sich mit einer langen Liste von Cloud-Anbietern, auch kleineren. Karpenter provisioniert Nodes ohne vordefinierte Gruppen — Sie geben Constraints vor, er wählt den Maschinentyp — und verwaltet den gesamten Node-Lebenszyklus, inklusive Erneuerung nach einer festgelegten Laufzeit und Upgrade auf neue Images. Karpenter hat weniger Anbieter-Integrationen, derzeit AWS und Azure. Unterstützt Ihr Anbieter beides, lautet die Frage: Node-Gruppen-Buchhaltung oder Lebenszyklus-Verwaltung?

Warum skaliert mein Cluster nicht herunter, obwohl die Auslastung niedrig ist?

Weil Konsolidierungsentscheidungen auf Resource Requests beruhen, nicht auf realer Nutzung — derselbe blinde Fleck wie beim Provisioning. Ein Node, dessen Pods 90% der Kapazität anfordern, aber 5% verbrauchen, wirkt für den Autoscaler voll belegt und wird nie zurückgegeben. Deshalb ist vertikales Rightsizing eine Voraussetzung für Kosteneffizienz auf Node-Ebene, keine spätere Zusatzoptimierung. Weitere typische Blocker: Pods ohne Controller, der sie neu erzeugt, restriktive PodDisruptionBudgets und lokaler Speicher, der Eviction verhindert.

Aus der Community

Diskussion im Fediverse

Antworten von Mastodon und Bluesky — direkt aus dem offenen Netz, ohne Tracking.

Antworten werden geladen …

ENDE