- Authors

- Name
- Phillip Pham
- @ddppham
Kubernetes Scalability Patterns: Horizontale und vertikale Skalierung fuer Microservices
TL;DR
- HPA skaliert horizontal (mehr Pods), VPA skaliert vertikal (groessere Pods), Cluster Autoscaler skaliert die Infrastruktur (mehr Nodes)
- KEDA erweitert HPA um Event-basierte Skalierung und ermoeglicht Scale-to-Zero fuer selten genutzte Workloads
- HPA und VPA auf derselben Metrik ist das haeufigste Anti-Pattern und fuehrt zu Skalierungsschleifen -- VPA im Off-Modus ist die sichere Alternative
- Multi-Dimensional Autoscaling kombiniert alle drei Ebenen: HPA fuer Pods, VPA fuer Sizing, Cluster Autoscaler fuer Nodes
- Eine Scaling Decision Matrix hilft bei der Wahl des richtigen Patterns je nach Workload-Typ
Die drei Dimensionen der Kubernetes Skalierung
Kubernetes bietet Skalierung auf drei Ebenen. Jede Ebene loest ein anderes Problem:
Horizontal (HPA) -- Mehr Pods desselben Typs. Geeignet fuer stateless Services, die Last durch Parallelisierung bewaeltigen.
Vertikal (VPA) -- Groessere Pods mit mehr CPU und Memory. Geeignet fuer stateful Workloads, die nicht einfach parallelisiert werden koennen.
Infrastruktur (Cluster Autoscaler) -- Mehr Nodes im Cluster. Reagiert automatisch, wenn Pods nicht gescheduled werden koennen.
Das Ziel: Alle drei Ebenen so kombinieren, dass der Cluster automatisch auf Last reagiert und gleichzeitig Kosten minimiert.
Horizontal Pod Autoscaler (HPA)
CPU-basierte Skalierung
Der klassische Einstieg. HPA beobachtet die CPU-Auslastung und skaliert Pods entsprechend:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-frontend-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-frontend
minReplicas: 3
maxReplicas: 30
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 4
periodSeconds: 60
- type: Percent
value: 50
periodSeconds: 60
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 20
periodSeconds: 120
selectPolicy: Min
Die behavior-Section ist entscheidend. Ohne sie skaliert der HPA zu aggressiv herunter und erzeugt Flapping. Die Konfiguration oben bedeutet:
- Scale-Up: Maximal 4 Pods oder 50% mehr (der groessere Wert gilt), alle 60 Sekunden
- Scale-Down: Maximal 20% weniger Pods alle 2 Minuten, mit 5 Minuten Stabilisierung
Multi-Metric HPA
Fuer komplexere Workloads reicht eine einzelne Metrik nicht aus. Der HPA kann mehrere Metriken gleichzeitig ueberwachen und skaliert auf den hoechsten errechneten Wert:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-gateway-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
minReplicas: 3
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
Custom Metrics mit Prometheus Adapter
# Prometheus Adapter ConfigMap fuer Custom Metrics
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-adapter-config
namespace: monitoring
data:
config.yaml: |
rules:
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace:
resource: namespace
pod:
resource: pod
name:
matches: "^(.*)_total$"
as: "${1}_per_second"
metricsQuery: 'rate(<<.Series>>{<<.LabelMatchers>>}[2m])'
- seriesQuery: 'rabbitmq_queue_messages{namespace!=""}'
resources:
overrides:
namespace:
resource: namespace
name:
as: "rabbitmq_queue_depth"
metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
Vertical Pod Autoscaler (VPA)
VPA im Recommender-Modus (sicher)
Der sicherste Einstieg: VPA im Off-Modus analysiert den tatsaechlichen Verbrauch und gibt Empfehlungen, ohne etwas zu aendern:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: order-service-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: order-service
minAllowed:
cpu: 50m
memory: 128Mi
maxAllowed:
cpu: 4
memory: 8Gi
controlledResources:
- cpu
- memory
# VPA-Empfehlungen abfragen
kubectl get vpa order-service-vpa -n production -o yaml | \
grep -A 20 "recommendation:"
Die Ausgabe zeigt drei Werte:
- lowerBound -- Minimum, unter dem der Pod wahrscheinlich Probleme bekommt
- target -- Optimaler Wert basierend auf historischen Daten
- upperBound -- Puffer fuer Lastspitzen
VPA im Auto-Modus
Im Auto-Modus passt VPA die Requests automatisch an. Der Pod wird dafuer neu gestartet:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: batch-worker-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: batch-worker
updatePolicy:
updateMode: "Auto"
minReplicas: 2
resourcePolicy:
containerPolicies:
- containerName: worker
minAllowed:
cpu: 100m
memory: 256Mi
maxAllowed:
cpu: 8
memory: 16Gi
controlledResources:
- cpu
- memory
controlledValues: RequestsAndLimits
minReplicas: 2 stellt sicher, dass VPA immer mindestens 2 Pods aktiv laesst, bevor es einen Pod zum Neustart evicted.
Cluster Autoscaler
Grundkonfiguration
# Cluster Autoscaler Helm Values
autoDiscovery:
clusterName: production-cluster
tags:
- k8s.io/cluster-autoscaler/enabled
- k8s.io/cluster-autoscaler/production-cluster
extraArgs:
scale-down-delay-after-add: 10m
scale-down-delay-after-delete: 1m
scale-down-unneeded-time: 10m
scale-down-utilization-threshold: "0.5"
max-graceful-termination-sec: 600
balance-similar-node-groups: true
expander: least-waste
skip-nodes-with-local-storage: false
max-node-provision-time: 5m
Node-Gruppen-Strategie
# Verschiedene Node-Gruppen fuer verschiedene Workload-Typen
# Node-Gruppe 1: General Purpose (On-Demand)
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-autoscaler-priority-expander
namespace: kube-system
data:
priorities: |
50:
- ".*spot.*"
30:
- ".*general-purpose.*"
10:
- ".*high-memory.*"
KEDA: Event-Driven Autoscaling
KEDA (Kubernetes Event-Driven Autoscaler) erweitert den HPA um dutzende Event-Quellen und ermoeglicht Scale-to-Zero.
KEDA installieren
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda \
--namespace keda \
--create-namespace \
--set watchNamespace="" \
--set resources.operator.requests.cpu=100m \
--set resources.operator.requests.memory=128Mi
Queue-basierte Skalierung
# ScaledObject fuer RabbitMQ Queue
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: order-processor-scaler
namespace: production
spec:
scaleTargetRef:
name: order-processor
pollingInterval: 15
cooldownPeriod: 120
minReplicaCount: 0
maxReplicaCount: 50
fallback:
failureThreshold: 3
replicas: 2
triggers:
- type: rabbitmq
metadata:
host: amqp://rabbitmq.messaging:5672
queueName: orders
queueLength: "10"
activationQueueLength: "1"
minReplicaCount: 0 ist der Schluessel: Wenn die Queue leer ist, skaliert KEDA den Workload komplett auf null. Sobald eine Nachricht eintrifft (activationQueueLength: 1), wird der erste Pod gestartet.
Cron-basierte Skalierung
Fuer vorhersehbare Last-Muster:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: web-shop-scaler
namespace: production
spec:
scaleTargetRef:
name: web-shop
minReplicaCount: 2
maxReplicaCount: 30
triggers:
- type: cron
metadata:
timezone: Europe/Berlin
start: "0 8 * * 1-5"
end: "0 20 * * 1-5"
desiredReplicas: "10"
- type: cron
metadata:
timezone: Europe/Berlin
start: "0 20 * * 1-5"
end: "0 8 * * 2-6"
desiredReplicas: "3"
- type: prometheus
metadata:
serverAddress: http://prometheus-server.monitoring:9090
metricName: http_requests_per_second
query: sum(rate(http_requests_total{deployment="web-shop"}[2m]))
threshold: "500"
Dieses Setup kombiniert Cron (vorhersehbare Geschaeftszeiten) mit Prometheus-Metriken (unvorhergesehene Lastspitzen).
Multi-Dimensional Autoscaling
Die Kombination aller Skalierungs-Dimensionen ergibt das staerkste Setup. Aber die Kombination muss korrekt sein.
Sichere Kombination: HPA + VPA (Off) + Cluster Autoscaler
# 1. VPA im Off-Modus fuer Sizing-Empfehlungen
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: api
controlledResources:
- cpu
- memory
---
# 2. HPA fuer horizontale Skalierung
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
Der Cluster Autoscaler laeuft separat und reagiert automatisch, wenn der HPA mehr Pods anfordert als auf bestehende Nodes passen.
VPA und HPA auf getrennten Metriken
Wenn VPA im Auto-Modus laufen soll, muessen VPA und HPA verschiedene Metriken steuern:
# VPA steuert nur Memory
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: ml-inference-vpa
namespace: ml
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: ml-inference
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: inference
controlledResources:
- memory
minAllowed:
memory: 512Mi
maxAllowed:
memory: 16Gi
---
# HPA skaliert auf Custom Metric (nicht CPU/Memory)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ml-inference-hpa
namespace: ml
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ml-inference
minReplicas: 2
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: inference_queue_depth
target:
type: AverageValue
averageValue: "5"
Scaling Anti-Patterns
Anti-Pattern 1: HPA und VPA auf derselben Metrik
# FALSCH -- fuehrt zu Skalierungsschleifen
# VPA erhoeht CPU-Requests -> HPA sieht niedrigere CPU-Auslastung -> skaliert runter
# -> weniger Pods -> hoehere Last -> HPA skaliert hoch -> VPA senkt Requests -> Schleife
# VPA auf CPU im Auto-Modus
# + HPA auf CPU-Auslastung
# = Skalierungsschleife
Loesung: VPA im Off-Modus nutzen oder VPA und HPA auf unterschiedliche Ressourcen beschraenken.
Anti-Pattern 2: Fehlende Resource Requests
# FALSCH -- ohne Requests kann kein Autoscaler korrekt arbeiten
spec:
containers:
- name: app
image: my-app:v1.0
# Keine resources.requests definiert
# RICHTIG -- Requests sind die Grundlage fuer Skalierung
spec:
containers:
- name: app
image: my-app:v1.0
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1
memory: 1Gi
Anti-Pattern 3: Zu enge Min/Max-Grenzen
# FALSCH -- HPA hat keinen Spielraum
spec:
minReplicas: 5
maxReplicas: 6
# RICHTIG -- genuegend Spielraum fuer Skalierung
spec:
minReplicas: 3
maxReplicas: 30
Anti-Pattern 4: Scale-Down ohne PodDisruptionBudget
# PodDisruptionBudget schuetzt vor zu aggressivem Scale-Down
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
namespace: production
spec:
minAvailable: "75%"
selector:
matchLabels:
app: api-service
Scaling Decision Matrix
Welches Pattern passt zu welchem Workload?
| Workload-Typ | HPA | VPA | KEDA | Empfehlung |
|---|---|---|---|---|
| Stateless Web API | Ja | Off-Modus | Optional | HPA auf CPU + Request-Rate |
| Stateful Database | Nein | Auto | Nein | VPA fuer Memory-Anpassung |
| Message Consumer | Optional | Off-Modus | Ja | KEDA auf Queue-Tiefe |
| Batch/CronJob | Nein | Off-Modus | Ja | KEDA Scale-to-Zero |
| ML Inference | Ja | Memory-only | Ja | KEDA + HPA auf GPU/Queue |
| Event Stream Processor | Ja | Off-Modus | Ja | KEDA auf Kafka Lag |
| Monolith | Nein | Auto | Nein | VPA, da nicht horizontal skalierbar |
Load Testing zur Verifizierung
Skalierung muss getestet werden, bevor sie in Produktion geht. Ein systematischer Ansatz:
k6 Load Test
# k6 als Kubernetes Job ausfuehren
kubectl apply -f - << 'LOADTEST'
apiVersion: batch/v1
kind: Job
metadata:
name: scaling-loadtest
namespace: testing
spec:
template:
spec:
containers:
- name: k6
image: grafana/k6:latest
command: ["k6", "run", "/scripts/test.js"]
volumeMounts:
- name: scripts
mountPath: /scripts
volumes:
- name: scripts
configMap:
name: k6-scaling-test
restartPolicy: Never
LOADTEST
# k6-Testskript als ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: k6-scaling-test
namespace: testing
data:
test.js: |
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 500 },
{ duration: '5m', target: 500 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\<500'],
http_req_failed: ['rate\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\<0.01'],
},
};
export default function () {
const res = http.get('http://api-service.production:8080/health');
check(res, { 'status 200': (r) => r.status === 200 });
sleep(0.1);
}
Skalierung waehrend des Tests beobachten
# HPA-Status live beobachten
kubectl get hpa -n production -w
# Pod-Anzahl verfolgen
kubectl get pods -n production -l app=api-service -w
# Node-Anzahl bei Cluster Autoscaler
kubectl get nodes -w
Monitoring von Skalierungs-Entscheidungen
# PrometheusRule fuer Autoscaling-Monitoring
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: autoscaling-monitoring
namespace: monitoring
spec:
groups:
- name: autoscaling-alerts
rules:
- alert: HPAAtMaxReplicas
expr: |
kube_horizontalpodautoscaler_status_current_replicas
== kube_horizontalpodautoscaler_spec_max_replicas
for: 15m
labels:
severity: warning
annotations:
summary: "HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} am Maximum seit 15 Minuten"
- alert: HPAScaleDownBlocked
expr: |
kube_horizontalpodautoscaler_status_current_replicas
> kube_horizontalpodautoscaler_spec_min_replicas * 2
and changes(kube_horizontalpodautoscaler_status_current_replicas[30m]) == 0
for: 1h
labels:
severity: info
annotations:
summary: "HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} skaliert seit 1 Stunde nicht herunter"
- alert: PendingPodsNoScaleUp
expr: |
sum(kube_pod_status_phase{phase="Pending"}) by (namespace) > 0
for: 10m
labels:
severity: critical
annotations:
summary: "Pods in {{ $labels.namespace }} seit 10 Minuten Pending -- Cluster Autoscaler moeglicherweise blockiert"
Verwandte Artikel
- Kubernetes Autoscaling: HPA, VPA und Cluster Autoscaler kombinieren
- Kubernetes Cluster Autoscaler: Setup und Tuning
- KEDA Event-Driven Autoscaling
- Kubernetes Performance Optimierung
- Kubernetes Capacity Planning
Fazit
Kubernetes skalierung ist kein einzelnes Feature, sondern ein Zusammenspiel aus HPA, VPA, Cluster Autoscaler und KEDA. Das richtige Pattern haengt vom Workload-Typ ab: Stateless Services profitieren von HPA, Datenbanken von VPA, Event-Driven Workloads von KEDA.
Der sicherste Einstieg: VPA im Off-Modus auf alle Deployments, HPA mit konservativem Behavior auf die wichtigsten Services, Cluster Autoscaler mit genug Spielraum nach oben. Testen mit echten Lastprofilen, bevor die Skalierung produktiv geht.
Falls Sie Unterstuetzung bei der Skalierungs-Strategie fuer Ihre Kubernetes-Umgebung brauchen, stehen wir unter /kontakt fuer ein Beratungsgespraech zur Verfuegung.
Kubernetes-Beratung gesucht?
Wir helfen deutschen Unternehmen bei der Kubernetes-Implementierung, Migration und Optimierung. DSGVO-konform und praxiserprobt.
📖 Verwandte Artikel
Weitere interessante Beiträge zu ähnlichen Themen
Capacity Planning: Kubernetes-Autoscaling richtig nutzen
HPA, VPA, Cluster Autoscaler und KEDA kombinieren für Capacity Planning in Kubernetes. Mit Praxisbeispielen und Entscheidungshilfe.
Kubernetes HPA konfigurieren: Autoscaling Tutorial
Kubernetes Horizontal Pod Autoscaler (HPA) einrichten und konfigurieren. YAML-Beispiele für CPU-, Memory- und Custom-Metrics-basiertes Autoscaling.
Kubernetes Autoscaling: Kosten sparen mit HPA, VPA und Karpenter
Mit Kubernetes Autoscaling 25-40% Cloud-Kosten sparen. HPA, VPA und Cluster Autoscaler richtig konfigurieren, Überprovisionierung erkennen und beseitigen.
Kubernetes Autoscaling: HPA, VPA und Cluster Autoscaler Guide
HPA, VPA und Cluster Autoscaler richtig kombinieren. Wann welchen Autoscaler nutzen, Konfiguration mit YAML-Beispielen und typische Fallstricke vermeiden.
Custom Metrics: Kubernetes-Autoscaling mit Prometheus
HPA mit Custom Metrics aus Prometheus konfigurieren. Prometheus Adapter installieren, Metriken registrieren und Pods nach Requests pro Sekunde skalieren.