Karpenter für GPU-Nodes: 6 Lehren zum Autoscaling
Karpenter und GPU-Nodes aus Sicht von Pexon: eigener NodePool mit GPU-Limit, KEDA auf der vLLM-Queue, Konsolidierung, Treiber-Stack, Spot und Kosten 2026.
Gastbeitrag aus der Praxis: Dieser Artikel stammt von Pexon Consulting, dem Betreiber dieser Seite und selbst Kubernetes-Dienstleister. Er beschreibt eigene Projekte und Erfahrungen und ist nicht Teil unserer neutralen Vergleiche.
TL;DR
Karpenter für GPU-Nodes lohnt sich, sobald mehr als eine GPU-Node dauerhaft läuft. Wir bei Pexon trennen dafür einen eigenen NodePool mit Taint und GPU-Limit ab, skalieren die vLLM-Pods per KEDA auf der Warteschlange und lassen Karpenter nur die Nodes nachziehen. Konsolidierung, Treiber-Stack und Spot regeln wir explizit, sonst wird Autoscaling teurer statt billiger.
Warum skalieren wir GPU-Nodes mit Karpenter statt fester Pools?
Ein fester GPU-Pool steht auf dem Wert der Lastspitze und läuft nachts weiter. Karpenter startet Nodes, wenn Pods keinen Platz finden, wählt den Instanztyp nach der Anforderung des Pods und gibt Nodes wieder frei. Bei Inferenz-Last, die fast nur zu Bürozeiten anfällt, ist das der größte Kostenhebel, den wir im Cluster selbst haben.
Dieser Beitrag ist ein gekennzeichneter Praxisbericht von Pexon Consulting, dem Betreiber dieser Seite. Wir beschreiben, wie wir GPU-Autoscaling auf Kubernetes für Kunden aufbauen, mit den Zahlen, Schwellen und Preisen, die wir auf unserer Seite zum GPU-Cluster-Betrieb veröffentlicht haben. Den Gesamt-Stack aus GPU Operator, vLLM und LiteLLM haben wir in KI auf Kubernetes betreiben beschrieben, die Frage, welches Team die Karte bekommt, in Kueue auf Kubernetes. Hier geht es nur darum, wie viele GPU-Nodes gerade laufen.
Der Ausgangspunkt ist fast immer derselbe: Ein SaaS-Produkt ruft sein Sprachmodell vor allem zwischen Morgen und Feierabend ab, der Cluster hält trotzdem rund um die Uhr dieselbe Zahl Karten vor. Datadog hat in seinem Bericht State of Cloud Costs 2024 83 Prozent der Container-Kosten Leerlauf zugeordnet, davon 54 Prozentpunkte überdimensionierter Cluster-Kapazität. Das ist eine Zahl über Container allgemein, nicht über GPUs, und nicht unsere Messung. Auf einer GPU-Node trifft dieser Leerlauf aber die teuerste Zeile der Rechnung.
Warum Karpenter und nicht der Cluster Autoscaler, fassen wir so zusammen:
| Kriterium | Cluster Autoscaler | Karpenter |
|---|---|---|
| Einheit | vorhandene Node-Gruppen | einzelne Nodes aus NodePool-Regeln |
| GPU-Varianten | eine Node-Gruppe je Kartentyp | Kartentyp als Anforderung (Label) im NodePool |
| Abbau | Scale-down leerer Nodes | Konsolidierung mit Budgets und Zeitfenstern |
| Verfügbar bei | allen großen Managed-Angeboten | EKS (AWS-Provider), AKS (Node Auto-Provisioning) |
Für AKS ist das kein Sonderweg mehr: Microsoft beschreibt Node Auto-Provisioning als verwaltetes Karpenter auf Basis des AKS Karpenter Providers, mit NodePool und AKSNodeClass als Objekten. Bei AKS Automatic ist es vorkonfiguriert, bei AKS Standard schalten Sie es explizit ein. Einschränkungen nach Microsoft: keine Windows-Node-Pools, kein IPv6, und ein Cluster mit NAP lässt sich nicht stoppen.
Lehre 1: Ein eigener NodePool mit Taint und GPU-Limit
Ein GPU-NodePool braucht drei Dinge: einen Taint, damit normale Pods die teuren Nodes nicht belegen, Anforderungen auf Kartentyp und Kapazitätsart und ein Limit auf nvidia.com/gpu. Ohne Limit gilt bei Karpenter nur das Quota des Cloud-Anbieters als Grenze. Das Limit ist unsere Kostenbremse, nicht das Budget-Alarm-Mail im Folgemonat.
Wir arbeiten mit der API karpenter.sh/v1; aktuell ist Karpenter v1.15.1 vom 23. Oktober laut Release-Seite. Auf EKS sieht ein typischer Inferenz-Pool so aus. Die Labels karpenter.k8s.aws/instance-gpu-name und instance-gpu-count stammen aus der Karpenter-Scheduling-Doku:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: gpu-inference
spec:
weight: 50
limits:
nvidia.com/gpu: 4 # harte Obergrenze für den ganzen Pool
template:
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: gpu-al2023
taints:
- key: nvidia.com/gpu
value: "true"
effect: NoSchedule
requirements:
- key: karpenter.k8s.aws/instance-gpu-name
operator: In
values: ["l4"]
- key: karpenter.k8s.aws/instance-gpu-count
operator: In
values: ["1"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 15m
Zwei Details aus der NodePool-Doku, die uns schon Zeit gekostet haben: Die Limit-Prüfung ist nur eventual consistent, ein schneller Scale-out kann das Limit also kurz überschreiten. Und weight entscheidet, welcher Pool zuerst gezogen wird; ist der Pool mit dem höchsten Gewicht an seinem Limit, weicht Karpenter auf den nächsten aus. Wer einen teuren Fallback-Pool mit größeren Karten definiert, sollte ihm deshalb ein eigenes, kleines Limit geben.
Auf AKS heißen die Labels anders. Microsoft nennt in der NAP-Doku zu Node Pools karpenter.azure.com/sku-family (Wert N für GPU-VMs), sku-gpu-name, sku-gpu-count und sku-gpu-manufacturer. Die Logik bleibt gleich.
Lehre 2: Erst die Pods skalieren, dann die Nodes
Karpenter reagiert nur auf Pods, die nicht platziert werden können. Die Frage, ob ein zweiter vLLM-Pod gebraucht wird, beantwortet Karpenter nicht. Wir skalieren deshalb zweistufig: KEDA erhöht die Replikas anhand der vLLM-Warteschlange, die neuen Pods bleiben Pending, und erst dann startet Karpenter eine GPU-Node.
vLLM liefert die nötige Metrik selbst. Die vLLM-Doku führt vllm:num_requests_waiting als Gauge für Anfragen, die auf Verarbeitung warten, abrufbar unter /metrics. CPU-Auslastung taugt als Signal nicht, weil die Arbeit auf der GPU passiert. Unsere Vorlage mit dem KEDA-Prometheus-Scaler, die Schwellen setzen wir erst nach dem Lasttest:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: vllm-chat
namespace: inference
spec:
scaleTargetRef:
name: vllm-chat
minReplicaCount: 1 # KEDA-Default wäre 0
maxReplicaCount: 4 # passt zum GPU-Limit des NodePools
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring:9090
query: sum(vllm:num_requests_waiting{model_name="chat"})
threshold: "5"
Wichtig ist das Zusammenspiel der Grenzen: maxReplicaCount und das GPU-Limit im NodePool müssen zueinander passen. Sonst skaliert KEDA auf sechs Pods, Karpenter darf nur vier Karten starten, und zwei Pods hängen dauerhaft in Pending, ohne dass ein Alarm anschlägt.
Den Default minReplicaCount: 0 aus der ScaledObject-Spezifikation setzen wir für Chat-Endpunkte bewusst auf 1. Der cooldownPeriod von 300 Sekunden gilt nur für den Schritt auf null. Scale-to-zero spart die letzte Karte, kostet aber beim ersten Aufruf am Morgen den kompletten Kaltstart: Node bereitstellen, Treiber, Image, Modellgewichte in den GPU-Speicher. Für Batch-Endpunkte, die Wartezeit vertragen, ist null richtig, für ein Produkt mit zahlenden Kunden nicht.
Lehre 3: Konsolidierung explizit setzen, nie den Default
Der Karpenter-Default ist für GPU-Inferenz zu aggressiv: WhenEmptyOrUnderutilized mit consolidateAfter: 0s. Karpenter darf dann eine halb ausgelastete GPU-Node sofort ersetzen oder leeren, also einen vLLM-Pod mitten in der Antwort verschieben. Für GPU-Pools setzen wir WhenEmpty mit Wartezeit und ein Budget, das zu Bürozeiten keine Störung zulässt.
Die Disruption-Doku nennt die Stellschrauben. Seit v1.15 gibt es neben WhenEmpty und WhenEmptyOrUnderutilized die Policy Balanced, die Aktionen überspringt, deren Ersparnis die Störung nicht rechtfertigt. Ohne eigene Angabe gilt ein Budget von 10 Prozent der Nodes. Budgets kennen einen Cron-schedule mit duration, und zwar in UTC, Zeitzonen werden nicht unterstützt. So sieht unser Bürozeit-Schutz aus:
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 15m
budgets:
- nodes: "0"
schedule: "0 6 * * mon-fri" # 06:00 UTC = 08:00 MESZ, im Winter 07:00 MEZ
duration: 11h
reasons: ["Empty", "Underutilized", "Drifted"]
- nodes: "1"
Den Zeitzonen-Fallstrick sehen wir regelmäßig: Ein Fenster, das im Sommer passt, verschiebt sich mit der Umstellung auf MEZ um eine Stunde. Wir legen das Fenster deshalb so breit, dass es beide Zeiten abdeckt. Für einzelne Pods hilft zusätzlich die Annotation karpenter.sh/do-not-disrupt, auch mit Dauer wie "30m". Sie schützt vor Konsolidierung, aber nicht vor Ablauf (expireAfter, Default 720 Stunden), Spot-Unterbrechung oder Node Auto Repair. Wer GPU-Nodes nie automatisch erneuern will, muss expireAfter selbst setzen; Microsoft macht genau das im NAP-Default-Pool mit expireAfter: Never.
Lehre 4: Den Treiber-Stack an das Image koppeln
Eine neue GPU-Node ist erst nutzbar, wenn Treiber, Container-Toolkit und Device Plugin laufen. Karpenter sagt es deutlich: Ohne Device-Plugin-DaemonSet gilt die Node nicht als initialisiert. Der häufigste Fehler, den wir bei übernommenen Setups sehen, ist ein doppelter Treiber: einmal im Image, einmal vom GPU Operator.
AWS beschreibt in der Doku zu den EKS-optimierten Accelerated AMIs, was wo liegt. Das AL2023-NVIDIA-Image bringt Treiber und Container-Toolkit mit, aber kein Device Plugin. Das Bottlerocket-NVIDIA-Image enthält zusätzlich das Device Plugin und den MIG Manager. Wer den NVIDIA GPU Operator trotzdem einsetzt, etwa für einheitliches Monitoring, muss die Treiber- und Toolkit-Installation des Operators abschalten, bei Bottlerocket auch das Device Plugin. Unsere Prüfreihenfolge nach jedem Image-Wechsel:
- NodeClass auf ein gepinntes Image setzen, etwa
alias: al2023@v20240703stattal2023@latest. Die NodeClass-Doku rät für Produktion ausdrücklich vonlatestab. - Neues Image in einem Testcluster mit einer GPU-Node hochziehen und
nvidia-smiim Pod prüfen. - Prüfen, ob die Node die Ressource meldet:
kubectl get nodes -l karpenter.sh/nodepool=gpu-inference -o custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu. - GPU-Operator-Werte gegen das Image abgleichen (Treiber und Toolkit aus, Device Plugin je nach Image).
- Erst dann die NodeClass in Produktion ändern. Das löst Drift aus, und Karpenter ersetzt die Nodes innerhalb der Budgets aus Lehre 3.
Auf AKS kann NAP Treiber, Device Plugin, DCGM-Exporter und GPU-Health-Signale selbst verwalten, wenn die AKSNodeClass als Managed GPU konfiguriert ist. Microsoft führt das zum Stand dieses Beitrags als Preview. Wir nutzen es in Testclustern, für Produktion entscheiden wir je Kunde.
Lehre 5: Spot nur für Last, die Unterbrechung verträgt
Spot-GPUs sind deutlich günstiger, können aber jederzeit entzogen werden. Karpenter priorisiert bei erlaubten Kapazitätsarten erst reserved, dann spot, dann on-demand; NAP auf AKS bevorzugt ebenfalls Spot, wenn beides erlaubt ist. Wer Spot und On-Demand im selben Inferenz-Pool zulässt, bekommt also fast immer Spot. Wir trennen deshalb in zwei NodePools.
Wie groß der Abstand ist, zeigt die öffentliche Azure Retail Prices API für Germany West Central, Stand 29.10.2026, Linux, netto in Euro: Standard_NC4as_T4_v3 (eine NVIDIA T4) kostet On-Demand 0,579 € pro Stunde, als Spot 0,1642 €. Das sind rund 72 Prozent weniger. Unsere Aufteilung:
| Workload | Kapazitätsart | Begründung |
|---|---|---|
| Chat-Endpunkt für Kunden | On-Demand | Unterbrechung ist ein Supportfall |
| Embeddings und Batch-Inferenz | Spot, Fallback On-Demand mit niedrigem Gewicht | Wiederholung kostet nur Zeit |
| Modell-Tests und Evaluierung | Spot | Abbruch ist hinnehmbar |
| Training und Fine-Tuning | nicht unser Angebot | bleibt bei Kunde oder Data-Science-Partner |
Der Fallback-Pool mit On-Demand bekommt ein kleineres weight und ein eigenes GPU-Limit. So weicht Karpenter auf On-Demand aus, wenn keine Spot-Kapazität verfügbar ist, ohne die Rechnung unbegrenzt zu öffnen.
Lehre 6: Die Rechnung vor dem ersten NodePool
Autoscaling ist kein Selbstzweck. Unsere Schwelle aus der Praxis: Ab zwei GPU-Nodes wird Autoscaling zum Kostenhebel, darunter reicht eine einzelne Node ohne Betriebsvertrag. Die Rechnung machen wir offen: Stundenpreis mal Laufzeit, einmal 730 Stunden im Monat, einmal 220 Stunden für zehn Stunden an 22 Arbeitstagen.
Preise Stand 29.10.2026, On-Demand, Linux, netto ohne Steuern, aus der AWS Price List API für EU (Frankfurt) und der Azure Retail Prices API für Germany West Central. Die g6.xlarge trägt laut AWS-Produktseite eine NVIDIA L4 mit 24 GB, 4 vCPU und 16 GiB RAM, die g6e.xlarge eine L40S mit 48 GB.
| Instanz (Region) | GPU | pro Stunde | 730 h (24/7) | 220 h (Bürozeit) |
|---|---|---|---|---|
| g6.xlarge (Frankfurt) | 1x L4 24 GB | 1,0064 USD | 734,67 USD | 221,41 USD |
| g6e.xlarge (Frankfurt) | 1x L40S 48 GB | 2,327 USD | 1.698,71 USD | 511,94 USD |
| NC4as_T4_v3 (Germany West Central) | 1x T4 | 0,579 € | 422,67 € | 127,38 € |
| NC24ads_A100_v4 (Germany West Central) | 1x A100 | 4,2019 € | 3.067,39 € | 924,42 € |
Die Bürozeit-Spalte ist eine Obergrenze dessen, was Autoscaling sparen kann, kein Versprechen: 220 von 730 Stunden sind rund 30 Prozent der Laufzeit. In der Praxis bleibt mit minReplicaCount: 1 mindestens eine Karte an, Lastspitzen ziehen zusätzliche Nodes, und Kaltstarts kosten bezahlte Minuten ohne Antworten. Welche Summe am Ende steht, messen wir am Cluster selbst, vorher und nachher. Die Control-Plane- und Node-Preise ohne GPU stehen im Managed-Kubernetes-Kostenvergleich.
Was kostet GPU-Autoscaling mit Pexon?
Der Einstieg ist der AI-Readiness-Scan für 990 € Festpreis, ein Tag mit Bericht und Termin. Der Aufbau bis Go-live kostet 35.000 bis 60.000 € einmalig, der laufende Betrieb 5.000 bis 10.000 € im Monat. Die GPU-Rechenleistung kaufen Kunden direkt beim Anbieter, wir schlagen nichts auf.
Alle Preise stammen von unseren Seiten zum GPU-Cluster-Betrieb und zur vLLM-Inferenz, Stand 29.10.2026, netto. Die Servicezeit ist Montag bis Freitag, 08:00 bis 18:00 Uhr MEZ; Verfügbarkeitsprozente nennen wir erst, wenn sie im Vertrag stehen. Ein Senior-Engineer betreut bei uns über GitOps 30 bis 50 Cluster, deshalb ist der Betrieb ein fester Monatsbetrag. Der Ablauf:
| Phase | Dauer | Inhalt |
|---|---|---|
| AI-Readiness-Scan | 1 Tag | GPU-Ausstattung, Auslastung, Scheduling, Datenwege, 990 € |
| Zielbild | Woche 1 bis 2 | Lastannahmen, Kartentypen, Schnitt der NodePools |
| Aufbau über GitOps | Woche 2 bis 5 | GPU Operator, Karpenter, Taints, Sharing-Verfahren |
| Hypercare, dann Betrieb | 30 Tage, dann laufend | Skalierregeln nachschärfen, Monatsbericht zu Auslastung und Kosten |
Was nicht dazugehört: Training und Fine-Tuning, Anwendungslogik und der Weiterverkauf fremder Rechenleistung. An NVIDIA bleiben Sie gebunden, weil Treiber und Laufzeit vom Hersteller kommen. Welche Managed-Kubernetes-Angebote in deutschen Regionen den Unterbau stellen, zeigt der Vergleich Managed Kubernetes Deutschland; die Profile von Amazon EKS und Azure AKS nennen Regionen und Zertifikate.
Die 6 Lehren im Überblick
Kurz gesagt: eigener GPU-Pool mit Limit, Pods vor Nodes skalieren, Konsolidierung und Zeitfenster selbst setzen, Treiber-Stack an ein gepinntes Image koppeln, Spot nur für unterbrechbare Last und keine Ersparnis ohne Messung am eigenen Cluster. Diese sechs Punkte prüfen wir, bevor wir über zusätzliche Karten sprechen.
- Eigener Pool, harte Grenze. Taint, Kartentyp als Anforderung,
nvidia.com/gpuals Limit. - Pods vor Nodes. KEDA auf
vllm:num_requests_waiting, Karpenter zieht nur nach. - Kein Default bei der Konsolidierung.
WhenEmpty, Wartezeit, Budget mit Zeitfenster in UTC. - Ein Treiber, nicht zwei. Image pinnen, GPU Operator passend zum Image konfigurieren.
- Spot getrennt halten. Eigener Pool für unterbrechbare Last, On-Demand-Fallback mit Limit.
- Zahl nur vom Cluster. Obergrenze rechnen, Ersparnis messen, nicht versprechen.
FAQ
Was kostet GPU-Autoscaling mit Karpenter bei Pexon?
Der AI-Readiness-Scan kostet 990 € einmalig, der Aufbau 35.000 bis 60.000 € einmalig, der Betrieb 5.000 bis 10.000 € im Monat, jeweils netto, Stand 29.10.2026. Die GPU-Instanzen zahlen Sie direkt beim Cloud-Anbieter. Karpenter selbst ist Open Source und kostet keine Lizenz.
Karpenter vs. Cluster Autoscaler: Was passt für GPU-Nodes?
Für GPU-Nodes nehmen wir Karpenter. Der Cluster Autoscaler skaliert vorhandene Node-Gruppen, jede GPU-Variante braucht also eine eigene Gruppe. Karpenter wählt den Instanztyp aus den Anforderungen im NodePool und konsolidiert mit Budgets und Zeitfenstern. Wo Karpenter nicht verfügbar ist, bleibt der Cluster Autoscaler die solide Wahl.
Gibt es Karpenter auch auf AKS?
Ja. Node Auto-Provisioning in AKS ist laut Microsoft verwaltetes Karpenter auf Basis des AKS Karpenter Providers, konfiguriert über NodePool und AKSNodeClass. GPU-VMs wählen Sie über karpenter.azure.com/sku-family mit dem Wert N und GPU-Labels wie sku-gpu-name. Windows-Node-Pools und IPv6 werden nicht unterstützt.
Braucht man mit Karpenter noch den NVIDIA GPU Operator?
Nicht zwingend. Die EKS-NVIDIA-Images bringen Treiber und Toolkit mit, Bottlerocket zusätzlich das Device Plugin. Fehlt das Device Plugin, gilt die Node für Karpenter nicht als initialisiert. Setzen Sie den Operator ein, schalten Sie dessen Treiber- und Toolkit-Installation ab, damit nicht zwei Treiber konkurrieren.
Sollen vLLM-Pods auf null skalieren?
Für Chat-Endpunkte mit Kunden nicht. Scale-to-zero spart die letzte Karte, kostet aber beim ersten Aufruf einen kompletten Kaltstart aus Node, Treiber, Image und Modellgewichten. Wir setzen minReplicaCount: 1 für interaktive Endpunkte und null nur für Batch-Last, die Wartezeit verträgt.
Wie viel spart Autoscaling bei GPU-Nodes?
Das hängt am Lastprofil. Läuft eine g6.xlarge in Frankfurt nur 220 statt 730 Stunden im Monat, sinkt die Rechnung von 734,67 USD auf 221,41 USD. Das ist die Obergrenze, nicht das Ergebnis: Mindest-Replikas, Spitzen und Kaltstarts kosten mit. Wir messen die Ersparnis am Cluster, statt sie vorab zu versprechen.
Quellen
- Pexon: GPU-Cluster auf Kubernetes (abgerufen 29. Oktober 2026)
- Pexon: KI-Inferenz auf Kubernetes mit vLLM (abgerufen 29. Oktober 2026)
- Pexon: KI-Plattform-Betrieb auf Kubernetes (abgerufen 29. Oktober 2026)
- Karpenter Docs: NodePools (abgerufen 29. Oktober 2026)
- Karpenter Docs: Disruption (abgerufen 29. Oktober 2026)
- Karpenter Docs: Scheduling (abgerufen 29. Oktober 2026)
- Karpenter Docs: NodeClasses (abgerufen 29. Oktober 2026)
- GitHub: kubernetes-sigs/karpenter Releases (abgerufen 29. Oktober 2026)
- Microsoft Learn: Node Auto-Provisioning in AKS (abgerufen 29. Oktober 2026)
- Microsoft Learn: Node Pools für Node Auto-Provisioning (abgerufen 29. Oktober 2026)
- AWS: EKS-optimierte Accelerated AMIs (abgerufen 29. Oktober 2026)
- AWS: Amazon EC2 G6 (abgerufen 29. Oktober 2026)
- AWS: Amazon EC2 G6e (abgerufen 29. Oktober 2026)
- AWS Price List API: EC2 eu-central-1 (abgerufen 29. Oktober 2026)
- Azure Retail Prices API (abgerufen 29. Oktober 2026)
- vLLM Docs: Metrics (abgerufen 29. Oktober 2026)
- KEDA Docs: Prometheus Scaler (abgerufen 29. Oktober 2026)
- KEDA Docs: ScaledObject Spec (abgerufen 29. Oktober 2026)
- Datadog: State of Cloud Costs 2024 (Pressemitteilung) (abgerufen 29. Oktober 2026)