Veröffentlicht am

Triton Inference Server auf Kubernetes: Setup und Tuning

Teilen:
Authors

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:

  1. 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.
  2. GPU-Effizienz: Ohne Batching verarbeitet eine GPU einzelne Requests seriell und ist die meiste Zeit idle. Triton sammelt Requests und verarbeitet sie als Batch.
  3. 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=false laedt Modelle automatisch, sobald sie im Repository erscheinen. Fuer Produktion ist true sicherer, da dann nur explizit konfigurierte Modelle geladen werden.
  • Die Probes nutzen Tritons eingebaute Health-Endpoints. live prueeft, ob der Prozess laeuft. ready prueeft, 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:

ParameterEffektEmpfehlung
max_batch_sizeMaximale Batch-GroesseAbhaengig vom GPU-Memory; 16-64 ist ein guter Start
preferred_batch_sizeTriton wartet bis einer dieser Werte erreicht ist2-3 Werte, aufsteigend
max_queue_delay_microsecondsMaximale Wartezeit fuer Batching100-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:

MetrikWas sie zeigtAlert-Schwelle
nv_inference_request_duration_usEnd-to-End-Latenz pro RequestP95 > SLA
nv_inference_queue_duration_usWartezeit in der Queue> 1000us
nv_inference_request_successErfolgreiche RequestsRate-Drop > 5%
nv_gpu_utilizationGPU-Auslastung< 20% (Overprovisioning) oder > 90% (Engpass)
nv_gpu_memory_used_bytesGPU-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:

KonfigurationThroughput (infer/s)P50 Latenz (ms)P99 Latenz (ms)GPU-Auslastung
Kein Batching, 1 Instance1208.212.125%
Batch 8, delay 200us, 1 Instance5809.118.462%
Batch 16, delay 500us, 2 Instances1.10011.324.778%
Batch 32, delay 1000us, 2 Instances1.45016.838.284%

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:

  1. Preprocessing-Modell (Python Backend): Normalisierung, Tokenisierung
  2. Inference-Modell (ONNX/PyTorch): Das eigentliche ML-Modell
  3. 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