- Authors

- Name
- Phillip Pham
- @ddppham
NVIDIA Triton Inference Server auf Kubernetes: Setup, Tuning und Betrieb
TL;DR
- Triton hosted mehrere Modelle verschiedener Frameworks (PyTorch, TensorFlow, ONNX) auf einem einzigen Server -- das spart GPU-Ressourcen erheblich
- Dynamic Batching ist der groesste Performance-Hebel: GPU-Auslastung steigt typischerweise von 20-30% auf 60-80%
- Der NVIDIA GPU Operator automatisiert Treiber- und CUDA-Setup auf euren Kubernetes-Nodes
- HPA-Scaling fuer Triton sollte auf Custom Metrics (Inferenz-Queue-Laenge) basieren, nicht auf CPU
- Startet mit einem einzelnen Modell, messt Latenz und Throughput, und optimiert iterativ
Warum Triton auf Kubernetes?
Model Serving in Produktion ist ein anderes Tier als Model Training. Waehrend beim Training Batch-Jobs laufen, die irgendwann fertig werden, muss Inference dauerhaft verfuegbar sein, mit vorhersagbarer Latenz und der Faehigkeit, mit schwankender Last umzugehen.
Triton Inference Server loest drei zentrale Probleme:
- Multi-Framework-Support: Euer Data-Science-Team trainiert mit PyTorch, ein zugekauftes Modell ist TensorFlow, und der Legacy-Classifier laeuft als ONNX. Triton hosted alle drei auf derselben Infrastruktur.
- GPU-Effizienz: Ohne Batching verarbeitet eine GPU einzelne Requests seriell und ist die meiste Zeit idle. Triton sammelt Requests und verarbeitet sie als Batch.
- Standardisierte API: Jedes Modell bekommt automatisch REST- und gRPC-Endpoints. Keine Custom-Server pro Modell.
Kubernetes bringt dazu die Orchestrierungsschicht: Rolling Updates fuer Modell-Versionen, Autoscaling bei Lastspitzen und Self-Healing bei Pod-Ausfaellen. Diese Kombination ist der Grund, warum Triton-auf-Kubernetes zum De-facto-Standard fuer Inference in Produktion geworden ist.
Wer noch keinen GPU-faehigen Cluster hat, findet im Artikel zu GPU-Clustern auf Kubernetes eine Anleitung fuer den Aufbau.
Voraussetzungen: GPU Operator installieren
Bevor Triton laufen kann, muessen eure Kubernetes-Nodes GPUs bereitstellen. Der NVIDIA GPU Operator automatisiert das komplett -- keine manuelle Treiberinstallation auf den Nodes noetig.
# NVIDIA GPU Operator via Helm installieren
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm repo update
helm install gpu-operator nvidia/gpu-operator \
--namespace gpu-operator \
--create-namespace \
--set driver.enabled=true \
--set toolkit.enabled=true \
--set devicePlugin.enabled=true \
--set dcgmExporter.enabled=true \
--wait
# Verifizieren, dass GPUs erkannt werden
kubectl get nodes -o json | \
jq '.items[] | {name: .metadata.name, gpus: .status.capacity["nvidia.com/gpu"]}'
Der dcgmExporter ist optional aber empfehlenswert: Er exportiert GPU-Metriken (Auslastung, Temperatur, Speicher) als Prometheus-Metriken. Das braucht ihr spaeter fuer Monitoring und HPA.
Details zum GPU-Monitoring findet ihr unter GPU Monitoring auf Kubernetes.
Triton Deployment: Basis-Setup
Das folgende Deployment ist ein produktionsnaher Startpunkt. Es mounted ein PVC als Model Repository und konfiguriert Health Checks fuer alle drei Triton-Ports.
apiVersion: apps/v1
kind: Deployment
metadata:
name: triton-inference-server
labels:
app: triton-inference
spec:
replicas: 1
selector:
matchLabels:
app: triton-inference
template:
metadata:
labels:
app: triton-inference
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8002"
spec:
containers:
- name: triton
image: nvcr.io/nvidia/tritonserver:24.08-py3
args:
- tritonserver
---model-repository=/models
---log-verbose=0
---strict-model-config=false
ports:
- containerPort: 8000
name: http
- containerPort: 8001
name: grpc
- containerPort: 8002
name: metrics
resources:
requests:
nvidia.com/gpu: 1
memory: "4Gi"
cpu: "2"
limits:
nvidia.com/gpu: 1
memory: "8Gi"
cpu: "4"
livenessProbe:
httpGet:
path: /v2/health/live
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /v2/health/ready
port: http
initialDelaySeconds: 30
periodSeconds: 10
volumeMounts:
- name: model-repo
mountPath: /models
volumes:
- name: model-repo
persistentVolumeClaim:
claimName: triton-models-pvc
---
apiVersion: v1
kind: Service
metadata:
name: triton-inference
labels:
app: triton-inference
spec:
selector:
app: triton-inference
ports:
- port: 8000
targetPort: 8000
name: http
- port: 8001
targetPort: 8001
name: grpc
- port: 8002
targetPort: 8002
name: metrics
type: ClusterIP
Wichtige Details:
--strict-model-config=falselaedt Modelle automatisch, sobald sie im Repository erscheinen. Fuer Produktion isttruesicherer, da dann nur explizit konfigurierte Modelle geladen werden.- Die Probes nutzen Tritons eingebaute Health-Endpoints.
liveprueeft, ob der Prozess laeuft.readyprueeft, ob mindestens ein Modell geladen ist. - Memory-Limits grosszuegig setzen: Triton laedt alle Modelle beim Start in den RAM. Bei vielen Modellen kann das schnell mehrere GB brauchen.
Fuer die Storage-Konfiguration des Model Repository gibt es verschiedene Optionen -- von NFS ueber Ceph bis zu Cloud-Storage. Eine Uebersicht findet ihr unter Kubernetes Storage.
Dynamic Batching konfigurieren
Dynamic Batching ist der groesste einzelne Performance-Hebel. Ohne Batching verarbeitet Triton jeden Request sofort einzeln. Mit Batching wartet es kurz (typischerweise 100-500 Mikrosekunden), sammelt mehrere Requests und schickt sie als einen Batch an die GPU.
Die Konfiguration erfolgt pro Modell in der config.pbtxt:
name: "image_classifier"
platform: "onnxruntime_onnx"
max_batch_size: 32
input [
{
name: "input"
data_type: TYPE_FP32
dims: [ 3, 224, 224 ]
}
]
output [
{
name: "output"
data_type: TYPE_FP32
dims: [ 1000 ]
}
]
dynamic_batching {
preferred_batch_size: [ 8, 16, 32 ]
max_queue_delay_microseconds: 200
}
instance_group [
{
count: 2
kind: KIND_GPU
}
]
Die drei Parameter, die ihr tunen muesst:
| Parameter | Effekt | Empfehlung |
|---|---|---|
max_batch_size | Maximale Batch-Groesse | Abhaengig vom GPU-Memory; 16-64 ist ein guter Start |
preferred_batch_size | Triton wartet bis einer dieser Werte erreicht ist | 2-3 Werte, aufsteigend |
max_queue_delay_microseconds | Maximale Wartezeit fuer Batching | 100-500 fuer Latenz-kritisch, 1000-5000 fuer Throughput |
Die instance_group mit count: 2 bedeutet, dass Triton zwei Kopien des Modells gleichzeitig auf der GPU haelt. Das verdoppelt den Throughput auf Kosten von GPU-Memory. Startet mit 1 und erhoeht schrittweise.
Autoscaling: HPA mit Custom Metrics
Standard-HPA auf CPU-Basis funktioniert fuer Triton schlecht, weil die CPU selten der Engpass ist. Stattdessen solltet ihr auf GPU-Auslastung oder Queue-Laenge skalieren.
Voraussetzung ist ein Prometheus Adapter, der Triton-Metriken als Kubernetes Custom Metrics bereitstellt:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: triton-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: triton-inference-server
minReplicas: 1
maxReplicas: 4
metrics:
- type: Pods
pods:
metric:
name: nv_inference_queue_duration_us
target:
type: AverageValue
averageValue: "500" # Skaliert wenn Queue > 500us
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 1
periodSeconds: 120
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 300
Die behavior-Sektion ist wichtig: Scale-Up sollte schnell passieren (60s Stabilisierung), Scale-Down langsam (300s). GPUs sind teuer, aber Latenz-Spikes durch zu wenige Replicas sind teurer.
Fuer eine allgemeine Einfuehrung in Kubernetes Autoscaling gibt es den Artikel zu Autoscaling-Strategien.
Model Repository: Struktur und Versionierung
Triton erwartet eine feste Verzeichnisstruktur im Model Repository:
models/
image_classifier/
config.pbtxt
1/
model.onnx
2/
model.onnx
text_embedder/
config.pbtxt
1/
model.pt
Jedes Unterverzeichnis ist eine Modell-Version. Triton kann mehrere Versionen gleichzeitig laden (konfigurierbar in config.pbtxt). Das ermoeglicht Canary-Deployments: Version 2 bekommt 10% des Traffics, Version 1 den Rest.
Fuer die Modell-Versionierung in der CI/CD-Pipeline bieten sich zwei Ansaetze an:
Ansatz 1: Git-basiert mit DVC. Modelle werden in DVC versioniert, die Pipeline baut ein neues PV-Image bei jeder Aenderung. Einfach, aber langsam bei grossen Modellen.
Ansatz 2: S3-kompatibles Backend. Triton kann direkt von S3 (oder MinIO) laden. Neue Versionen werden in den Bucket gepusht, Triton laedt sie automatisch nach. Schneller, erfordert aber Netzwerk-Konnektivitaet.
Wer den GitOps-Ansatz weiter denken will, findet im Artikel zu MLOps auf Kubernetes weitere Patterns.
Monitoring: Die richtigen Metriken
Triton exportiert nativ Prometheus-Metriken auf Port 8002. Die wichtigsten:
| Metrik | Was sie zeigt | Alert-Schwelle |
|---|---|---|
nv_inference_request_duration_us | End-to-End-Latenz pro Request | P95 > SLA |
nv_inference_queue_duration_us | Wartezeit in der Queue | > 1000us |
nv_inference_request_success | Erfolgreiche Requests | Rate-Drop > 5% |
nv_gpu_utilization | GPU-Auslastung | < 20% (Overprovisioning) oder > 90% (Engpass) |
nv_gpu_memory_used_bytes | GPU-Speicherverbrauch | > 90% vom Limit |
Die Queue Duration ist die frueheste Warnung fuer Kapazitaetsengpaesse. Wenn sie steigt, kommen Requests schneller rein als Triton sie verarbeiten kann. Dann ist es Zeit fuer Scale-Up oder Batching-Optimierung.
Fuer den kompletten Monitoring-Stack (Prometheus, Grafana, Alertmanager) gibt es eine Anleitung unter Kubernetes Observability Stack.
Performance-Vergleich: Batching-Konfigurationen
Um die Auswirkung verschiedener Batching-Settings zu verdeutlichen, hier exemplarische Messwerte fuer ein ResNet-50-Modell auf einer NVIDIA A100:
| Konfiguration | Throughput (infer/s) | P50 Latenz (ms) | P99 Latenz (ms) | GPU-Auslastung |
|---|---|---|---|---|
| Kein Batching, 1 Instance | 120 | 8.2 | 12.1 | 25% |
| Batch 8, delay 200us, 1 Instance | 580 | 9.1 | 18.4 | 62% |
| Batch 16, delay 500us, 2 Instances | 1.100 | 11.3 | 24.7 | 78% |
| Batch 32, delay 1000us, 2 Instances | 1.450 | 16.8 | 38.2 | 84% |
Der Trade-off ist klar: Mehr Batching bedeutet mehr Throughput und hoehere GPU-Auslastung, aber auch hoehere Latenz. Fuer Real-Time-Anwendungen (Chat, Empfehlungen) ist Batch 8-16 mit niedrigem Delay ein guter Kompromiss. Fuer Batch-Verarbeitung (Dokumenten-Klassifikation, Bild-Analyse) kann man aggressiver batchen.
Model Ensembles
Triton unterstuetzt Model Ensembles: Mehrere Modelle werden in einer Pipeline verkettet, ohne Netzwerk-Overhead zwischen den Schritten. Ein typischer Anwendungsfall:
- Preprocessing-Modell (Python Backend): Normalisierung, Tokenisierung
- Inference-Modell (ONNX/PyTorch): Das eigentliche ML-Modell
- Postprocessing-Modell (Python Backend): Label-Mapping, Confidence-Filtering
Die Ensemble-Config in config.pbtxt:
name: "ensemble_pipeline"
platform: "ensemble"
max_batch_size: 16
input [
{ name: "RAW_INPUT" data_type: TYPE_STRING dims: [ 1 ] }
]
output [
{ name: "FINAL_OUTPUT" data_type: TYPE_FP32 dims: [ 10 ] }
]
ensemble_scheduling {
step [
{
model_name: "preprocessor"
model_version: -1
input_map { key: "RAW_INPUT" value: "RAW_INPUT" }
output_map { key: "PROCESSED" value: "PROCESSED" }
},
{
model_name: "classifier"
model_version: -1
input_map { key: "PROCESSED" value: "INPUT" }
output_map { key: "OUTPUT" value: "PREDICTION" }
},
{
model_name: "postprocessor"
model_version: -1
input_map { key: "PREDICTION" value: "PREDICTION" }
output_map { key: "FINAL_OUTPUT" value: "FINAL_OUTPUT" }
}
]
}
Der Vorteil gegenueber separaten Service-Calls: Die Daten bleiben im Triton-Prozess. Kein Netzwerk-Serialisierung/Deserialisierung zwischen den Schritten. Bei Latenz-kritischen Pipelines kann das 5-15ms pro Hop sparen.
Haeufige Fehler beim Triton-Setup
GPU-Memory wird nicht freigegeben. Triton alloziert GPU-Memory beim Modell-Laden und gibt es nicht zurueck. Wenn ihr Modelle loedr und entfernt, kann es zu Memory-Fragmentierung kommen. Workaround: Pod-Restart nach groesseren Modell-Aenderungen.
gRPC statt REST fuer hohen Throughput. REST/JSON hat Serialisierungs-Overhead, besonders bei grossen Tensoren. Fuer Throughput-kritische Anwendungen ist gRPC + Protobuf deutlich effizienter.
Health Checks zu aggressiv. Triton braucht beim Start Zeit, um alle Modelle zu laden. Wenn initialDelaySeconds zu niedrig ist, killt Kubernetes den Pod bevor er ready ist. Bei vielen oder grossen Modellen auf 60-120s setzen.
Kein Resource Limit fuer GPU-Memory. Kubernetes kann GPU-Memory nicht nativ limitieren. Wenn ein Triton-Pod mehr Memory braucht als die GPU hat, crasht er ohne hilfreiche Fehlermeldung. Testet Memory-Verbrauch vorab mit nvidia-smi.
Fazit
Triton Inference Server auf Kubernetes ist ein ausgereifter Stack fuer produktives Model Serving. Der groesste Hebel liegt im Dynamic Batching -- damit allein laesst sich die GPU-Auslastung oft verdreifachen. Zusammen mit dem GPU Operator fuer automatisiertes Node-Setup und HPA auf Custom Metrics bekommt ihr eine Inference-Plattform, die mit euren Anforderungen waechst.
Fangt mit einem Modell an, messt Baseline-Metriken, optimiert Batching und Instance-Count, und skaliert dann horizontal. Das ist der schnellste Weg zu einem produktiven Setup.
Falls ihr Unterstuetzung beim Aufbau eurer Inference-Plattform braucht oder eine Architektur-Review fuer ein bestehendes Setup wollt, koennt ihr uns jederzeit unter /kontakt 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 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.
Kubernetes als Machine Learning Plattform einrichten
Kubernetes als ML-Plattform aufbauen: GPU-Cluster mit NVIDIA Device Plugin, MLOps-Pipelines mit Kubeflow und Model Serving mit TensorFlow und PyTorch.
Kubernetes Deutschland: KI Model Serving mit TorchServe, TensorFlow & Seldon
Erfahren Sie, wie Sie KI-Modelle sicher und effizient auf Kubernetes-Clustern in Deutschland bereitstellen. Dieser Artikel vergleicht TorchServe, TensorFlow Serving und Seldon für skalierbares AI Model Serving im deutschen Mittelstand, unter Berücksichtigung von Performance, Kosten und lokalen Anforderungen.
Self-Hosted Kubernetes AI Code Assistant: Ihr eigener Copilot für Datensouveränität
Entdecken Sie, wie Ihr Unternehmen mit einem selbst-gehosteten Kubernetes AI Code Assistant maximale Datensouveränität sicherstellt und Compliance-Anforderungen erfüllt. Profitieren Sie von Kosteneffizienz und maßgeschneiderter Coding AI als leistungsstarke Copilot-Alternative – ideal für deutsche Entwicklungsteams und den Mittelstand.
LLM Self-Hosting auf Kubernetes: Llama und Mistral
Open-Source LLMs wie Llama und Mistral auf Kubernetes self-hosten für volle Datensouveränität, DSGVO-Konformität und präzise Kostenkontrolle.