- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
- Standard-HPA skaliert nur auf CPU/Memory -- fuer GPUs braucht ihr Custom Metrics (DCGM Exporter + Prometheus Adapter)
- KEDA vereinfacht event-basiertes Scaling und unterstuetzt GPU-Metriken, Queue-Laengen und Cron-Schedules
- Spot/Preemptible Instances sparen 60-70% bei GPU-Kosten, erfordern aber Checkpointing
- GPU-Nodes immer mit Taints und Tolerations schuetzen, damit nur GPU-Workloads dort landen
- Time-Slicing und MIG (Multi-Instance GPU) erhoehen die Auslastung bei Inference-Workloads
Das Problem: GPUs sind teuer und schlecht ausgelastet
Eine einzelne NVIDIA A100 kostet bei Cloud-Providern zwischen 2-4 EUR/Stunde. Trotzdem liegen GPU-Auslastungsraten in der Praxis oft unter 30%. Die Gruende:
- Training-Jobs laufen nur zu bestimmten Zeiten
- Inference-Services haben stark schwankende Last
- Standard-Autoscaling reagiert nicht auf GPU-spezifische Metriken
- Teams provisionieren "auf Verdacht" statt bedarfsgerecht
Die Loesung: GPU-aware Autoscaling, das auf tatsaechliche GPU-Auslastung reagiert.
GPU-Metriken verfuegbar machen
Bevor ihr skalieren koennt, muss Prometheus GPU-Metriken kennen. Dafuer sorgt der NVIDIA DCGM Exporter.
# NVIDIA Device Plugin installieren (falls noch nicht vorhanden)
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.3/nvidia-device-plugin.yml
# DCGM Exporter fuer GPU-Metriken
helm repo add gpu-helm-charts https://nvidia.github.io/dcgm-exporter/helm-charts
helm repo update
helm install dcgm-exporter gpu-helm-charts/dcgm-exporter \
--namespace gpu-monitoring \
--create-namespace \
--set serviceMonitor.enabled=true
Damit stehen Metriken wie DCGM_FI_DEV_GPU_UTIL (GPU-Auslastung), DCGM_FI_DEV_FB_USED (GPU-Memory) und DCGM_FI_DEV_POWER_USAGE (Stromverbrauch) in Prometheus zur Verfuegung.
HPA mit Custom GPU Metrics
Der Horizontal Pod Autoscaler kann ueber den Prometheus Adapter auf beliebige Prometheus-Metriken reagieren -- auch GPU-Metriken.
Prometheus Adapter konfigurieren
# prometheus-adapter-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-adapter-config
namespace: monitoring
data:
config.yaml: |
rules:
- seriesQuery: 'DCGM_FI_DEV_GPU_UTIL{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "DCGM_FI_DEV_GPU_UTIL"
as: "gpu_utilization"
metricsQuery: 'avg(DCGM_FI_DEV_GPU_UTIL{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
- seriesQuery: 'DCGM_FI_DEV_FB_USED{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "DCGM_FI_DEV_FB_USED"
as: "gpu_memory_used"
metricsQuery: 'avg(DCGM_FI_DEV_FB_USED{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
HPA mit GPU-Auslastung
# hpa-gpu-inference.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: inference-service-hpa
namespace: ml-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: inference-service
minReplicas: 1
maxReplicas: 8
metrics:
- type: Pods
pods:
metric:
name: gpu_utilization
target:
type: AverageValue
averageValue: "70" # Skaliert hoch ab 70% GPU-Auslastung
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 2
periodSeconds: 120 # Max 2 Pods alle 2 Min
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 300 # Max 1 Pod alle 5 Min runter
Warum das behavior-Feld wichtig ist: GPU-Pods brauchen oft 1-2 Minuten zum Starten (Modell laden, CUDA initialisieren). Zu aggressives Hoch- oder Runterskalieren fuehrt zu Thrashing. Die stabilizationWindowSeconds verhindern das.
KEDA fuer event-basiertes GPU-Scaling
KEDA (Kubernetes Event-Driven Autoscaling) ist flexibler als der Standard-HPA und unterstuetzt ueber 60 Scaler -- darunter Prometheus, RabbitMQ, Kafka und Cron.
# KEDA installieren
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda \
--namespace keda \
--create-namespace
KEDA ScaledObject: Queue-basiertes Scaling
Typischer ML-Use-Case: Inference-Requests landen in einer Queue. KEDA skaliert basierend auf der Queue-Laenge.
# keda-scaledobject-inference.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: inference-queue-scaler
namespace: ml-production
spec:
scaleTargetRef:
name: inference-worker
minReplicaCount: 0 # Scale-to-Zero moeglich!
maxReplicaCount: 10
pollingInterval: 15
cooldownPeriod: 300
triggers:
- type: prometheus
metadata:
serverAddress: http://monitoring-kube-prometheus-prometheus.monitoring:9090
metricName: inference_queue_length
query: 'sum(rabbitmq_queue_messages{queue="inference-requests"})'
threshold: "5" # 1 Pod pro 5 Messages in der Queue
- type: cron
metadata:
timezone: Europe/Berlin
start: "0 8 * * 1-5" # Mo-Fr 08:00 mindestens 2 Replicas
end: "0 20 * * 1-5" # Mo-Fr 20:00 zurueck auf Minimum
desiredReplicas: "2"
KEDA ScaledObject: GPU-Auslastung
# keda-scaledobject-gpu.yaml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: gpu-utilization-scaler
namespace: ml-production
spec:
scaleTargetRef:
name: training-job-dispatcher
minReplicaCount: 1
maxReplicaCount: 6
triggers:
- type: prometheus
metadata:
serverAddress: http://monitoring-kube-prometheus-prometheus.monitoring:9090
metricName: avg_gpu_utilization
query: 'avg(DCGM_FI_DEV_GPU_UTIL{namespace="ml-production"})'
threshold: "80"
Vorteil von KEDA gegenueber Standard-HPA: Scale-to-Zero. Wenn keine Inference-Requests anstehen, laeuft kein einziger GPU-Pod -- das spart erhebliche Kosten.
Kostenoptimierung: Spot Instances fuer GPUs
Spot/Preemptible Instances bieten massive Einsparungen, erfordern aber Vorbereitung.
Kostenvergleich GPU-Instanzen (Stand 2026, eu-central-1)
| GPU-Typ | On-Demand/Stunde | Spot/Stunde | Ersparnis | Geeignet fuer |
|---|---|---|---|---|
| NVIDIA T4 | ~0,60 EUR | ~0,20 EUR | ~67% | Inference, leichtes Training |
| NVIDIA A10G | ~1,50 EUR | ~0,50 EUR | ~67% | Mixed Workloads |
| NVIDIA A100 (40G) | ~3,80 EUR | ~1,30 EUR | ~66% | Grosses Training |
| NVIDIA H100 | ~8,00 EUR | ~3,00 EUR | ~63% | LLM-Training, HPC |
Spot-Nodes im Cluster einrichten
# gpu-spot-nodepool.yaml (Beispiel fuer Karpenter)
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: gpu-spot-pool
spec:
template:
spec:
requirements:
- key: "karpenter.sh/capacity-type"
operator: In
values: ["spot"]
- key: "node.kubernetes.io/instance-type"
operator: In
values: ["g5.xlarge", "g5.2xlarge", "g5.4xlarge"]
- key: "nvidia.com/gpu"
operator: Exists
taints:
- key: nvidia.com/gpu
value: "true"
effect: NoSchedule
disruption:
consolidationPolicy: WhenUnderutilized
expireAfter: 720h
limits:
cpu: "128"
memory: 512Gi
nvidia.com/gpu: "16"
GPU-Workload mit Spot-Tolerierung und Checkpointing
# training-job-spot.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: model-training
namespace: ml-training
spec:
backoffLimit: 3
template:
spec:
tolerations:
- key: nvidia.com/gpu
operator: Equal
value: "true"
effect: NoSchedule
nodeSelector:
karpenter.sh/capacity-type: spot
containers:
- name: trainer
image: registry.example.com/ml-trainer:v2.1
resources:
requests:
nvidia.com/gpu: 1
memory: "16Gi"
cpu: "4"
limits:
nvidia.com/gpu: 1
memory: "32Gi"
env:
- name: CHECKPOINT_DIR
value: "/checkpoints"
- name: CHECKPOINT_INTERVAL
value: "300" # Alle 5 Min speichern
volumeMounts:
- name: checkpoints
mountPath: /checkpoints
volumes:
- name: checkpoints
persistentVolumeClaim:
claimName: training-checkpoints # Ueberlebt Pod-Eviction
terminationGracePeriodSeconds: 120
restartPolicy: OnFailure
Checkpointing ist Pflicht bei Spot: Spot-Instances koennen jederzeit entzogen werden. Ohne regelmaessige Checkpoints verliert ihr den gesamten Trainingsfortschritt.
GPU-Auslastung verbessern: Time-Slicing und MIG
Fuer Inference-Workloads, die keine volle GPU benoetigen, gibt es zwei Ansaetze:
NVIDIA Time-Slicing
Mehrere Pods teilen sich eine GPU ueber zeitliches Multiplexing. Einfach einzurichten, aber ohne Memory-Isolation.
# time-slicing-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nvidia-device-plugin-config
namespace: kube-system
data:
config.yaml: |
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4 # 4 Pods pro GPU
NVIDIA MIG (Multi-Instance GPU, nur A100/H100)
Partitioniert die GPU physisch -- mit echter Memory- und Compute-Isolation.
| Feature | Time-Slicing | MIG |
|---|---|---|
| GPU-Support | Alle NVIDIA GPUs | A100, H100 |
| Memory-Isolation | Nein | Ja |
| Compute-Isolation | Nein | Ja |
| Max. Partitionen | Beliebig (Software) | 7 (Hardware) |
| Geeignet fuer | Dev/Test, leichte Inference | Production Inference, Multi-Tenant |
Taints, Tolerations und Node Affinity
GPU-Nodes sollten immer mit einem Taint geschuetzt werden, damit regulaere Pods nicht dort scheduled werden.
# gpu-deployment-with-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-service
namespace: ml-production
spec:
replicas: 2
selector:
matchLabels:
app: inference-service
template:
metadata:
labels:
app: inference-service
spec:
tolerations:
- key: nvidia.com/gpu
operator: Equal
value: "true"
effect: NoSchedule
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nvidia.com/gpu.product
operator: In
values:
- NVIDIA-A10G
- NVIDIA-T4
containers:
- name: inference
image: registry.example.com/inference:v1.5
resources:
requests:
nvidia.com/gpu: 1
limits:
nvidia.com/gpu: 1
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: metrics
Monitoring: GPU-spezifische Dashboards
Ergaenzend zu eurem Kubernetes Monitoring Stack solltet ihr GPU-spezifische Metriken ueberwachen:
# gpu-prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: gpu-alerts
namespace: monitoring
spec:
groups:
- name: gpu-alerts
rules:
- alert: GPUHighTemperature
expr: DCGM_FI_DEV_GPU_TEMP > 85
for: 5m
labels:
severity: warning
annotations:
summary: "GPU Temperatur ueber 85C auf {{ $labels.instance }}"
- alert: GPUMemoryNearFull
expr: (DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_FREE) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "GPU Memory fast voll auf {{ $labels.instance }}"
- alert: GPULowUtilization
expr: avg_over_time(DCGM_FI_DEV_GPU_UTIL[1h]) < 10
for: 2h
labels:
severity: info
annotations:
summary: "GPU auf {{ $labels.instance }} ist seit 2h kaum ausgelastet -- Kosten pruefen"
Checkliste: GPU-Scaling produktionsreif
- NVIDIA Device Plugin und DCGM Exporter deployed
- GPU-Metriken in Prometheus verfuegbar
- Taints auf GPU-Nodes gesetzt
- HPA oder KEDA mit GPU-Metriken konfiguriert
- Spot-Instances evaluiert und Checkpointing implementiert
- Time-Slicing oder MIG fuer Inference-Workloads geprueft
- GPU-spezifische Alerts eingerichtet
- Kosten-Monitoring (z.B. Kubecost) fuer GPU-Workloads aktiv
Weitergehende Ressourcen
- GPU Monitoring in Kubernetes -- Detailliertes GPU-Observability-Setup
- Kubernetes GPU Cluster -- Grundlagen fuer GPU-Node-Pools
- Kubernetes Autoscaling -- Allgemeine HPA/VPA/Cluster-Autoscaler-Patterns
- MLOps auf Kubernetes -- End-to-End ML-Pipelines
GPU-Workloads optimieren? Wir helfen Teams dabei, ML-Infrastruktur auf Kubernetes kosteneffizient und skalierbar aufzubauen -- von der GPU-Node-Strategie bis zur Autoscaling-Konfiguration. Kontakt aufnehmen fuer ein unverbindliches Erstgespraech.
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.
KEDA Autoscaling: Event-driven Skalierung einrichten
KEDA einrichten für Event-driven Autoscaling mit Kafka, RabbitMQ und Azure Queue inklusive Scale-to-Zero und Kostenoptimierung in Kubernetes.
KI-Qualitätskontrolle auf Kubernetes mit YOLO
KI-gestützte Qualitätskontrolle auf Kubernetes: Kamera-zu-Inference-Pipeline mit YOLO und Triton Inference Server für automatisierte Defekterkennung.
Kubernetes Skalierung: HPA, VPA und KEDA kombinieren
Kubernetes Skalierungs-Patterns im Überblick: HPA, VPA, Cluster Autoscaler und KEDA richtig kombinieren für Multi-Dimensional Autoscaling.
YOLO Inference auf Kubernetes: GPU-Scheduling und Autoscaling
YOLO-Modelle als skalierbare Inference-Services auf Kubernetes deployen: NVIDIA Device Plugin, GPU-Scheduling, HPA mit Custom Metrics und Triton Serving.