Netflix、自社開発の LLM サービング基盤と設計判断を公開
本文の状態
日本語全文を表示中
詳細モードで約21分の本文を読めます。
Netflix は大規模言語モデルの推論インフラを自社で構築し、TensorRT-LLM から vLLM へ基盤エンジンを変更するなどの具体的な設計判断と、その運用上の教訓を公開した。
AI深層分析を開く2026年8月4日 14:09
AI深層分析
キーポイント
フルスタック自前化の推進
Netflix は外部 API に依存せず、モデルデプロイから推論までの全工程を既存の生産環境内で完結させる独自のアプローチを採用している。
vLLM への基盤エンジン移行
2025 年夏にワークロードの多様化とオープンソースエンジンの性能向上を踏まえ、専用スタックとの性能差が縮まったことを理由に vLLM を標準エンジンとして選定した。
統合された JVM ベースのサービングシステム
ルーティング、A/B テスト、特徴量取得、推論、後処理を一貫して処理する JVM ベースの統一システムが構築され、gRPC と HTTP の両経路をサポートしている。
Model Scoring Service (MSS) の役割
大規模モデルの推論は NVIDIA Triton Inference Server を基盤とする MSS に委譲され、Java コントロールプレーンがデプロイやバージョン管理を統括するハイブリッド構成となっている。
vLLM ベンドのアーキテクチャ的優位性
モデルとフロントエンドが独立して進化できるため、I/O 仕様の凍結や協調変更が不要になる。ただしバージョンミスマッチやカスタムロジックが必要なケースでは Python バックエンドが必要となる。
重要な引用
Netflix went further — we run the full stack ourselves, from model deployment through inference, inside our existing production environment rather than a separate ML silo.
We re-benchmarked against this mix and selected vLLM as our paved-path engine on operational fit.
The vLLM backend is the architecturally correct default.
Every model — XGBoost ensemble or large-scale LLMs — is scored via the same gRPC call, so we reuse the same client libraries, health checking, and deployment pipelines.
編集コメントを表示
編集コメント
Netflix が公開した詳細なアーキテクチャ図と、TensorRT-LLM から vLLM への移行に至った具体的な判断プロセスは、大規模 LLM 運用のベストプラクティスを示す貴重な事例である。特に、外部 API に依存しないフルスタック自前化のメリットとトレードオフを論じている点は、同様の基盤構築を検討する技術者にとって極めて参考になる。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
AI プラットフォームのモデルランタイムチームと推論チームによる記事
はじめに
多くの組織は、ホストされた API を通じて大規模言語モデル(LLM)を利用しています。しかし Netflix ではさらに一歩進み、既存の生産環境内で、モデルのデプロイから推論に至るまでのフルスタックを自前で運用しています。これは ML 専用のサイロ(閉じたシステム)とは異なるアプローチです。これらの判断の中には、直感的に明らかなものもあれば、本番環境での負荷がかかるまでそのトレードオフが明らかにならないものもありました。
この記事では、特に代替案が真剣に検討された分野に焦点を当てます。具体的には、推論エンジンの選定、モデルのパッケージ化方法、API サーフェスの設計、デプロイ戦略、そして出力制約の強制です。単に何を作ったかだけでなく、なぜそう決めたのか、そして設計段階では予測できなかった本番環境が明らかにした教訓を共有することが目的です。
アーキテクチャの概要
Netflix におけるメンバー規模の機械学習は、JVM ベースの統一されたサービングシステムによって支えられています。このシステムは、後続の消費者向けにエンドツーエンドの流れを処理します。具体的には、ルーティングと A/B テストロジック、候補生成、特徴量フェッチ、推論、ポストプロセッシング、そして各段階でのログ記録を担当しています。リアルタイムパスとキャッシュされたバッチパスの両方をサポートしています。
図 1 は、現在の呼び出し元が推論に到達する2つの経路を示しています。1 つはこのサービングシステムを経由する gRPC パス、もう 1 つは新しい LLM ドライブ型アプリケーションで使用される直接 HTTP パスです。
推論の実行場所はモデルによって異なります。小さな CPU モデルはプロセス内で実行され、リモート呼び出しのオーバーヘッドを回避します。一方、大規模なモデルには GPU が必要であり、サービングシステムは前処理と後処理をローカルで処理しますが、推論自体はリモートサービスである Model Scoring Service (MSS) に委ねます。
MSS は共有推論バックエンドとして機能し、XGBoost、TensorFlow、PyTorch、LLM といった多様なモデルを統一されたインターフェースの裏側でサポートしています。その下層には NVIDIA Triton Inference Server が位置し、モデルの読み込み、バッチ処理、GPU スケジューリングを管理しています。
Triton の上には Java で書かれたコントロールプレーンが構築されており、デプロイ、バージョン管理、ヘルスチェック、自動スケーリング、複数リージョンへのロールアウトを担当します。モデル作者はアーティファクトをパッケージ化してデプロイ設定を行い、コントロールプレーンが GPU インスタンスの割り当て、Triton の設定、そしてダウンタイムゼロでのアップグレードをオーケストレーションします。
image図 1. サービングアーキテクチャの概要
設計決定と実装
このプラットフォームを形作っているのは、エンジン、パッケージング、API サフェース、ロールアウトという 4 つの決断です。これらは依存関係の順序で提示されており、それぞれの決定が次の段階に制約を与えます。
vLLM をパブド・パス(推奨)エンジンとして採用
当初、このプラットフォームは TensorRT-LLM を基盤にして構築されました。これは当時高性能な推論エンジンであり、すでに MSS で使用されている計算バックエンドである Triton と統合されていました。
2025 年夏までに、2 つの大きな変化が生じていました。オープンソースエンジンが専用スタックとの性能差をほぼ埋め、私たちのワークロード構成も多様化し、埋め込み生成やランク付け・検索のためのプレフィルのみ推論、自己回帰的デコーディング、そして非自明なステップ制約ロジックを持つカスタムモデルの処理が含まれるようになりました。この新しい構成に対して再ベンチマークを実施した結果、運用上の適合性を考慮して vLLM を標準エンジンとして選定しました。
その理由は以下の通りです。
多様なカスタムモデルアーキテクチャを、マルチステップコンパイルパイプラインなしで直接ロードできるため、非標準モデルに対するイテレーションが高速化されます。
後述する制約付きデコーディングのワークに不可欠な、カスタムデコーディングロジック用の拡張フックを提供します。
デバッグ性が高く、従来の TensorRT-LLM のようなコンパイル済みエンジンと比較して、障害や中間状態の調査が容易です。
親和性が高く、多くの ML プラクティショナーが研究段階で vLLM を利用していたため、研究から本番環境への移行コストを削減できました。
vLLM の導入と Triton への統合
vLLM を採用したことで、次はモデルをどのようにパッケージ化するかという課題が残りました。Triton はこれに対応する 2 つの方法をサポートしており、この選択は保守性に大きな影響を与えます。具体的には、モデルアーティファクトがフロントエンドのアップグレードとどの程度密結合されるかが問われます。
Python ベースエンドでは、著者はパッケージ作成時に明示的な入出力テンソル仕様を定義します。この仕様はアーティファクトに固定され、サードパーティベンダーのフロントエンドが期待するリクエストビルダーと一致している必要があります。そのため、I/O 仕様に影響を与えるすべてのフロントエンドアップグレードには、パッケージコードとの調整された変更が必要です。これを怠ると、実行時にリクエストが失敗します。
vLLM ベースエンドでは、アーティファクトはモデルの重みとトークナイザーを指す単なる JSON 設定ファイルです。Triton の vLLM バックエンドはこの設定を読み取り、デプロイ時に動的に入出力テンソル仕様を生成します。著者がこれらを定義することはありません。モデルとフロントエンドは独立して進化できます。
vLLM ベースエンドがアーキテクチャ上正しいデフォルトです。しかし、本番環境で二つの問題が発生しました。
Triton と vLLM のバージョン不一致。Triton の vLLM バックエンドは特定の vLLM API サフェースに対してコンパイルされています。両者の間にズレが生じると(例:Triton 25.09 が vLLM 0.11.2 で削除された vllm.engine.metrics モジュールをインポートする場合)、バックエンド自体がロードできません。プラットフォームはサービスイメージを作成する際に互換性のあるバージョンを固定し、モデル作成者がパッケージ作成時に vLLM のバージョンを上書きできないようにする必要があります。
カスタムモデルロジック
vLLM バックエンドは標準的な HuggingFace 互換のモデルを想定しており、推論ライフサイクル全体を処理します。事前処理や事後処理にカスタムロジックが必要だったり、アンサンブルパイプラインのような非標準的な実行や独自トークナイゼーションが必要なモデルの場合には、Python バックエンドを使用する必要があります。これにより execute() 関数に対する完全な制御が可能になります。この「緊急脱出経路」は、特定のモデル群に対して今後も必要となるでしょう。
エコシステムと互換性のある HTTP フロントエンド
エンジンとパッケージングの設計が固まった後、次なる課題は呼び出し元がどのようにシステムにアクセスするかです。当システムの重要な設計目標の一つは、LLM モデルを特別扱いしないことです。XGBoost アンサンブルであっても大規模 LLM であっても、すべてのモデルが同じ gRPC コールを通じてスコアリングされます。これにより、クライアントライブラリ、ヘルスチェック、デプロイパイプラインを共通化できます。OpenAI 互換 API インターフェースが、推論エンジン、オーケストレーションフレームワーク、評価ツール、クライアントライブラリなど LLM エコシステム全体で事実上の標準インターフェースとなっていることを踏まえ、gRPC のほかに OpenAI 互換 API を追加のフロントエンドとして公開しています。
この設計によるメリットは、実験から本番環境への移行プロセスに現れます。品質、レイテンシ、コスト、データプライバシーなどの理由で、ホスト型モデルからファインチューニングされたセルフホスト型モデルへ移行する際も、ほぼシームレスです。API はそのままに、コードの変更は最小限で済みます。
API の実装では、NVIDIA の Triton が提供する OpenAI 互換フロントエンドを流用しています。組み込み型の Triton サーバーを起動し、これを TritonLLMEngine でラップしてリクエストスキーマを Triton 推論リクエストに変換し、FastAPI を介してレスポンスを返します。同時に KServe の HTTP/gRPC フロントエンドも有効化しており、同じ Triton インスタンスは Java コントロールプレーンからも gRPC でアクセス可能です。
Triton のフロントエンドをそのまま採用したことで、一つの課題が浮き彫りになりました。スキーマで受け入れられる response_format パラメータが、vLLM に到達する前に静かに無視されてしまうのです。その結果、JSON 出力を要求した呼び出し元は、ガイダンス付きデコーディングの制約なしに処理され、プラットフォーム側からエラーが報告されることなく不正な JSON が返される事態が発生しました。
この問題を解決するため、フロントエンドを git-subtree で取り込み、パッチを適用して response_format をリクエスト時に vLLM のガイダンス付きデコーディングパラメータに変換する仕組みを実装しました。
デプロイ戦略
API サーフェスとエンジンが整った今、残る課題は新バージョンをリリースする際に如何にリクエストの切断を防ぐかです。GPU デプロイは CPU サービスよりも起動に時間がかかり、モデルバージョン間で I/O スキーマが変更される可能性もあります。これらは追加の調整問題を招きます。
プラットフォームでは、この課題に対処するために 2 つのデプロイ戦略を提供しています。
Red-Black デプロイでは、新しいバージョンを現在のバージョンと並行して展開します。新しいインスタンスがヘルスチェックに合格すると、トラフィックは段階的に切り替わります。具体的には、新しいバージョンのスケーリングアップと古いバージョンのスケーリングダウンが同じ速度で同時に行われます。もしどのステップでも失敗した場合、システムは即座に原子ロールバックを実行します。
モデルインターフェースが安定している場合は、Red-Black が最適な選択肢です。しかし、本番環境では、新しいバージョンで I/O スキーマの変更(例えばテンソルの次元変更など)が必要になった際に、調整の隙間が発覚しました。この場合、新しいモデルが完全に稼働するまでアップストリームのコンシューマーは設定を更新できないため、移行期間中に「古い」リクエストを「新しい」デプロイメントに送信せざるを得ず、その結果失敗が発生します。
Versioned はこの隙間を埋めるために設計されました。各 (modelId, modelVersion) ペアに対して独立したデプロイメントを維持し、複数のバージョンを同時にサービス提供します。これにより、モデルのデプロイとコンシューマーの更新が切り離されます。コンシューマーは新しいバージョンが完全に準備されるまで設定を変更せず待機し、古いバージョンは引き続きレガシートラフィックを引き受けます。プラットフォームは一定期間非アクティブな古いデプロイメントをクリーンアップしますが、最新バージョンは常に保持されます。その代償として、移行期間の重複部分では GPU コストが一時的に増加します。
変数設定(テンソル形状など)を推論モデル内に直接埋め込み、バージョン非依存化することで、より安価な Red-Black パスを利用できるようにすることをお勧めします。Versioned は、避けられない破壊的インターフェース変更が必要な稀なケースのために用意されています。
運用上の注意点
上記の 4 つの決定事項以外に、設計段階で想定されていなかった運用上の課題が 2 つあります。
ブートシーケンス
vLLM-on-Triton インスタントを起動し、gRPC ポートを開放するまでには、いくつか協調的な手順が必要です。そのうち 2 つは定型処理ではありません。
モデルのキャッシュ化
大規模な LLM を起動時に S3 や Hugging Face から直接ダウンロードすると、コールドスタートのレイテンシがスケジューラーが許容できる範囲を超えてしまいます。そのため、モデル発表時点で Amazon FSx にモデルを事前展開(マテリアライズ)し、ウォームスタート時にはオブジェクトストレージではなく高性能ファイルシステムを利用するようにしています。
埋め込み型とスタンドアロン型の Triton
OpenAI 互換 API を必要とする場合、Triton は OpenAI 互換フロントエンドプロセス内に埋め込みサーバーとして動作します。それ以外の場合はスタンドアロンモードで稼働します。この設定はパッケージ化時に各デプロイメントごとに個別に行われます。
ブートシーケンスの残りの手順は機械的な処理です。モデルパッケージの展開、Python entry_points を通じたカスタム vLLM プラグインのインストール、Prometheus のマルチプロセスディレクトリのクリーンアップ、そしてエンジンが準備完了するまで gRPC ポートをゲート(待機)させる処理を行います。
統一されたメトリクスエンドポイント
前述の Prometheus クリーンアップは、より広範な観測可能性の欠如を示唆しています。vLLM は PROMETHEUS_MULTIPROC_DIR へ .db ファイルとしてメトリクスを出力しますが、Triton は独自の Prometheus エンドポイントを介してサーバーレベルのメトリクスを報告します。両者は互いの存在を認識しておらず、Triton の組み込みブリッジが表面化するのは vLLM の 40 以上あるメトリクスのうちわずか 9 つだけです。トークンスループット、KV キャッシュ利用率、プレフィックスキャッシュヒット率といった重要な指標が欠落しています。
両方のメトリクスを統合した単一の /metrics エンドポイントへ提供するために、軽量な HTTP プロキシを追加しました。このプロキシは、HTTP を介して Triton のメトリクスを取得し、Prometheus の MultiProcessCollector を用いて vLLM のメトリクスをディスクから読み取り、統合された出力を返します。既存のダッシュボードやアラート設定も変更なしで動作します。
スケールにおける制約付きデコーディングの詳細
Netflix の一部の生産環境ワークロードでは、トークン生成に対するきめ細やかな制御が不可欠です。推論後にビジネスロジックを適用して無効な生成物を処理し、再試行や修復を行うのではなく、制約をデコードループ内部に組み込みます。これにより、モデルは構造的にコンプライアンスを満たす出力を生成します。実装は vLLM のカスタム logits プロセッサインターフェースを通じて行い、各制約を状態機械としてモデル化します。この状態機械は生成されたトークンの履歴に応じて進化し、各ステップでトークンの有効性を示すマスクを出力します。リクエストごとに異なるルールが適用されるため、それぞれに個別の構成済みプロセッサが割り当てられます。
これをスケールさせるには、2 つのエンジンバージョンを経験しました。当初は vLLM V0 で展開しましたが(V1 は当時の機能不足のため)、2025 年第4四半期に V1 が成熟した段階で移行を完了しています。以下の2つのセクションでは、移行前後の状況をそれぞれ解説します。
なぜ最初の導入がスケールしなかったか
当初の純粋な Python 実装は機能面では問題なく動作しましたが、スケーラビリティのボトルネックに直面しました。vLLM V0 では、カスタムロジットプロセッサがリクエストごとに実行されます。具体的には、GPU がバッチ全体のロジットを生成した後、CPU がそれらをコピーして転送完了を待ち、その後 GIL(グローバルインタープリタロック)のために Python が並列化できないため、制約ロジックが各リクエストに対して順次実行されます。その結果、ロジット処理にかかる CPU 時間はバッチサイズに比例して増加し、遅延の尾部(tail latency)が悪化します。モデルのフォワードパスが GPU で効率的にバッチ処理されているにもかかわらず、エンドツーエンドのレイテンシは CPU バウンド状態になります。これは単一リクエストのベンチマークでは見えないボトルネックですが、現実的な同時実行環境下で顕在化します。図 2 はこの逐次処理のパターンを可視化したものです。
image図 2: vLLM V0 における CPU 上のロジットプロセッサの逐次実行
vLLM V1 では、構造的な改善がなされ、ロジット処理をバッチレベルに引き上げました。カスタムプロセッサを再実装し、複数のリクエストに対してマスク計算を一括で行うようにバッチレベルのデータ構造で動作するように変更しました。また、GIL の影響を回避するためにホットパス(頻繁に実行される経路)を C++ でマルチスレッド対応で書き直しています。V1 の API では、動的に変化するバッチの状態を正しく維持するために、update_state(batch_update) を通じてバッチメンバーシップの変化を明示的に追跡する必要があります。これは V0 のリクエストごとのインターフェースよりも複雑ですが、必要な設計です。図 3 は、バッチサイズが増加してもロジット処理時間が一定に保たれている様子を示しています。
image図 3: vLLM V1 を使用した CPU 上でのバッチ処理されたロジットプロセッサの実行
運用上の堅牢化
これでパフォーマンスがボトルネックではなくなりました。しかし、デコードループ内のステートフルな制約論理により、設計段階で想定していなかった 2 つの問題が発生しました。
部分的なプリフィル(Partial prefills)
vLLM V1 ではチャンク単位でのプリフィル処理を行うため、リクエストは複数のエンジンステップにわたってプリフィルされます。BatchUpdate には、リクエストが完全に完了したのか、それとも部分的なものだけなのかを区別する粒度がありませんでした。そのため、内部で追跡機能を追加しました。
プリエンプション(Preemption)
メモリ圧力が高まった際、vLLM は部分的に完了したリクエストの KV キャッシュを退去させ、異なるプロンプトと出力トークンリストと共に後ほど再スケジュールすることがあります。これにより、「出力トークンリストは単調増加する」という状態機械の前提が崩れてしまいます。そこで、デコードステップ間でトークン履歴が縮小した場合を検知し、状態機械をリセットして新しいプロンプトから再初期化するようにしました。
まとめ
私たちは、低レイテンシ、深いカスタマイズ性、既存インフラとの統合といった、幅広い生産環境での ML 要件に対応する LLM サービングプラットフォームの構築を目指しました。その結果、vLLM と Triton を基盤とし、一貫した API の背後に統合されたシステムが完成しました。これにより、ML プラクティショナーは実験から本番環境への移行を迅速に行うことができます。
教訓は往々にして詳細に潜んでいました。バージョンの固定、静かな API のギャップ、パッケージングにおけるトレードオフなどです。これらに対処したことで、プラットフォームは本格的に堅牢になり、開発者体験も滑らかになりました。今後の投資先には、摩擦が発生すると予想される領域が反映されています。
システムプロンプトの圧縮により、品質を損なうことなくプロンプト長を短縮します。
vLLM V1 の非同期スケジューリングの実装です。
CPU コードではなく融合された GPU カーネルとして動作するベクトル化ロジットプロセッサです。
メモリフットプリントを削減しスループットを向上させる低精度モデルのバリアントです。
この分野が進化するにつれ、オープンソースコミュニティと引き続き密接に協力していきます。
このシステムは、Netflix のAI Platform組織に所属する多くのチームとの緊密な協力と貢献によって実現されました。特にLiping Pengがモデルのパッケージングワークフローを設計・開発し、Triton と vLLM を MSS に統合して LLM のサービングのための統一されたパスを実現しました。
Hakan Baba、Nicolas Hortiguera、ZQ Zhang は、GPU 容量計画、システムパフォーマンスのチューニング、アプリケーション統合と観測性、およびすべての本番モデルに対する A/B テストの準備と運用卓越性の取り組みを主導しました。Santino Ramos は vLLM を本番モデルで利用可能にし、制約付きデコーディングのパフォーマンスを最適化しました。Binh Tang はカスタムモデルサービングの初期バージョンを開発し、さまざまな LLM サービングフレームワークをベンチマークしました。Lanxi Huang と Daneo Zhang は、ユーザーによるセルフサービスを実現するためのサービング開発ツールを構築しました。
Lingyi Liu が全体のシステムアーキテクチャと中核的な技術決定を主導し、Abhishek Agrawal と Shaojing Li が管理リーダーシップを提供して、整合性、優先順位付け、実行の確保を行いました。
謝辞
この取り組みは、Triton、vLLM、PyTorch などのオープンソース ML ライブラリに大きく依存しています。コミュニティからのチームと貢献者に対して特に感謝申し上げます。また、Netflix AI のメンバーシステム部門のパートナーチームにも、モデリング側における緊密な協力と革新への貢献に対し謝意を表します。
Netflix の社内 LLM サービングは、もともと Medium 上の「Netflix TechBlog」で公開された記事です。ここでは読者によるハイライトやコメントを通じて議論が続いています。
原文を表示
By AI Platform’s Model Runtime team and Inference team
Introduction
Most organizations consume LLMs through hosted APIs. Netflix went further — we run the full stack ourselves, from model deployment through inference, inside our existing production environment rather than a separate ML silo. Some of those decisions weren’t obvious, and a few revealed their trade-offs only under production load.
This post focuses on the choices where alternatives were seriously considered: engine selection, model packaging, API surface design, deployment strategy, and output constraints enforcement. The goal is to share not just what was built, but why — and what production revealed that the design phase didn’t anticipate.
Architecture Overview
Member-scale ML at Netflix is fronted by a unified JVM-based serving system that handles the end-to-end flow for downstream consumers: routing and A/B test logic, candidate generation, feature fetching, inference, post-processing, and logging at each stage. Both real-time and cached batch paths are supported. Figure 1 shows the two ways callers reach inference today: the gRPC path through this serving system and a direct HTTP path used by newer LLM-driven applications.
Where inference runs depends on the model. Small CPU models run in-process, avoiding remote-call overhead. Larger models need GPUs — the serving system handles pre- and post-processing locally but delegates inference to a remote service, Model Scoring Service (MSS). MSS is the shared inference backend, supporting XGBoost, TensorFlow, PyTorch, and LLMs behind a unified interface, with NVIDIA Triton Inference Server underneath managing model loading, batching, and GPU scheduling.
On top of Triton sits a Java control plane that handles deployment, versioning, health checking, autoscaling, and multi-region rollout. Model authors package their artifacts and configure the deployment; the control plane provisions GPU instances, configures Triton, and orchestrates zero-downtime upgrades.
imageFigure 1. Serving Architecture OverviewDesign Decisions and Implementation
Four decisions shape this platform — engine, packaging, API surface, and rollout — presented in dependency order, since each one constrains the next.
vLLM as the Paved-Path Engine
The platform was originally built on TensorRT-LLM, a performant inference engine at the time and already integrated with Triton — the compute backend in use within MSS.
By summer 2025, two things had shifted: open-source engines had largely closed the performance gap with specialized stacks, and our workload mix had broadened to include embedding generation, prefill-only inference for ranking and retrieval, autoregressive decoding, and custom models with non-trivial per-step constraint logic. We re-benchmarked against this mix and selected vLLM as our paved-path engine on operational fit:
Loads custom model architectures without a multi-step compilation pipeline — faster iteration on non-standard models.
Extensibility hooks for custom decoding logic — necessary for the constrained-decoding work described later.
Debuggability — easier to inspect failures and intermediate state than with a compiled engine in earlier TensorRT-LLM.
Familiarity — many ML practitioners were already using vLLM in research, which cut the research-to-production handoff cost.
Integrating vLLM into Triton
With vLLM picked, the next decision was how to package models for it. Triton supports two ways, and the choice has significant implications for maintainability — specifically, how tightly model artifacts are coupled to frontend upgrades.
Python backend. The author defines explicit input/output tensor specs at packaging time. These specs are frozen in the artifact and must match what the third-party vendor’s frontend’s request builder expects, so every frontend upgrade that touches I/O specs requires a coordinated change to packaging code; otherwise, requests fail at runtime.
vLLM backend. The artifact is just a JSON config pointing to the model weights and tokenizer. Triton’s vLLM backend reads this config and generates I/O tensor specs dynamically at deployment time — the author never defines them. Models and frontend evolve independently.
The vLLM backend is the architecturally correct default. Two things bit us in production:
Triton/vLLM version mismatch. Triton’s vLLM backend is compiled against a specific vLLM API surface. When the two drift — for example, Triton 25.09 importing vllm.engine.metrics, a module removed in vLLM 0.11.2 — the backend fails to load entirely. The platform has to pin compatible versions when baking the service image, and prevent model authors from overriding the vLLM version at packaging time.
Custom model logic. The vLLM backend expects a standard HuggingFace-compatible model and handles the full inference lifecycle. Models needing custom preprocessing, postprocessing, or non-standard execution — ensemble pipelines, custom tokenization — must use the Python backend, which gives full control over execute(). This escape hatch will likely remain necessary for a subset of models.
Ecosystem-Compatible HTTP Frontend
With engine and packaging settled, the next question is how callers reach the system. A key design goal of our system was that LLM models should NOT be special snowflakes. Every model — XGBoost ensemble or large-scale LLMs — is scored via the same gRPC call, so we reuse the same client libraries, health checking, and deployment pipelines. Given that the OpenAI-compatible API interface has become the de facto interface for the LLM ecosystem — inference engines, orchestration frameworks, evaluation tools, and client libraries all speak it — so we expose the OpenAI-compatible API as an additional frontend alongside gRPC.
The payoff shows up in the experimentation-to-production path: graduating from a hosted model to a fine-tuned self-hosted one — for quality, latency, cost, or data privacy — is nearly seamless. Same API, minimal code changes.
Behind the API, the implementation reuses NVIDIA’s Triton OpenAI-compatible frontend. It starts an embedded Triton server, wraps it in a TritonLLMEngine that converts request schemas into Triton inference requests, and serves responses through FastAPI. KServe HTTP/gRPC frontends are enabled alongside, so the same Triton instance remains accessible to the Java control plane over gRPC. Adopting Triton’s frontend directly exposed one gap: response_format — accepted by the schema — was silently dropped before reaching vLLM, so that a caller requesting JSON output proceeded without guided decoding constraints and could receive malformed JSON with no error surfaced by the platform. We git-subtreed and patched the frontend to translate response_format into vLLM’s guided decoding parameters at request time.
Deployment Strategies
With API surface and engine in place, the question that remains is how new versions roll out without dropping requests. GPU deployments take longer to bring up than CPU services, and the I/O schema may change between model versions — adding a coordination problem on top. The platform offers two strategies:
Red-Black deploys a new version alongside the current one. Once the new instance passes health checks, traffic shifts in phases — the new version scales up while the old scales down at the same rate. If any step fails, the system triggers an atomic rollback. Red-Black is the right choice when the model interface is stable. Production revealed a coordination gap when a new version requires an I/O schema change (e.g., new tensor dimensions): the upstream consumer can’t update its config until the new model is fully live, so it inevitably sends “old” requests to a “new” deployment during the migration window, and those fail.
Versioned solves that gap by maintaining an independent deployment for every (modelId, modelVersion) pair. Multiple versions serve simultaneously, decoupling model deployment from consumer updates: the consumer waits for the new version to be fully ready before switching its config, while the old version keeps serving legacy traffic. The platform cleans up older deployments after inactivity but always preserves the latest. The trade-off is a temporary increase in GPU cost during the transition overlap.
We recommend embedding variable configurations (e.g., tensor shapes) directly into the inference model to make it version-agnostic, so it can use the cheaper Red-Black path. Versioned is reserved for the rare cases where a breaking interface change is unavoidable.
Operational Notes
Beyond those four decisions, two operational details are worth flagging — both hit production gaps the design phase didn’t anticipate.
Boot sequence
Bringing a vLLM-on-Triton instance up involves several coordinated steps before the gRPC port opens. Two are non-routine.
Model caching. Downloading large LLMs directly from S3 or Hugging Face at startup is slow enough to inflate cold-start latency past what schedulers tolerate. We materialize models on Amazon FSx at the time of model announcement, so warm starts hit a high-performance file system instead of object storage.
Embedded vs standalone Triton. When consumers need the OpenAI-compatible API, Triton runs as an embedded server inside the OpenAI-compatible frontend process; otherwise, it runs standalone. This is configured per-deployment at packaging time.
The rest of the boot sequence is mechanical: extracting the model package, installing custom vLLM plugins via Python entry_points, cleaning the Prometheus multiprocess directory, and gating the gRPC port until the engine is ready.
Unified metrics endpoint
The Prometheus cleanup above hints at a wider observability gap. vLLM writes metrics to PROMETHEUS_MULTIPROC_DIR as .db files; Triton reports server-level metrics through its own Prometheus endpoint. Neither is aware of the other, and Triton’s built-in bridge surfaces only 9 of 40+ vLLM metrics — missing critical ones like token throughput, KV cache utilization, and prefix cache hit rates.
We added a lightweight HTTP proxy that merges both into a single /metrics endpoint: it fetches Triton metrics via HTTP, reads vLLM metrics from disk using Prometheus’s MultiProcessCollector, and returns the combined output. Existing dashboards and alerts work without modification.
Deep-Dive: Constrained Decoding at Scale
Some Netflix production workloads rely heavily on fine-grained control over token generation. Rather than applying business logic after inference — paying for invalid generations, then retrying or repairing — we push constraints inside the decode loop, so the model generates outputs that are compliant by construction. We implement this via vLLM’s custom logits processor interface, modeling each constraint as a state machine that evolves with the generated token history and emits token-eligibility masks at each step. Each request gets its own configured processor, since different requests apply different rules.
Getting this to scale ran across two engine versions: we initially deployed on vLLM V0 (V1 had feature gaps), then migrated to V1 in Q4 2025 once it matured. The two subsections that follow are the before-and-after.
Why the first implementation didn’t scale
Our initial pure-Python implementation worked functionally but hit a scaling bottleneck. In vLLM V0, custom logits processors run per-request: the GPU produces logits for the whole batch, the CPU copies them across and waits for the transfer, and then constraint logic runs sequentially for each request — sequentially because the GIL prevents Python from parallelizing the per-request work. CPU time in logit processing therefore grows linearly with batch size, hitting tail latencies. End-to-end latency becomes CPU-bound even though the model’s forward pass is batched efficiently on GPU. It’s a bottleneck invisible in single-request benchmarks that only surfaces under realistic concurrency. Figure 2 makes the serial pattern visible.
imageFigure 2: Logits processor serial execution on CPU with vLLM V0vLLM V1 enabled a batch-level design
The structural fix arrived in vLLM V1, which moved logits processing to batch level. We rewrote our custom processor to operate on batch-level data structures, computing masks across many requests together, and reimplemented the hot path in C++ with multi-threading to step around the GIL. The V1 API requires explicit tracking of batch membership changes via update_state(batch_update) — more complex than V0’s per-request interface, but necessary to maintain correct state in a dynamically evolving batch. Figure 3 shows logits processing time staying flat as batch size grows.
imageFigure 3: Batched logits processor execution on CPU with vLLM V1Operational hardening
Now, performance was no longer the bottleneck. But stateful constraint logic in the decode loop introduced two issues the design phase didn’t anticipate:
Partial prefills. V1 performs chunked prefilling, so a request can be prefilled over multiple engine steps. BatchUpdate lacks the granularity to tell whether a request was fully or only partially prefilled, so we added internal tracking.
Preemption. Under memory pressure, vLLM may evict a partially completed request’s KV cache and reschedule it later with a different prompt and output token list. This breaks the state machine’s assumption that the output token list grows monotonically. We detect when the token history shrinks between decode steps, reset the state machine, and reinitialize from the new prompt.
Wrap up
We set out to build an LLM serving platform for broad production ML requirements — low latency, deep customization, and integration with existing infrastructure. The result is a system on vLLM and Triton, unified behind a consistent API, designed to give ML practitioners a fast path from experimentation to production.
The lessons were often in the details — version pinning, silent API gaps, packaging trade-offs — but addressing them has made the platform meaningfully more robust and the developer experience smoother. Next investments reflect where we expect friction:
System prompt compression to reduce prompt length without sacrificing quality.
Asynchronous scheduling of vLLM V1.
Vectorized logits processors that run as fused GPU kernels instead of CPU code.
Lower-precision model variants to decrease memory footprint and increase throughput.
We’ll continue working closely with the open-source community as this space evolves.
Contributions
This system is the result of close collaboration and contributions from many teams within the AI Platform org at Netflix. In particular, Liping Peng designed and developed the model packaging workflow and drove the integration of Triton and vLLM with MSS to enable a unified pathway for serving LLMs. Hakan Baba, Nicolas Hortiguera, and ZQ Zhang led GPU capacity planning, system performance tuning, application integration and observability, as well as A/B test readiness and operational excellence efforts for all production models. Santino Ramos enabled vLLM for production models and optimized constrained decoding performance. Binh Tang developed the initial version of custom model serving and benchmarked different LLM serving frameworks. Lanxi Huang and Daneo Zhang built the serving development tools to enable user self-service. Lingyi Liu drove the overall system architecture and core technical decisions. Abhishek Agrawal and Shaojing Li provide management leadership to ensure alignment, prioritization and execution.
Acknowledgements
This work heavily leverages open-source ML libraries, such as Triton, vLLM and PyTorch, etc. We’re especially grateful to the teams and contributors from the community. We also thank our partner teams in Netflix AI for Member Systems for their close collaborations and innovation on the modeling side.
imageIn-House LLM Serving at Netflix was originally published in Netflix TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.
AI算出
技術分析ainew評価高い
記事は外部 API に依存しない独自基盤の構築プロセスと、Triton や JVM を活用したアーキテクチャの詳細な技術的知見を提供しており、AI/ML の核心テーマに深く関連する。Netflix という大規模事例における具体的な実装ノウハウは新規性が高く、検索意図への適合度が高いが、日本固有のコンテキストや企業への直接的な影響は限定的である。
6つの評価軸を見る
- AI関連度
- 100
- 情報源の信頼性
- 100
- 新規性
- 75
- 調べる価値
- 75
- 重複の少なさ
- 100
- 日本での有用性
- 25
同じ出来事を2媒体で確認
同じ出来事を扱う別媒体の記事です。見出しと公開時刻を比較できます。
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み