In-House LLM Serving at Netflix

Deine Reaktion:

Netflix betreibt Large Language Models in eigener Infrastruktur, nicht über externe APIs. Die Integration erfolgt tief in die bestehende Produktionsumgebung. Kein separater ML-Silo, sondern eine durchgängige Einbettung.

Der Netflix Technology Blog beschreibt vier Design-Entscheidungen: Wahl der Inference-Engine, Verpackung der Modelle, Gestaltung der API-Oberfläche und Deployment-Strategie. Jede baut auf der vorherigen auf. In der Theorie klar, zeigten sich unter Produktionslast unerwartete Nebenwirkungen.

Die Architektur im Überblick

Netflix betreibt ein einheitliches JVM-basiertes Serving-System für Routing, A/B-Tests, Candidate Generation, Feature Fetching, Inferenz, Post-Processing und Logging. Es gibt einen Echtzeit-Pfad (gRPC) und einen Batch-Pfad (HTTP). Kleine CPU-Modelle laufen direkt im Prozess des Serving-Systems – das spart Remote-Call-Overhead. Größere Modelle benötigen GPUs. Das Serving-System übernimmt Vor- und Nachbereitung, delegiert die Inferenz an den Model Scoring Service (MSS). MSS ist der gemeinsame Inference-Backend für XGBoost, TensorFlow, PyTorch und LLMs. Darunter liegt NVIDIA Triton Inference Server für Modell-Loading, Batching und GPU-Scheduling. Darüber ein Java-Control-Plane für Deployment, Versioning, Health Checks, Autoscaling und Multi-Region-Rollout. Modellautoren packen Artefakte und konfigurieren das Deployment; das Control Plane stellt GPU-Instanzen bereit, konfiguriert Triton und orchestriert Zero-Downtime-Upgrades.

Engine-Wahl: Warum vLLM der Standard wurde

Ursprünglich setzte Netflix auf TensorRT-LLM. Leistungsfähig, in Triton integriert. Bis Sommer 2025 hatten Open-Source-Engines den Performance-Vorsprung spezialisierter Stacks weitgehend aufgeholt. Der Workload-Mix wurde vielfältiger – Embedding-Generierung, Prefill-only-Inferenz für Ranking und Retrieval, autoregressives Decoding und kundenspezifische Modelle mit komplexer Constraint-Logik. Netflix benchmarkte erneut und wählte vLLM aus operativen Gründen: vLLM lädt benutzerdefinierte Modellarchitekturen ohne mehrstufige Compilation-Pipeline – das beschleunigt Iterationen bei nicht-standardisierten Modellen. Es bietet Erweiterungshooks für eigene Decoding-Logik, die für das spätere Constrained Decoding entscheidend sind. Die Debugbarkeit ist besser als bei einem kompilierten Engine wie TensorRT-LLM, weil Fehler und Zwischenzustände leichter inspizierbar sind. Viele ML-Praktiker nutzen vLLM bereits in der Forschung, was den Übergang zur Produktion vereinfacht.

Integration von vLLM in Triton: Zwei Wege, eine Entscheidung

Nach der Engine-Wahl musste Netflix entscheiden, wie die Modelle für vLLM verpackt werden. Triton unterstützt zwei Backends. Das Python-Backend erfordert, dass der Autor explizite Input/Output-Tensor-Spezifikationen zur Packzeit definiert. Diese Spezifikationen werden im Artefakt eingefroren und müssen mit dem Request-Builder des Frontends übereinstimmen. Jedes Frontend-Update, das I/O-Spezifikationen ändert, erfordert eine koordinierte Änderung des Verpackungscodes. Das vLLM-Backend erwartet nur eine JSON-Config, die auf Modellgewichte und Tokenizer verweist. Tritons vLLM-Backend generiert I/O-Tensor-Spezifikationen dynamisch zur Deployment-Zeit – der Autor definiert sie nie. Modelle und Frontend entwickeln sich unabhängig voneinander. Für Netflix ist das vLLM-Backend der architektonisch korrekte Standard. Zwei Probleme traten in der Produktion auf: Triton/vLLM-Versionsinkompatibilitäten (Tritons vLLM-Backend ist gegen eine spezifische vLLM-API-Version kompiliert) und benutzerdefinierte Modelllogik, die das Python-Backend erfordert, wenn das Modell nicht standardkonform ist. Dieser Escape-Hatch wird wohl für eine Teilmenge der Modelle nötig bleiben.

API-Design: OpenAI-kompatibel als zweite Front

Jedes Modell – XGBoost-Ensemble oder großes LLM – wird über denselben gRPC-Aufruf gescort. Das ermöglicht Wiederverwendung von Client-Bibliotheken, Health Checking und Deployment-Pipelines. Die OpenAI-kompatible API ist inzwischen der De-facto-Standard für das LLM-Ökosystem. Inference-Engines, Orchestrierungs-Frameworks, Evaluierungstools und Client-Bibliotheken sprechen sie alle. Netflix entschied sich, diese API als zusätzliches Frontend neben gRPC anzubieten. Der Vorteil: Der Weg von der Experimentierphase zur Produktion – von einem gehosteten Modell zu einem feinabgestimmten, selbst gehosteten – wird nahezu nahtlos. Gleiche API, minimale Code-Änderungen. Hinter der API nutzt Netflix das Triton OpenAI-kompatible Frontend von NVIDIA. Es startet einen eingebetteten Triton-Server, wickelt Requests in Triton-Inferenz-Requests um und liefert Antworten über FastAPI. Zusätzlich sind KServe-HTTP/gRPC-Frontends aktiv, sodass dieselbe Triton-Instanz auch über gRPC erreichbar bleibt. Eine Lücke zeigte sich: Das response_format-Feld wurde stillschweigend verworfen, bevor es vLLM erreichte. Ein Caller, der JSON-Output anforderte, bekam keine Guided-Decoding-Constraints – und konnte malformed JSON erhalten, ohne dass die Plattform einen Fehler meldete. Netflix patchte das Frontend, um response_format in vLLMs Guided-Decoding-Parameter zu übersetzen.

Deployment-Strategien: Rot-Schwarz oder Versioniert

GPU-Deployments brauchen länger zum Hochfahren als CPU-Dienste. Das I/O-Schema kann sich zwischen Modellversionen ändern – ein Koordinationsproblem. Netflix bietet zwei Strategien an. Red-Black deployt die neue Version neben der aktuellen. Wenn die neue Instanz die Health Checks besteht, wird der Traffic phasenweise umgeschaltet – die neue skaliert hoch, die alte herunter. Bei Fehlern erfolgt ein atomarer Rollback. Red-Black ist die richtige Wahl bei stabilem Modell-Interface. In der Praxis zeigte sich eine Koordinationslücke: Wenn eine neue Version ein I/O-Schema-Update erfordert (z.B. neue Tensor-Dimensionen), kann der Upstream-Consumer seine Konfiguration erst aktualisieren, wenn das neue Modell vollständig live ist. Während des Migrationsfensters sendet er „alte“ Requests an das „neue“ Deployment – die schlagen fehl. Versioned schließt diese Lücke, indem es für jedes (modelId, modelVersion)-Paar ein unabhängiges Deployment unterhält. Mehrere Versionen laufen gleichzeitig, was Modell-Deployment von Consumer-Updates entkoppelt: Der Consumer wartet, bis die neue Version bereit ist, dann schaltet er um. Die alte Version bedient solange Legacy-Traffic. Das erhöht temporär die GPU-Kosten. Netflix empfiehlt, variable Konfigurationen (z.B. Tensor-Shapes) direkt in das Inferenz-Modell einzubetten, um es versionsagnostisch zu machen, sodass der günstigere Red-Black-Pfad genutzt werden kann. Versioned bleibt seltenen Fällen mit breaking changes vorbehalten.

Operative Details: Boot-Sequenz und Metriken

Zwei operative Details verdienen besondere Aufmerksamkeit. Erstens die Boot-Sequenz: Das Herunterladen großer LLMs von S3 oder Hugging Face beim Start ist zu langsam – es übersteigt die Cold-Start-Latenz, die Scheduler tolerieren. Netflix materialisiert die Modelle auf Amazon FSx zum Zeitpunkt der Modell-Ankündigung, sodass Warm-Starts auf ein High-Performance-Dateisystem zugreifen. Wenn Consumer die OpenAI-kompatible API benötigen, läuft Triton als eingebetteter Server im OpenAI-Frontend-Prozess; sonst eigenständig. Der Rest der Boot-Sequenz ist mechanisch: Paket extrahieren, benutzerdefinierte vLLM-Plugins installieren, Prometheus-Multiprocess-Verzeichnis bereinigen und gRPC-Port erst öffnen, wenn die Engine bereit ist.

Zweitens die Metriken: vLLM schreibt Metriken in PROMETHEUS_MULTIPROC_DIR als .db-Dateien; Triton meldet Server-Metriken über einen eigenen Prometheus-Endpunkt. Keiner weiß vom anderen. Tritons eingebaute Brücke liefert nur 9 von über 40 vLLM-Metriken – wichtige wie Token-Durchsatz, KV-Cache-Auslastung und Prefix-Cache-Trefferquote fehlen. Netflix baute einen leichten HTTP-Proxy, der beide in einem einzigen /metrics-Endpunkt zusammenführt: Er holt Triton-Metriken über HTTP, liest vLLM-Metriken von der Festplatte mit Prometheus’ MultiProcessCollector und gibt die kombinierte Ausgabe zurück. Bestehende Dashboards und Alarme funktionieren ohne Änderung.

Constrained Decoding im Produktionsmaßstab

Einige Netflix-Produktionsworkloads erfordern fein granulare Kontrolle über die Token-Generierung. Statt Geschäftslogik nach der Inferenz anzuwenden – ungültige Generierungen bezahlen und dann wiederholen oder reparieren – schiebt Netflix die Constraints in die Decode-Schleife. Das Modell erzeugt Ausgaben, die von Natur aus konform sind. Umsetzung erfolgt über vLLMs Custom-Logits-Processor-Schnittstelle: Jede Constraint wird als Zustandsmaschine modelliert, die sich mit der generierten Token-Historie entwickelt und bei jedem Schritt Token-Eligibility-Masken ausgibt. Jeder Request bekommt seinen eigenen konfigurierten Processor, da verschiedene Requests unterschiedliche Regeln haben.

Die Skalierung dieser Constraint-Logik war eine Herausforderung. In der ersten Implementierung auf vLLM V0 lief die Logit-Processing pro Request – der GPU produziert Logits für den gesamten Batch, die CPU kopiert sie rüber und wartet auf den Transfer, dann läuft die Constraint-Logik sequenziell für jeden Request – sequenziell, weil die GIL Python daran hindert, die Arbeit pro Request zu parallelisieren. CPU-Zeit im Logit-Processing wurde zum Flaschenhals. Netflix migrierte später auf vLLM V1, das diese Einschränkung adressiert. Die Details der V1-Architektur und wie sie das Problem löst, sind im Originalblog nachzulesen – aber die Lektion ist klar: Nicht jeder Algorithmus, der funktioniert, skaliert auch linear. Besonders bei LLMs müssen Engine und Decoding-Logik aufeinander abgestimmt sein.

Was bedeutet das konkret?

Netflix zeigt, dass die Kontrolle über die gesamte LLM-Infrastruktur Vorteile bringt – wenn man bereit ist, in die Tiefe zu gehen. Die Entscheidungen sind nicht trivial, und unter Produktionslast tauchen immer wieder unerwartete Details auf. Der Vorteil ist eine Architektur, die nicht an die Grenzen eines Drittanbieters gebunden ist. Entwickler und Architekten können diese Erkenntnisse nutzen, auch wenn sie nicht im Netflix-Maßstab denken. Die Lektionen sind universell: Wähle eine Engine, die zu deinem Workload-Mix passt – nicht nur nach Benchmark-Zahlen. Setze auf standardisierte APIs, um den Ökosystem-Zugang zu öffnen. Baue Deployment-Strategien, die Schemamigrationen einkalkulieren. Teste die Skalierung von Constraints in der Decode-Schleife unter realistischer Last. Die Frage ist nicht, ob eigene LLMs betrieben werden sollten, sondern wie tief die Detailarbeit gehen muss. Netflix hat den Weg gezeigt.

Quelle: netflixtechblog.com

Deine Reaktion:
Artikel teilen:
Sebastian Krötzsch
Autor

Sebastian Krötzsch

Sebastian Krötzsch schreibt auf sebask.de über Künstliche Intelligenz, Automatisierung, digitale Systeme und die Frage, was davon im Alltag wirklich nützlich ist. Ohne Buzzword-Nebel, dafür mit klarem Blick auf Praxis, Tools und echte Wirkung.