---
title: "Kubernetes Autoscaling: Drei Controller, die nicht miteinander reden"
description: "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."
author: Aleksei Aleinikov
date: 2026-08-26
lang: de
tags: [kubernetes-autoscaling, hpa-kubernetes, horizontal-pod-autoscaler, vertical-pod-autoscaler, cluster-autoscaler, karpenter, in-place-pod-resize]
canonical: https://www.alekseialeinikov.com/de/blog/topics/devops/kubernetes-autoscaling-2026-hpa-vpa-cluster-autoscaler-vergleich
source: alekseialeinikov.com
---

# 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.

<figure>
  <img src="/blog/kubernetes-autoscaling-layers-2026.webp" alt="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" width="1200" height="660" loading="lazy" decoding="async" />
  <figcaption>Drei Controller, drei Objekte, ein gemeinsamer Input und null Koordination.</figcaption>
</figure>

## 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:

```text
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:

```yaml
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:

```bash
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:

```yaml
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:

```yaml
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:

```bash
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:

```yaml
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:

```yaml
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.

<figure>
  <img src="/blog/kubernetes-autoscaling-feedback-2026.webp" alt="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" width="1200" height="640" loading="lazy" decoding="async" />
  <figcaption>Nichts ist kaputt. Beide Controller bearbeiten korrekt gegenüberliegende Hälften desselben Bruchs.</figcaption>
</figure>

## 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](https://www.alekseialeinikov.com/de/blog/topics/devops/platform-engineering-auf-kubernetes-2026). Wenn Sie zuerst eine Distribution wählen, siehe [k3s vs k0s vs MicroK8s vs RKE2](https://www.alekseialeinikov.com/de/blog/topics/devops/k3s-vs-k0s-vs-microk8s-vs-rke2-2026-vergleich). Zum Abwägen zwischen verwaltet und selbstverwaltet auf GKE siehe [GKE Autopilot vs Standard](https://www.alekseialeinikov.com/de/blog/topics/cloud/gke-autopilot-vs-standard-vergleich-2026). Um durchzusetzen, dass Requests überhaupt gesetzt sind, siehe [OPA vs Kyverno](https://www.alekseialeinikov.com/de/blog/topics/security/opa-vs-kyverno-2026-kubernetes-policy-engine-vergleich).

**Quellen:** [Horizontal Pod Autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/), [Vertical Pod Autoscaling](https://kubernetes.io/docs/concepts/workloads/autoscaling/vertical-pod-autoscale/) (überarbeitet am 31. Mai 2026), [Autoscaling Workloads](https://kubernetes.io/docs/concepts/workloads/autoscaling/), [Node Autoscaling](https://kubernetes.io/docs/concepts/cluster-administration/node-autoscaling/) und [Resize CPU and Memory Resources assigned to Containers](https://kubernetes.io/docs/tasks/configure-pod-container/resize-container-resources/), sämtlich Kubernetes-Dokumentation, CC BY 4.0.
