はじめに: Netflix はなぜ自社 LLM サービングを選んだのか

多くの組織は LLM をホスティング API として利用します。しかし Netflix は、モデルデプロイから推論までを自社のプロダクション環境で直接運用しています。これは単なる ML サイロではなく、既存インフラに統合された形です。本記事では、Netflix の技術ブログで公開された内容をもとに、実際のプロダクション負荷で明らかになった設計判断とトレードオフを分析します。

Netflix の LLM サービングシステムは、JVM ベースの統合サービングシステムを中心に、ルーティング、A/B テスト、候補生成、フィーチャー取得、推論、後処理、ロギングをすべて処理します。gRPC パスと HTTP パスの両方をサポートし、小さい CPU モデルはインプロセスで、大きい GPU モデルはリモートサービス(Model Scoring Service)に委譲します。

アーキテクチャ概要: vLLM と Triton の出会い

Netflix は当初 TensorRT-LLM を使用していましたが、2025 年夏時点でオープンソースエンジンの性能差が縮まり、ワークロードが多様化したため、vLLM に移行しました。vLLM を選んだ理由は以下の通りです。

  • カスタムモデルアーキテクチャを多段階コンパイルなしでロード可能
  • カスタムデコーディングロジックの拡張フックを提供
  • デバッグの容易さ
  • 研究段階で多くの実務者がすでに使用

vLLM を Triton に統合する方法も重要な決定でした。Triton は 2 つのバックエンドを提供します。

  • Python バックエンド: パッケージング時に I/O テンソル仕様を定義する必要があり、フロントエンドのアップグレード時にはパッケージングコードも変更する必要があります。
  • vLLM バックエンド: JSON 設定でモデルウェイトとトークナイザーを指定するだけで、I/O テンソル仕様を動的に生成します。モデルとフロントエンドは独立して進化できます。

Netflix は vLLM バックエンドをデフォルトにしましたが、本番環境で 2 つの問題が発生しました。

問題 1: Triton/vLLM バージョンの不一致

Triton の vLLM バックエンドは特定の vLLM API に合わせてコンパイルされています。バージョンがずれると(例: Triton 25.09 が vLLM 0.11.2 で削除されたモジュールを import)、バックエンド自体がロードされません。プラットフォームはサービスイメージに互換バージョンを固定し、モデル作成者がパッケージング時に vLLM バージョンを上書きできないようにする必要があります。

問題 2: カスタムモデルロジック

vLLM バックエンドは標準的な HuggingFace 互換モデルを期待します。カスタム前処理/後処理や非標準実行が必要なモデルは Python バックエンドを使用する必要があります。このエスケープハッチは一部のモデルで今後も必要になるでしょう。

API 設計: OpenAI 互換インターフェースの採用

Netflix はすべてのモデルを同じ gRPC 呼び出しでスコアリングします。これは既存のクライアントライブラリ、ヘルスチェック、デプロイパイプラインを再利用するためです。同時に、LLM エコシステムの事実上の標準となった OpenAI 互換 API を追加のフロントエンドとして公開します。

この決定の利点は、実験からプロダクションへの移行がシームレスであることです。ホスティングモデルからファインチューニング済みの自社モデルへの移行時に API が同じなので、コード変更がほとんどありません。

実装は NVIDIA Triton の OpenAI 互換フロントエンドを再利用します。しかし 1 つのギャップがありました。response_format パラメータが黙って無視され、JSON 出力を要求しても制約デコーディングが適用されませんでした。Netflix はこのフロントエンドをパッチし、response_format を vLLM の guided decoding パラメータに変換しました。

デプロイ戦略: Red-Black vs Versioned

GPU デプロイは CPU サービスより起動時間が長く、モデルバージョン間で I/O スキーマが変わることがあります。Netflix は 2 つの戦略を提供します。

  • Red-Black: 新バージョンと旧バージョンを並行してデプロイし、ヘルスチェック後にトラフィックを段階的に移行します。インターフェースが安定している場合に適しています。ただし I/O スキーマが変わると、アップストリームコンシューマーは新モデルが完全にライブになるまで設定を更新できないため、移行期間中に失敗が発生します。
  • Versioned: (modelId, modelVersion) のペアごとに独立したデプロイを維持します。複数バージョンが同時にサービスを提供し、コンシューマーは新バージョンの準備ができるまで待ってから設定を切り替えられます。トレードオフは、移行期間中に GPU コストが一時的に増加することです。

Netflix は可変設定(例: テンソルシェイプ)を推論モデルに直接埋め込み、バージョンに依存しないようにすることを推奨します。そうすればより安価な Red-Black パスを使用できます。Versioned はインターフェース変更が避けられないまれなケースに限定します。

運用ノウハウ: 起動シーケンスとメトリクス統合

起動シーケンス

vLLM-on-Triton インスタンスを起動する際、重要な点が 2 つあります。

  • モデルキャッシング: LLM を起動時に S3 や Hugging Face からダウンロードすると、コールドスタートの遅延が長くなります。Netflix はモデル発表時に Amazon FSx にモデルを具体化し、ウォームスタートが高性能ファイルシステムを使用するようにしています。
  • 組み込み vs スタンドアロン Triton: OpenAI 互換 API が必要な場合は組み込みサーバーとして、そうでない場合はスタンドアロンとして実行します。これはパッケージング時にデプロイごとに設定します。

統合メトリクスエンドポイント

vLLM は PROMETHEUS_MULTIPROC_DIR に .db ファイルとしてメトリクスを書き込み、Triton は独自の Prometheus エンドポイントでサーバーメトリクスを報告します。しかし Triton のブリッジは 40 以上の vLLM メトリクスのうち 9 つしか公開しません。トークン処理量、KV キャッシュ利用率、プレフィックスキャッシュヒット率などの重要なメトリクスが欠落します。

Netflix は軽量 HTTP プロキシを追加し、2 つのメトリクスを単一の /metrics エンドポイントに統合しました。既存のダッシュボードとアラートは修正なしで動作します。

ディープダイブ: 制約デコーディングのスケーリング

一部の Netflix プロダクションワークロードでは、トークン生成の細かい制御が必要です。推論後にビジネスロジックを適用する代わりに、デコードループ内に制約を入れ、モデルが最初からルールを守る出力を生成するようにします。

vLLM のカスタム logits processor インターフェースを使用し、各制約を状態機械としてモデル化します。リクエストごとに異なるルールが適用されるため、各リクエストに独自のプロセッサが割り当てられます。

最初の実装のスケーラビリティ限界

vLLM V0 では、カスタム logits processor はリクエストごとに実行されます。GPU がバッチ全体の logits を生成すると CPU がコピーし、GIL のため順次実行されます。バッチサイズが大きくなるにつれて CPU 時間が線形に増加し、テールレイテンシが発生します。これは単一リクエストのベンチマークでは見えないボトルネックです。

vLLM V1 でのバッチレベル設計

vLLM V1 は logits 処理をバッチレベルに移行しました。Netflix はバッチレベルのデータ構造でマスクを計算し、ホットパスを C++ で再実装して GIL を回避しました。update_state(batch_update) を通じて動的に変化するバッチで状態を維持します。

運用の強化

パフォーマンス問題が解決された後、2 つの予期しない問題が発生しました。

  • 部分プリフィル: V1 はチャンクプリフィルを実行するため、リクエストが複数のエンジンステップにわたってプリフィルされる可能性があります。バッチ更新の粒度では完全/部分プリフィルを区別できないため、内部追跡を追加しました。
  • プリエンプション: メモリ圧力下で vLLM が進行中のリクエストの KV キャッシュを退避し、別のプロンプトと出力トークンリストで再スケジュールする可能性があります。これは出力トークンリストが単調増加するという状態機械の仮定を壊します。トークン履歴が縮小することを検出したら、状態機械をリセットし、新しいプロンプトから再初期化します。

日本市場への適用コンテキスト

日本でも LLM を実際のサービスに適用する動きが活発です。特に大規模なトラフィックを処理するサービスでは、Netflix のアプローチが良い参考になります。ただし、日本のレガシーシステムとの統合は重要です。既存の Java ベースシステムに gRPC を導入するのは難しくありませんが、Triton と vLLM のバージョン互換性には特に注意が必要です。また、GPU リソースが限られている場合、Red-Black デプロイよりも Versioned 戦略がコスト負担になる可能性があるため、サービス特性に合わせた戦略を選ぶ必要があります。

この技術の限界と注意点

  • バージョン管理の複雑さ: Triton と vLLM の互換性を継続的に確認する必要があります。カスタムパッチが必要になることもあります。
  • カスタムモデルサポートの制限: vLLM バックエンドは標準モデルのみサポートするため、カスタムロジックが必要なモデルは Python バックエンドに移行する必要があります。これはメンテナンス負担を増加させます。
  • 制約デコーディングの複雑さ: 状態機械ベースの制約は実装が難しく、vLLM のバージョンによって API が変わる可能性があります。

次のステップの学習方向

  • vLLM V1 の非同期スケジューリングとベクトル化された logits processor を調べると、パフォーマンス最適化に役立ちます。
  • システムプロンプト圧縮技術を学び、プロンプト長を短縮する方法を検討しましょう。
  • 低精度モデル変換を試し、メモリ使用量を減らしてスループットを向上させる方法を実験しましょう。

まとめ: プロダクションで学んだ教訓

Netflix の LLM サービング基盤は、vLLM と Triton を基盤に一貫した API の背後で統合されています。実務から得た教訓は、バージョン固定、サイレントな API ギャップ、パッケージングのトレードオフといった細部にありました。これらの問題を解決することで、プラットフォームはより堅牢になり、開発者体験も向上しました。

この記事が実際の LLM サービングシステムを設計する際に役立つことを願っています。原文は Netflix テックブログ で確認できます。

合わせて読みたい記事

Netflix in-house LLM serving architecture with vLLM and Triton on GPU servers

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。