# In-House LLM Serving at Netflix
**著者**: Liping Peng, Hakan Baba, Nicolas Hortiguera, ZQ Zhang, Santino Ramos, Binh Tang, Lanxi Huang, Daneo Zhang, Lingyi Liu, Abhishek Agrawal, Shaojing Li(Netflix AI Platform)
**発行**: 2026-07 / Netflix TechBlog
**URL**: https://netflixtechblog.com/in-house-llm-serving-at-netflix-a5a8e799ea2c
---
## 問題設定
多くの組織はホスト型APIを通じてLLMを利用するが、[[Netflix]]はモデルデプロイから推論まで、既存の本番環境の内部でフルスタックを自前運用する道を選んだ。別建てのML専用サイロを作らず、既存のメンバースケールML基盤に統合する方針である。本記事は、代替案が真剣に検討された意思決定——エンジン選定、モデルパッケージング、APIサーフェス設計、デプロイ戦略、出力制約の強制——に絞り、設計段階では想定できず本番負荷で初めて露見したトレードオフも合わせて報告する。
## アーキテクチャ概要
Netflixのメンバースケールでは、ルーティング・A/Bテストロジック・候補生成・特徴量取得・推論・後処理・各段階のロギングをエンドツーエンドで扱う、JVMベースの統合サービングシステムが前段に立つ。リアルタイムパスとキャッシュ済みバッチパスの両方をサポートする。呼び出し元が推論に到達する経路は2つあり、既存のgRPC経由の統合サービングシステムと、新しいLLM駆動アプリケーションが使う直接HTTPパスである。
推論の実行場所はモデルによって異なる。小規模なCPUモデルはリモート呼び出しのオーバーヘッドを避けるためインプロセスで動く。大規模モデルはGPUを必要とし、サービングシステムは前処理・後処理をローカルで行いつつ推論自体はリモートサービスである**Model Scoring Service(MSS)**へ委譲する。MSSはXGBoost・TensorFlow・PyTorch・LLMを単一インターフェース越しに支える共有推論バックエンドで、その下でNVIDIA Triton Inference Serverがモデルロード・バッチング・GPUスケジューリングを管理する。
Tritonの上にはJavaの制御プレーンが乗り、デプロイ・バージョニング・ヘルスチェック・オートスケーリング・マルチリージョンロールアウトを担当する。モデル作者は成果物をパッケージングしてデプロイを設定するだけで、制御プレーンがGPUインスタンスをプロビジョニングし、Tritonを設定し、無停止アップグレードを調整する。
![[_attachments/in-house-llm-serving-at-netflix/fig01-serving-architecture.png]]
(Figure 1. サービングアーキテクチャ全体図。gRPC経由の統合サービングシステムと、LLM向け直接HTTPパスの2経路を示す。)
## 設計判断と実装
エンジン・パッケージング・APIサーフェス・ロールアウトの4つの判断がこの基盤を形作る。各判断が次を制約するため、依存順に述べる。
### paved-pathエンジンとしてのvLLM
プラットフォームは元々、当時性能に優れTritonとの統合も済んでいたTensorRT-LLM上に構築されていた。
2026年夏までに2つの変化があった。オープンソースエンジンが専用スタックとの性能差をほぼ埋めたことと、Netflixのワークロード構成が埋め込み生成、ランキング・検索向けのprefill専用推論、自己回帰デコード、ステップごとに非自明な制約ロジックを持つカスタムモデルへと広がったことである。このワークロード構成に対して再ベンチマークを行い、運用適合性を根拠に**paved-pathエンジンとしてvLLMを選定した**。
- **多段階コンパイルパイプラインなしにカスタムモデルアーキテクチャをロードできる** — 非標準モデルでのイテレーションが速い。
- **カスタムデコードロジック向けの拡張フック** — 後述の制約付きデコーディング作業に必須。
- **デバッグ性** — 従来のTensorRT-LLMのようなコンパイル済みエンジンより、失敗や中間状態を検査しやすい。
- **習熟度** — 多くのML実践者が研究段階で既にvLLMを使っており、研究から本番への引き継ぎコストが下がる。
### vLLMをTritonに統合する
vLLMを選んだ後の判断は、モデルをどうパッケージングするかである。Tritonは2つの方式をサポートし、その選択がメンテナンス性——モデル成果物がフロントエンドのアップグレードとどれだけ密結合するか——を大きく左右する。
- **Pythonバックエンド**: 作者がパッケージング時に明示的な入出力テンソル仕様を定義する。この仕様は成果物に固定され、サードパーティベンダーのフロントエンドのリクエストビルダーが期待する仕様と一致していなければならない。そのため、I/O仕様に触れるフロントエンドアップグレードのたびにパッケージングコード側の協調変更が必要になり、怠ると実行時にリクエストが失敗する。
- **vLLMバックエンド**: 成果物はモデル重みとトークナイザを指すJSON設定にすぎない。Tritonのvllmバックエンドがこの設定を読み、デプロイ時にI/Oテンソル仕様を動的に生成する——作者が仕様を定義することはない。モデルとフロントエンドが独立に進化できる。
vLLMバックエンドがアーキテクチャ上正しい既定値である。本番で2点が問題になった。
- **Triton/vLLMのバージョン不整合**: TritonのvLLMバックエンドは特定のvLLM API surfaceに対してコンパイルされている。両者がずれると——例えばTriton 25.09が、vLLM 0.11.2で削除されたモジュール`vllm.engine.metrics`をインポートしようとすると——バックエンドが完全にロードに失敗する。プラットフォームはサービスイメージのビルド時に互換バージョンを固定し、パッケージング時にモデル作者がvLLMバージョンを上書きすることを禁止する必要がある。
- **カスタムモデルロジック**: vLLMバックエンドは標準的なHuggingFace互換モデルを前提とし、推論ライフサイクル全体を扱う。カスタム前処理・後処理・非標準実行(アンサンブルパイプライン、カスタムトークナイゼーション)を必要とするモデルは、`execute()`を完全に制御できるPythonバックエンドを使うしかない。このエスケープハッチは一定のモデル群に対して今後も必要であり続けるだろう。
### エコシステム互換のHTTPフロントエンド
エンジンとパッケージングが定まった次の問いは、呼び出し元がどうシステムに到達するかである。設計上の重要な目標は、LLMモデルを特別扱いしないことだった。XGBoostアンサンブルであれ大規模LLMであれ、すべてのモデルは同じgRPC呼び出しでスコアリングされ、同じクライアントライブラリ・ヘルスチェック・デプロイパイプラインを再利用する。OpenAI互換APIインターフェースが推論エンジン・オーケストレーションフレームワーク・評価ツール・クライアントライブラリすべてが話す、LLMエコシステムの事実上の標準インターフェースになっていることを踏まえ、**OpenAI互換APIをgRPCに加える追加フロントエンドとして公開する**ことにした。
この投資の見返りは実験から本番への移行パスに現れる。ホスト型モデルから、品質・レイテンシ・コスト・データプライバシーのために自前ホストするファインチューン済みモデルへ卒業する作業が、ほぼシームレスになる。同じAPI、最小限のコード変更で済む。
API裏側の実装は、NVIDIAのTriton OpenAI互換フロントエンドを再利用する。埋め込みTritonサーバーを起動し、リクエストスキーマをTriton推論リクエストへ変換する`TritonLLMEngine`でラップし、FastAPI経由でレスポンスを返す。KServeのHTTP/gRPCフロントエンドも並行して有効化されており、同じTritonインスタンスがJavaの制御プレーンからgRPCでも引き続きアクセス可能である。Tritonのフロントエンドをそのまま採用したことで、1つのギャップが露見した。スキーマ上は受理される`response_format`が、vLLMに届く前に黙って捨てられていた。そのため、JSON出力を要求した呼び出し元がguided decoding制約なしに処理を進め、プラットフォーム側でエラーが表面化しないまま不正なJSONを受け取りうる状態だった。フロントエンドをgit-subtreeで取り込みパッチを当て、リクエスト時に`response_format`をvLLMのguided decodingパラメータへ変換するようにした。
### デプロイ戦略
APIサーフェスとエンジンが定まった後に残る問いは、リクエストを落とさずに新バージョンをどうロールアウトするかである。GPUデプロイはCPUサービスより起動に時間がかかり、モデルバージョン間でI/Oスキーマが変わりうることが、さらに調整問題を上乗せする。プラットフォームは2つの戦略を提供する。
- **Red-Black**は新バージョンを現行バージョンと並行してデプロイする。新インスタンスがヘルスチェックを通過すると、トラフィックが段階的に移行し、新バージョンが同じ速度でスケールアップする一方で旧バージョンはスケールダウンする。どこかのステップが失敗すればアトミックなロールバックが発動する。Red-Blackはモデルインターフェースが安定している場合の正しい選択である。本番では、新バージョンがI/Oスキーマ変更(例: 新しいテンソル次元)を必要とする場合に**協調のギャップ**が露見した。上流の利用者は新モデルが完全に稼働するまで自身の設定を更新できないため、移行ウィンドウ中に必然的に「旧」リクエストを「新」デプロイに送ってしまい、それらは失敗する。
- **Versioned**は(modelId, modelVersion)の組ごとに独立したデプロイを維持することでこのギャップを解決する。複数バージョンが同時に稼働し、モデルデプロイと利用者の更新を分離する。利用者は新バージョンが完全に準備できるまで待ってから自分の設定を切り替えればよく、旧バージョンはその間もレガシートラフィックを提供し続ける。プラットフォームは非活動状態の古いデプロイを掃除するが、最新版は常に保持する。トレードオフは、移行期間中の**GPUコストの一時的な増加**である。
Netflixは、テンソル形状のような可変設定を推論モデル自体にバージョン非依存な形で埋め込み、より安価なRed-Blackパスを使えるようにすることを推奨している。Versionedは、破壊的なインターフェース変更が避けられない稀なケースに限定して使う。
## 運用上の注意点
この4つの判断に加え、設計段階では想定できなかった本番のギャップに触れた運用上の細部が2つある。
### 起動シーケンス
vLLM-on-Triton インスタンスの起動には、gRPCポートが開くまでにいくつかの協調したステップがある。うち2つは非定型的である。
- **モデルキャッシング**: 起動時にS3やHugging Faceから直接大規模LLMをダウンロードするのは、スケジューラが許容する範囲を超えてコールドスタートレイテンシを悪化させるほど遅い。モデル発表時点でAmazon FSx上にモデルを実体化しておくことで、ウォームスタートがオブジェクトストレージではなく高性能ファイルシステムにヒットするようにしている。
- **埋め込みTriton vs スタンドアロンTriton**: 利用者がOpenAI互換APIを必要とする場合、TritonはOpenAI互換フロントエンドプロセス内に埋め込みサーバーとして動作する。そうでなければスタンドアロンで動く。これはパッケージング時にデプロイ単位で設定される。
残りの起動シーケンスは機械的で、モデルパッケージの展開、Python `entry_points`経由のカスタムvLLMプラグインのインストール、Prometheusマルチプロセスディレクトリのクリーンアップ、エンジン準備完了までのgRPCポートのゲーティングからなる。
### 統合メトリクスエンドポイント
上記のPrometheusクリーンアップは、より広いオブザーバビリティのギャップを示唆している。vLLMは`PROMETHEUS_MULTIPROC_DIR`に`.db`ファイルとしてメトリクスを書き込み、Tritonは自身のPrometheusエンドポイント経由でサーバーレベルのメトリクスを報告する。どちらも互いの存在に気づかず、Tritonの組み込みブリッジは40以上あるvLLMメトリクスのうち9個しか橋渡ししない——トークンスループット、KVキャッシュ利用率、prefixキャッシュヒット率といった重要な指標を欠いたままである。
そこで両者を単一の`/metrics`エンドポイントに統合する軽量HTTPプロキシを追加した。TritonメトリクスをHTTP経由で取得し、vLLMメトリクスをPrometheusの`MultiProcessCollector`でディスクから読み込み、統合出力を返す。既存のダッシュボードとアラートは変更なしに動作する。
## ディープダイブ: 大規模な制約付きデコーディング
Netflixの一部の本番ワークロードは、トークン生成に対する細粒度の制御に強く依存している。推論後にビジネスロジックを適用する(無効な生成に対価を払い、再試行や修復を行う)代わりに、制約をデコードループの内部に押し込み、モデルが構造的に準拠した出力を生成するようにしている。これをvLLMのカスタムlogits processorインターフェース経由で実装しており、各制約を生成済みトークン履歴とともに進化する状態機械としてモデル化し、各ステップでトークン適格性マスクを発行する。異なるリクエストは異なるルールを適用するため、リクエストごとに独自の設定済みprocessorを持つ。
これをスケールさせる作業は2つのエンジンバージョンにまたがった。最初はvLLM V0にデプロイ(V1には機能的なギャップがあった)し、V1が成熟した2025年第4四半期に移行した。以下の2小節はその前後を示す。
### 最初の実装がスケールしなかった理由
当初のPure-Python実装は機能的には動作したが、スケーリングのボトルネックにぶつかった。vLLM V0では、カスタムlogits processorはリクエストごとに動く。GPUがバッチ全体のlogitsを生成し、CPUがそれをコピーして転送を待ち、その後に制約ロジックが各リクエストに対して逐次実行される——GILがPythonのリクエストごとの並列化を妨げるため逐次になる。したがって、logit処理のCPU時間はバッチサイズに比例して線形に増加し、テイルレイテンシを悪化させる。モデルのforward passがGPU上で効率的にバッチ化されていても、エンドツーエンドのレイテンシはCPUバウンドになる。単一リクエストのベンチマークでは見えず、現実的な並行度の下で初めて表面化するボトルネックである。
![[_attachments/in-house-llm-serving-at-netflix/fig02-logits-processor-v0-serial.png]]
(Figure 2. vLLM V0でのlogits processorのCPU上での逐次実行。バッチサイズが増えるほどCPU処理時間が線形に伸びる様子を示す。)
### vLLM V1がバッチレベル設計を可能にした
構造的な修正はvLLM V1で到来した。V1はlogits処理をバッチレベルへ移した。カスタムprocessorをバッチレベルのデータ構造上で動くよう書き直し、多数のリクエストにまたがるマスクを一括計算し、GILを回避するためにホットパスをマルチスレッドC++で再実装した。V1のAPIは、`update_state(batch_update)`経由でバッチメンバーシップの変化を明示的に追跡することを要求する——V0のリクエストごとのインターフェースより複雑だが、動的に変化するバッチの中で正しい状態を維持するために必要である。
![[_attachments/in-house-llm-serving-at-netflix/fig03-logits-processor-v1-batched.png]]
(Figure 3. vLLM V1でのバッチ化されたlogits processor実行。バッチサイズが増えても処理時間がフラットに保たれる様子を示す。)
### 運用面の堅牢化
この段階で性能はもはやボトルネックではなくなった。しかし、デコードループ内のステートフルな制約ロジックは、設計段階では想定していなかった2つの問題を持ち込んだ。
- **部分的なprefill**: V1はchunked prefillingを行うため、1つのリクエストが複数のエンジンステップにまたがってprefillされうる。`BatchUpdate`には、あるリクエストが完全にprefillされたのか部分的にしかprefillされていないのかを区別する粒度がなく、内部トラッキングを追加する必要があった。
- **プリエンプション**: メモリ圧迫下では、vLLMは部分的に完了したリクエストのKVキャッシュを退避し、後で異なるプロンプトと出力トークン列で再スケジュールすることがある。これは、出力トークン列が単調に増加するという状態機械の前提を壊す。トークン履歴がデコードステップ間で縮んだことを検知し、状態機械をリセットして新しいプロンプトから再初期化するようにした。
## まとめ
Netflixは、低レイテンシ・深いカスタマイズ性・既存インフラとの統合という広範な本番ML要件のために、LLMサービング基盤を構築することを目指した。結果として得られたのは、vLLMとTritonの上に構築され、一貫したAPIの背後に統合された、ML実践者に実験から本番への速いパスを提供するシステムである。
得られた教訓はしばしば細部に宿っていた——バージョン固定、暗黙のAPIギャップ、パッケージングのトレードオフ——が、それらに対処したことでプラットフォームは意味のある形でより堅牢になり、開発者体験も滑らかになった。次の投資は、摩擦が予想される箇所を反映している。
- 品質を犠牲にせずプロンプト長を減らす**システムプロンプト圧縮**。
- vLLM V1の**非同期スケジューリング**。
- CPUコードではなく融合GPUカーネルとして動く**ベクトル化されたlogits processor**。
- メモリフットプリントを減らしスループットを増やす**低精度モデルバリアント**。
Netflixはこの領域が進化するにつれ、オープンソースコミュニティと引き続き密接に連携していく。
## コントリビューション
このシステムは、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が、整合性・優先順位付け・実行を確実にするマネジメントリーダーシップを提供した。
## 関連概念・エンティティ
- [[Netflix]] — 本記事が実装例。組織としてのプロファイルは entity ページ参照
- [[vLLM]] — paved-pathエンジンとして選定。V0→V1移行によるlogits processorのバッチレベル化
- [[NVIDIA]] / [[TensorRT-LLM]] — 旧paved-pathエンジン。移行の起点
- [[Triton Inference Server]] — GPU推論の共有バックエンド。Python/vLLMバックエンドの選択とOpenAI互換フロントエンド
- [[制約付きデコーディング]] — logits processorによる状態機械ベースの出力制約強制
- [[LLM推論]] — Prefill/Decodeの段階的性質とKVキャッシュ管理の文脈