DEV Community

Aleksei Aleinikov
Aleksei Aleinikov

Posted on Originally published at alekseialeinikov.com on

Kubernetes Autoscaling: Drei Controller, die nicht miteinander reden

Kubernetes Autoscaling: Drei Controller, die nicht miteinander reden

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

Ä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 )]
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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"}}}]}}'

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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:

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.

Ursprünglich veröffentlicht auf alekseialeinikov.com

Top comments (0)