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.

Drei Controller, drei Objekte, ein gemeinsamer Input und null Koordination.
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.
| 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:
| 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.
- VPA beobachtet niedrigen Verbrauch und senkt den Request.
- Dieselbe unveränderte Anwendung zeigt nun deutlich höhere Auslastung — der Nenner ist geschrumpft.
- HPA sieht die Auslastung über dem Ziel und fügt Replicas hinzu.
- Die Last verteilt sich auf mehr Pods, der Verbrauch pro Pod sinkt.
- 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
controlledResourceseingegrenzt , 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.

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:
| 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
- Requests bewusst setzen. Alles andere liest sie. Raten korrumpiert hier alle drei Ebenen gleichzeitig.
-
VPA im Modus
Offinstallieren und eine Woche beobachten lassen. Vergleichen Sie.status.recommendationmit dem, was Sie angefordert haben. Die Lücke ist Ihr reales Rightsizing-Potenzial. - Requests korrigieren auf Basis dieser Daten — von Hand oder über die Pipeline. Node-Konsolidierung beginnt zu wirken, sobald Requests die Realität abbilden.
-
HPA ergänzen auf der Metrik, die Ihre Last abbildet. Bei Sidecars
ContainerResourceverwenden. - HPA und VPA von derselben Ressource fernhalten. Memory-VPA mit CPU-HPA ist die sichere Standardpaarung.
- Node-Autoscaling zuletzt aktivieren. Es verstärkt, was Ihre Requests aussagen — zutreffend oder nicht.
-
Auf
PodResizePendingmitreason: Infeasiblealarmieren , wenn Sie In-Place-Resize nutzen, und auf HPAScalingActive: 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.
Ursprünglich veröffentlicht auf alekseialeinikov.com

Top comments (0)