- Authors

- Name
- Phillip Pham
- @ddppham
TL;DR
- YOLO-Inference laeuft als Kubernetes-Deployment mit GPU-Requests ueber das NVIDIA Device Plugin. Jeder Pod fordert exakt eine GPU an.
- Horizontal Pod Autoscaler (HPA) skaliert Inference-Pods basierend auf Custom Metrics wie GPU-Auslastung oder Queue-Laenge statt CPU.
- Triton Inference Server oder TorchServe als Model-Serving-Layer entkoppeln das Modell vom Applikationscode und ermoeglichen A/B-Testing zwischen Modellversionen.
- Fuer Latenz-sensitive Anwendungen (unter 100ms pro Frame) ist ein dedizierter GPU-Node-Pool mit Taints der sauberste Ansatz.
- MinIO als S3-kompatibler Object Store im Cluster speichert Modell-Artefakte und Inference-Ergebnisse On-Premise.
Ausgangslage: Warum YOLO auf Kubernetes
YOLO (You Only Look Once) ist der De-facto-Standard fuer Echtzeit-Objekterkennung. YOLOv8 und seine Nachfolger erreichen bei 640x640 Pixel Eingabegroesse Inferenzzeiten von 5-15ms auf einer NVIDIA T4 -- schnell genug fuer 30fps Video-Streams.
Das Problem beginnt, wenn die Loesung skalieren soll. Ein einzelner GPU-Server mit einem Python-Skript funktioniert im Labor. In der Produktion braucht man Hochverfuegbarkeit, automatisches Scaling bei Lastspitzen, Rolling Updates ohne Downtime und reproduzierbare Deployments. Genau das liefert Kubernetes.
Typische Anwendungsfaelle, die wir in der Praxis sehen: Qualitaetskontrolle auf Fertigungslinien (Kratzer, Risse, Fehlteile), automatische Inventarisierung in der Logistik, Sicherheitsmonitoring auf Betriebsgelaenden und Anomalie-Erkennung in der Lebensmittelproduktion.
GPU-Setup: NVIDIA Device Plugin und Node-Konfiguration
Bevor Kubernetes GPUs an Pods vergeben kann, muss das NVIDIA Device Plugin installiert sein. Es registriert GPUs als Extended Resources (nvidia.com/gpu) beim Kubelet. Die Installation laeuft als DaemonSet.
Voraussetzungen auf den GPU-Nodes:
- NVIDIA-Treiber (mindestens 525.x fuer CUDA 12)
- NVIDIA Container Toolkit (ehemals nvidia-docker2)
- containerd als Container Runtime mit NVIDIA-Runtime konfiguriert
# NVIDIA Device Plugin als DaemonSet installieren
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.5/nvidia-device-plugin.yml
# Pruefen ob GPUs erkannt werden
kubectl describe node gpu-worker-01 | grep -A5 "Allocatable"
# Erwartete Ausgabe enthaelt: nvidia.com/gpu: 2
# GPU-Nodes labeln fuer gezielte Scheduling-Entscheidungen
kubectl label nodes gpu-worker-01 gpu-worker-02 \
accelerator=nvidia-t4 \
node-role=gpu-inference
# Taint setzen, damit nur GPU-Workloads hier landen
kubectl taint nodes gpu-worker-01 gpu-worker-02 \
gpu-only=true:NoSchedule
Der Taint stellt sicher, dass keine CPU-only-Pods auf den teuren GPU-Nodes landen. Inference-Pods muessen eine entsprechende Toleration mitbringen. Details zum GPU-Cluster-Setup beschreibt der Artikel GPU-Workloads auf Kubernetes.
Deployment-Manifest: YOLO Inference Service
Das folgende Manifest deployt einen YOLO-Inference-Service mit FastAPI als HTTP-Endpunkt. Es nutzt eine GPU, hat Health Checks und ist fuer Rolling Updates konfiguriert.
apiVersion: apps/v1
kind: Deployment
metadata:
name: yolo-inference
namespace: cv-production
labels:
app: yolo-inference
model-version: yolov8s-v2
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: yolo-inference
template:
metadata:
labels:
app: yolo-inference
model-version: yolov8s-v2
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
tolerations:
- key: "gpu-only"
operator: "Equal"
value: "true"
effect: "NoSchedule"
nodeSelector:
accelerator: nvidia-t4
containers:
- name: inference
image: registry.internal/cv/yolo-serve:2.1.0
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "2"
memory: "4Gi"
nvidia.com/gpu: 1
limits:
cpu: "4"
memory: "8Gi"
nvidia.com/gpu: 1
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 15
env:
- name: MODEL_PATH
value: "/models/yolov8s-custom.pt"
- name: CONFIDENCE_THRESHOLD
value: "0.65"
- name: DEVICE
value: "cuda:0"
- name: MAX_BATCH_SIZE
value: "8"
volumeMounts:
- name: model-store
mountPath: /models
readOnly: true
initContainers:
- name: model-loader
image: registry.internal/cv/model-loader:1.0.0
command: ["python", "download_model.py"]
env:
- name: S3_ENDPOINT
value: "minio.storage.svc.cluster.local:9000"
- name: MODEL_BUCKET
value: "cv-models"
- name: MODEL_KEY
value: "yolov8s-custom-v2.pt"
volumeMounts:
- name: model-store
mountPath: /models
volumes:
- name: model-store
emptyDir:
sizeLimit: 2Gi
---
apiVersion: v1
kind: Service
metadata:
name: yolo-inference
namespace: cv-production
spec:
selector:
app: yolo-inference
ports:
- port: 80
targetPort: 8080
name: http
type: ClusterIP
Ein paar Design-Entscheidungen, die hier wichtig sind:
initContainer fuer Model Loading. Das Modell wird beim Pod-Start aus MinIO geladen, nicht ins Container-Image eingebaut. Das haelt die Images klein (unter 2GB statt 5GB+) und erleichtert den Modell-Wechsel ohne Image-Rebuild.
maxUnavailable: 0. Waehrend eines Rolling Updates bleibt immer mindestens die volle Replica-Anzahl verfuegbar. Es wird erst ein neuer Pod hochgefahren, bevor ein alter terminiert wird.
Separate readiness- und liveness-Probe. Das Laden des YOLO-Modells in den GPU-Speicher dauert 15-30 Sekunden. Die readinessProbe hat ein laengeres initialDelaySeconds, damit der Pod erst Traffic bekommt, wenn das Modell tatsaechlich geladen ist.
Autoscaling: HPA mit Custom Metrics
Standard-HPA skaliert nach CPU-Auslastung. Fuer GPU-Workloads ist das unbrauchbar, weil die CPU oft idle ist, waehrend die GPU am Limit laeuft. Stattdessen skalieren wir nach einer Custom Metric: der Anzahl wartender Inference-Requests in der Queue.
Voraussetzung: Prometheus Adapter ist installiert und exponiert Custom Metrics an die Kubernetes Metrics API.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: yolo-inference-hpa
namespace: cv-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: yolo-inference
minReplicas: 2
maxReplicas: 6
metrics:
- type: Pods
pods:
metric:
name: inference_queue_depth
target:
type: AverageValue
averageValue: "5"
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 1
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 120
Die scaleDown.stabilizationWindowSeconds: 300 verhindern, dass nach einem kurzen Traffic-Dip sofort Pods entfernt werden. GPU-Pods brauchen 30+ Sekunden zum Starten -- schnelles Hoch- und Runterskalieren ist kontraproduktiv. Mehr zu HPA-Konfigurationen unter Kubernetes HPA.
Modellversionierung und A/B-Testing
In der Praxis laeuft selten nur ein Modell. Oft wird eine neue Modellversion parallel zur bestehenden getestet, bevor sie vollstaendig uebernommen wird. Das laesst sich in Kubernetes ueber zwei Deployments mit unterschiedlichen Labels und gewichteter Traffic-Verteilung ueber einen Ingress Controller oder Service Mesh loesen.
| Ansatz | Vorteile | Nachteile |
|---|---|---|
| Separate Deployments + Ingress-Weights | Einfach, keine Extra-Tools | Manuelles Traffic-Splitting |
| Triton Inference Server | Multi-Model, Batching, Metrics | Komplexeres Setup |
| Seldon Core / KServe | Kubernetes-native, CRDs | Hoher Overhead fuer kleine Setups |
| Canary Deployment (Argo Rollouts) | Automatisiert, Metric-basiert | Setzt Argo voraus |
Fuer die meisten mittelstaendischen Setups empfiehlt sich der Ansatz mit separaten Deployments und einem Label-basierten Service-Switch. Wer mehr Automatisierung will, findet Details unter Canary Deployments.
Storage: Modelle und Ergebnisse
Zwei Storage-Anforderungen dominieren:
Modell-Artefakte (Read-Heavy). YOLO-Modelle sind typisch 20-200MB gross. Sie muessen schnell lesbar sein, werden aber selten geschrieben. MinIO als S3-kompatibler Object Store im Cluster ist ideal. Der initContainer im Deployment laedt das Modell beim Pod-Start.
Inference-Ergebnisse (Write-Heavy). Jeder erkannte Defekt, jedes annotierte Bild muss gespeichert werden -- fuer Audit-Trails, Retraining und Qualitaetsberichte. Hier bietet sich eine Kombination aus PostgreSQL fuer strukturierte Metadaten (Bounding Boxes, Confidence Scores, Timestamps) und MinIO fuer die annotierten Bilder an.
Wer tiefer in Storage-Optionen einsteigen will, findet Vergleiche unter Kubernetes Storage.
Monitoring: Die richtigen Metriken
Ein Inference-Service ohne Monitoring ist ein Blindflug. Folgende Metriken sollten in Prometheus erfasst und in Grafana visualisiert werden:
| Metrik | Beschreibung | Alert-Schwelle |
|---|---|---|
inference_latency_seconds (p95) | Zeit pro Inference-Request | ueber 200ms |
inference_queue_depth | Wartende Requests | ueber 20 (skalieren) |
gpu_utilization_percent | GPU-Kernauslastung | ueber 95% (sustained) |
gpu_memory_used_bytes | Genutzter GPU-Speicher | ueber 90% der Kapazitaet |
model_confidence_avg | Durchschnittlicher Confidence Score | unter 0.4 (Modell-Drift) |
inference_errors_total | Fehlgeschlagene Requests | ueber 1% der Gesamtanfragen |
Der Confidence-Score-Durchschnitt ist besonders nuetzlich: Wenn er ueber Zeit sinkt, deutet das auf Model Drift hin -- die Eingabedaten haben sich veraendert und das Modell muss nachtrainiert werden. Ein Artikel zu Monitoring-Best-Practices findet sich unter Kubernetes Monitoring.
YOLO vs. SAM: Wann welches Modell
| Kriterium | YOLO (v8/v9) | SAM (Segment Anything) |
|---|---|---|
| Aufgabe | Bounding Box Detection | Instanz-Segmentierung |
| Inferenzzeit (T4 GPU) | 5-15ms | 50-200ms |
| Output | Klasse + Box + Confidence | Pixelgenaue Maske |
| Typischer Use Case | Defekt-Erkennung, Zaehlung | Praezise Konturerfassung |
| Modellgroesse | 20-200MB | 300MB-2.5GB |
| GPU-Speicherbedarf | 1-4GB | 4-12GB |
| Trainingsaufwand | Gering (Transfer Learning) | Hoch (Foundation Model) |
In der Praxis wird oft eine Kombination eingesetzt: YOLO fuer die schnelle Erkennung und Filterung, SAM fuer die praezise Segmentierung der relevanten Detektionen. Das spart GPU-Ressourcen, weil SAM nur auf den relevanten Bildausschnitten laeuft.
Kostenvergleich: On-Premise vs. Cloud GPU
| Posten | On-Premise (2x T4) | Cloud (2x T4 Spot) | Cloud (2x T4 On-Demand) |
|---|---|---|---|
| Hardware/Instanz | 5.000 EUR einmalig | ca. 400 EUR/Monat | ca. 1.200 EUR/Monat |
| Kubernetes Cluster | eigenes Ops-Team | Managed (GKE/EKS/AKS) | Managed |
| Stromkosten | ca. 100 EUR/Monat | inklusive | inklusive |
| Amortisation (Break-Even) | - | - | ca. 5 Monate vs. On-Demand |
| Verfuegbarkeitsrisiko | keines (lokal) | Spot-Unterbrechungen | keines |
| Datensouveraenitaet | maximal | abhaengig vom Anbieter | abhaengig vom Anbieter |
Fuer DSGVO-sensitive Workloads (Kamerabilder mit Personen im Bild) ist On-Premise oder ein Rechenzentrum innerhalb der EU oft die pragmatischste Loesung. Wer die Daten vorher anonymisiert (Gesichter blurren, Kennzeichen maskieren), hat mehr Flexibilitaet bei der Standortwahl.
Haeufige Fallstricke
GPU-Speicher-Fragmentierung. Wenn ein Pod abstuerzt und der GPU-Speicher nicht sauber freigegeben wird, kann der naechste Pod keine GPU allozieren. Loesung: nvidia-smi Monitoring und im Notfall Node-Drain.
Zu grosse Container-Images. Ein PyTorch-Image mit CUDA-Runtime wird schnell 8-10GB gross. Das verlaengert Pod-Startzeiten auf Minuten. Nutzen Sie Multi-Stage-Builds und schlanke Base-Images (z.B. nvcr.io/nvidia/pytorch statt vollem Ubuntu).
Kein Batching. Einzelne Bilder durch das Modell zu schicken ist ineffizient. Batching (mehrere Bilder gleichzeitig) verbessert den GPU-Durchsatz um den Faktor 3-5x. Triton Inference Server bringt Batching out-of-the-box mit.
Model-Serving im Applikationscode. Das Modell direkt in der FastAPI-App zu laden funktioniert, skaliert aber schlecht. Fuer Produktionssysteme ist ein dedizierter Model-Serving-Layer (Triton, TorchServe) die bessere Wahl, weil er Modellversionierung, Batching und Multi-Model-Support mitbringt.
Fazit und naechste Schritte
YOLO auf Kubernetes zu betreiben ist keine Raketenwissenschaft, erfordert aber ein paar Entscheidungen, die man frueh richtig treffen sollte: GPU-Node-Konfiguration, Model-Serving-Ansatz, Autoscaling-Strategie und Storage-Architektur.
Der empfohlene Einstieg: Ein einzelnes YOLO-Modell als Deployment mit einem GPU-Pod, MinIO als Model Store, Prometheus fuer Metriken. Wenn das stabil laeuft, kommen HPA, Canary Deployments und ein dedizierter Model-Serving-Layer dazu.
Fuer eine gemeinsame Evaluation Ihres Use Cases oder Unterstuetzung beim Setup koennen Sie uns ueber die Kontaktseite erreichen.
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
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.
vLLM auf Kubernetes: GPU-effiziente LLM-Inferenz Guide
vLLM auf Kubernetes deployen, GPU-Speicher mit PagedAttention optimal nutzen und Auto-Scaling für LLM-Workloads konfigurieren.
GPU-Workloads in Kubernetes: Scheduling, Sharing und Kosten
GPU-Auslastung in Kubernetes von 30% auf über 70% steigern mit NVIDIA Device Plugin, MIG, Time-Slicing und Volcano Scheduler.
KI auf Kubernetes im Healthcare: ML-Pipelines produktiv betreiben
Wie ein deutsches Healthcare-Unternehmen 50+ ML-Modelle DSGVO-konform auf Kubernetes betreibt und die Diagnose-Genauigkeit um 35% verbesserte.
GPU Workloads auf Kubernetes: NVIDIA Device Plugin einrichten
GPU-Workloads auf Kubernetes betreiben: NVIDIA Device Plugin installieren, GPU-Scheduling mit Taints und Affinity konfigurieren und mit DCGM überwachen.