PD 分離
Prefill/Decode(PD)分離では、デプロイを別々に実行するロールに分割します。Prefill はプロンプトの KV キャッシュを計算し、Decode はそこからトークンを生成し、手前のルーターが各リクエストを両者にまたがって処理します。KV キャッシュは再計算せず、ネットワークで転送します。
2 つのフェーズが求めるものは対照的です。Prefill は演算律速で、幅広いテンソル並列を求めます。Decode はメモリ帯域律速で、多数の同時シーケンスを求めます。単一のデプロイでは両方に同じパラメーターを選ぶ必要がありますが、分離すると、それぞれの役割に合う規模と調整を選択できます。また、プロンプトが重いワークロードでは Prefill を増やし、生成が長いものでは Decode を増やすように、両者の比率をワークロードに合わせられます。
実際のコストもあります。リクエストの途中でネットワークをまたぎ、維持するロールは 1 つから 3 つに増え、グループの可用性は最も可用性の低いロールに制約されます。分離が有益なのは、2 つのフェーズの要求が実際に競合する場合です。
有効な場合と有効でない場合
判断には、次の 3 点を確認します。
- Prefill と Decode が異なるパラメーターを必要とするか。 1 組のエンジンパラメーターで両フェーズを十分に処理できる場合、分離してもスループットは増えず、構成だけが複雑になります。
- プロンプトと生成の比率が 1:1 から大きく離れているか。 利点は両者の規模を個別に調整できることにあり、負荷が異なる場合にのみ効果があります。
- KV 転送は十分に速いか。 プロンプトの KV キャッシュは毎回ネットワークを通ります。低速または共有の回線では、節約した Prefill 時間が転送時間に置き換わります。
単一 GPU、またはプロンプトと生成の両方が短いワークロードでは、通常は分離しない方が高速です。
分離モデルのデプロイ
- 通常どおりモデルをデプロイし、
Disaggregationセクションを展開して有効にします。 - PD モードを選択します。モードは、エンジン、KV コネクター、ルーターを含む完全なレシピです。コネクター、ハンドシェイク用の変数、ポートはこの選択で決まり、フォームには表示されません。利用可能なモードはバックエンドに依存し、別のエンジン用のモードは理由付きで無効表示されます。
- 各ロールのレプリカ数を設定します。これが比率です。Prefill が 2、Decode が 1 なら 2P1D グループです。
- 両者で異なる設定をロールごとに上書きします。継承のままのフィールドはデプロイレベルの値を使用するため、差分だけを入力すれば十分です。
- 保存します。
ルーターは自動管理されます。イメージ、コマンド、ピアアドレス、ヘルスパスはモードのレシピで決まるため、フォームでは指定しません。指定するのは、レシピからは分からないルーター独自の引数と環境です。プラットフォームが導出した接続引数も同じ一覧に事前入力されるため、実際に生成されたものを確認できます。変更せずに残した行は保存時に再び取り除かれ、引き続き Server が生成します。
ルーターは構造上 1 レプリカなので、数の設定はありません。2 つ目を作るとプレフィックスキャッシュが分散し、グループのアドレスが 2 つになってしまいます。
独自のルーターが必要な場合は、custom モードを選択し、ルーターロールではなくデプロイにイメージと実行コマンドを設定します。ロールは両方を継承します。1 つのイメージにエンジンとルーターを含め、ロールに応じて実行を切り替えます。2 か所に指定するのと同じ処理を、設定の同期が必要な場所を増やさずに行えます。
グループの起動順序
グループ起動中に表示される内容を理解するために、次の順序を把握してください。
- Prefill と Decode のインスタンスは、1 つのトランザクションで同時に作成されます。Gang Admission を利用できる場合は、まとめて受け入れます。半分しか起動していないグループでは、サービスを提供できないためです。
- 両者に実行中のメンバーができると、ルーターを作成します。コマンドラインにピアのアドレスが含まれ、そのアドレスはピアが起動するまで存在しないため、最後に作成します。
- ルーターが起動すると、グループのエンドポイントを登録します。登録するのはルーターだけです。Prefill と Decode のメンバーは独自のポートで OpenAI 形式の API を提供しますが、単独では完全なリクエストに応答できません。
そのため、サービス提供前にしばらく Partially ready と表示されます。これは正常な経路であり、障害ではありません。
グループ状態の読み方
| 表示 | 意味 |
|---|---|
Pending | まだ実行中のメンバーがありません。 |
Partially ready | メンバーは起動していますが、準備済みメンバーのないロールや未登録のルーターエンドポイントがあり、グループはまだサービスを提供できません。 |
Running | 各ロールに準備済みメンバーが 1 つ以上あり、エンドポイントが登録済みです。 |
Ratio 2:1 (currently 1:1) | サービスは提供できますが、指定した比率ではありません。状態の代わりではなく、状態の横に表示します。 |
時計アイコン / Config changed | 実行中のメンバーは表示中の構成より古く、再起動するまでロールをスケールアップできません。構成変更の適用を参照してください。 |
指定比率に足りないことは状態ではなく、能力低下です。3P1D を指定したグループが 2P1D で動作していても、想定したスループットではないもののサービスは提供できます。
構成変更の適用
分離デプロイを編集しても、再起動は行いません。変更を保存し、グループが古い構成で動作していることを示しますが、再起動するまでは動作を変更しません。
これは意図的な動作です。メンバーを順番に再起動すると、一時的に新しい構成の Prefill と古い構成の Decode が組み合わされ、エンジンはその組み合わせを受け入れてしまいます。たとえばコンテキスト長の不一致でも、ハンドシェイクと KV 転送が成功し、短いプロンプトには正常に答えます。Decode のウィンドウより長いプロンプトが来たときだけ、すでに Prefill の計算を終えた後で失敗します。不正な応答より再起動を選ぶため、グループ全体を停止してから再起動します。
変更を適用するには、デプロイの Restart を使用します。グループ全体を終了し、現在の構成で再形成します。一部のロールだけの再起動は、上記の混在を起こす操作なので提供しません。
構成変更が保留されていなければ、スケーリングはグループ全体を再起動せずに各ロールのインスタンス数を変えます。ルーターの動作は、pd-modes.yaml の router.membership_update に依存します。
| ポリシー | 同梱モード | スケーリングまたはメンバー復旧時の動作 |
|---|---|---|
dynamic | vLLM NIXL、SGLang Mooncake/NIXL、vLLM Ascend Mooncake | GPUStack がメンバーシップ API でルーターを更新します。 |
restart | vLLM Mooncake | 追加した GPU メンバーが準備できた後、GPUStack がルーターを置き換え、起動時のピアとブートストラップメタデータを更新します。削除または再起動したメンバーについては、両側に利用可能なピアができ次第更新します。存続する GPU インスタンスは動き続けますが、サービスは短時間利用できなくなり、処理中のリクエストが失敗する場合があります。 |
manual | Custom | 運用者がルーターのピアとブートストラップメタデータを更新し、必要に応じてルーターを再起動します。 |
ユーザーがルーターの実行コマンドを指定した場合、レシピが自動更新に対応していても、メンバーシップは手動管理になります。デプロイフォームとレプリカ編集画面には、カタログのポリシーを表示します。復旧にルーターの置き換えや運用者の操作が必要な場合は、インスタンス削除時にも警告します。
静的ルーターのスナップショットには、インスタンス ID、再起動世代、HTTP アドレス、名前付きポートを含みます。そのため、同じアドレスでの置き換えや再起動でも、Mooncake のブートストラップメタデータを更新します。スナップショットのない既存ルーターは、ピアの準備後に一度置き換えます。
保存した変更が再起動を待っている間は、ロールをスケールアップできません。 新しいメンバーは編集後の構成で作成され、古い構成のメンバーと混在するためです。スケール変更は、構成変更とともに再起動時に有効になります。
スケールダウンは、編集が保留中でもすぐに適用します。メンバーを減らしても、異なる構成の組み合わせは作られないためです。
共有 KV キャッシュとの併用
分離デプロイと共有 KV キャッシュは、互いを置き換えるものではなく補完的です。GPUStack は通常どおりキャッシュサービスを接続すると、2 つのコネクターをエンジンが受け入れる 1 つの構成にまとめます。
キャッシュを使用する各 vLLM ロールは MultiConnector[PD, Store] を生成します。ラッパーは Prefill で kv_producer、Decode で kv_consumer を使用します。各子コネクターは、転送またはストアが要求するロールを維持します。
| モード | キャッシュなし | プロセス内キャッシュ | キャッシュサービス |
|---|---|---|---|
vllm-nixl(NVIDIA の既定) | NixlConnector | NixlConnector + LMCacheConnectorV1 | Prefill のみ:NixlConnector + LMCache。LMCache のみ |
vllm-mooncake(NVIDIA) | MooncakeConnector | MooncakeConnector + LMCacheConnectorV1 | MooncakeConnector + MooncakeStoreConnector。Mooncake のみ |
vllm-ascend-mooncake | モデルに応じて選択する Mooncake PD コネクター | PD コネクター + SimpleCPUOffloadConnector | PD コネクター + AscendStoreConnector。Mooncake のみ |
プロセス内 LMCache の子コネクターは、両方のロールで kv_both を使用します。NIXL で共有 LMCache を使用できるのは Prefill だけです。MP コネクターは、MultiConnector がリクエストの読み込みを NIXL に割り当てた場合でも、キャッシュ済みプレフィックスを読み込むことがあり、2 回目の非同期完了で Decode がクラッシュする可能性があります(vLLM #53049)。NVIDIA Mooncake Store は Prefill で kv_both、Decode で kv_consumer を使用します。Ascend Store は Prefill で kv_producer、Decode で kv_consumer を使用します。これらの構成は、vLLM 0.29 Mooncake Store と vLLM-Ascend 0.23 KV pool に従います。
Ascend は、Qwen3、GLM-5、DeepSeek V3.1 などの標準 KV レイアウトでは MooncakeConnectorV1、DeepSeek V4、V4 Flash、V4 Pro などのハイブリッドレイアウトでは MooncakeHybridConnector を選択します。選択はデプロイ名ではなく、圧縮 Attention や再帰層を含む事前学習構成を参照します。両ロールは保存された同じ選択を使用します。レイアウトメタデータを取得できない場合は、ハイブリッドコネクターが標準レイアウトへのフォールバックを処理します。GPUStack は、デプロイのモデル入力に対する選択を保持するため、あるメンバーの起動後にメタデータが利用可能になっても、別のメンバーが異なるコネクターを使用することはありません。
NIXL とキャッシュの組み合わせは、vLLM 0.26 から利用できる実ブロックの契約を使用します。PD レシピは対応するキャッシュプロバイダーとコネクターの設定を宣言しますが、キャッシュサービスのリリースやクライアントパッケージ要件は定義も検証もしません。サービスとクライアントの互換性は、キャッシュプロバイダーが管理します。runner の依存は引き続き 0.5.4 です。NVIDIA の両モードでは、同梱 runner の基準は vLLM 0.27.1 以降です。それらの CUDA runner は、エンジンのイメージから実行する vllm-router 0.1.15 を提供します。同じ理由で、Ascend モードは CANN 0.23 を基準にします。これらは GPUStack の同梱イメージの下限であり、各コネクターを初めて提供した upstream リリースを示すものではありません。
NVIDIA Mooncake で共有 Store を使用する場合には、さらに 0.29 以上が必要です。GPUStack の 0.29 runner には、拒否された読み込みの修正を含みます。Store の子コネクターは、PD の子コネクターに割り当てられた読み込みをスキップする必要があります。MultiConnector は選択していない Store に外部トークン数ゼロを渡し、この修正は保留中の読み込みを発行する前に can_load を確認します。Decode 側のキャッシュヒットにも適用します。標準の vLLM 0.29 には、このバックポートが必要で、upstream の 0.30 には含まれます。runner の修正を参照してください。
必要な各ロールに、それぞれサービスを接続してください。ドロップダウンは、モード、プロバイダー、ロールでサービスを絞り込みます。API はこれらの互換性ルールを強制します。PD のエンジンバージョン範囲は助言的なものです。範囲外の組み込みエンジンバージョンを選ぶと、モードまたは有効なキャッシュへの警告を表示しますが、デプロイとキャッシュの注入は許可します。カスタムバージョンと明示的なイメージでは、これらの警告を表示しません。起動失敗は、インスタンスの状態とログで報告します。
現在、Auto はバックエンド、アクセラレーターのランタイム、プラットフォームに基づいて runner を選択します。PD モードやキャッシュ構成の範囲ではバージョンを絞り込みません。SGLang は、vLLM の MultiConnector ではなく、フラグと構成ファイルで LMCache または MeshFusion を接続します。
NVIDIA Mooncake は、推論イメージ内に転送エンジンのランタイムと RDMA 対応が必要です。GPUStack runner イメージは Mooncake の転送および期限切れメトリクスも公開し、転送カウンターは Prefill で読み取ります。GPUStack は一意の Prefill ブートストラップポートを割り当て、ルーターに渡します。vllm-router 0.1.15 では、メンバーシップの変更時にルーターを再起動してブートストラップメタデータを更新するため、トラフィックが短時間中断する場合があります。
ネットワーク要件
Kubernetes:Pod がクラスターに要求するもの
グループのメンバーには、ホストネットワーク、hostPort、ホスト IPC、デバイスマウントの 4 つすべてが必要です。これは Pod Security Admission の baseline レベルを超えます。GPUStack は所有する名前空間に適切なラベルを設定します。独自に用意したクラスターでの意味と、その対象外については、名前空間と Pod Security Admissionを参照してください。
KV 転送のインターフェース
KV キャッシュは毎回ネットワークを通るため、使用するインターフェースは重要です。転送層は、自動選択ではコンテナブリッジ(docker0、br-*、flannel.1)を選び、ピアから到達できないアドレスを公開することがあります。この場合、構成エラーではなく、ハンドシェイク時のコネクターエラーになります。
GPUStack は Worker ごとにインターフェースを導出します。
- ルーティング可能なインターフェースが 1 つ — それを使用します。設定は不要です。
- ルーティング可能なインターフェースが複数 — GPUStack は推測しません。その Worker の
--kv-transfer-ifname、Worker 構成キーkv_transfer_ifname、またはGPUSTACK_KV_TRANSFER_IFNAMEに、KV トラフィックを流すインターフェースを設定してください。検出した候補は Worker ログに表示します。
管理用 NIC と RDMA NIC を持つ場合など、複数のネットワーク系統があるマシンでは明示的に設定してください。管理用 NIC が適切なことは、ほとんどありません。
ポート
各ロールには独自の HTTP ポートと、コネクターが要求するポートが必要です。GPUStack は Worker のサービスポート範囲から連続した帯域で割り当て、同一ホスト上のメンバー同士が衝突しないようにします。
既定の範囲には 64 ポートあり、多くのデプロイには十分ですが、すべてには足りません。ランクごとのポートを使うコネクターでテンソル並列 8 のメンバーを実行すると、9 ポートを消費します。同一ホストでそのようなメンバーを複数実行する場合は、--service-port-range を広げてください。
GPUStack が管理できない 3 種類のポート
一部のコネクターは、エンジンが実行時に選択し、GPUStack が読める場所には宣言されないポートをバインドします。
- ホストのエフェメラルポート範囲から取得する、SGLang のランクポート。
- Mooncake のランクごとの転送エンジンのハンドシェイクポート。
- Mooncake のセグメントポート。
これらは構成ファイルの値が実際にバインドされるポートではないため、GPUStack は予約もホストポートとしての宣言もできません。15000~17000、20000~20800 付近で観測されており、既定のサービスポート範囲(40000~40063)が重ならないのは、予約ではなく範囲が分離されているためです。
そのため、--service-port-range をこれらの帯域に移動しないでください。 実測では、コネクターポート帯域を 20001 からにすると、メンバー自身の 2 番目のランクが 20003 をバインドしようとし、直前にエンジンが選んだ転送エンジンのポートが使用済みであることが分かりました。同じデプロイは、帯域を既定範囲にすると最初の試行で起動しました。
メンバーが再起動を繰り返し、Running に到達しない場合は、エンジンログで Address already in use を確認してください。
ポートの重複排除が対象とするのも、1 つの Worker プロセスだけです。同じホスト上の 2 つ目の GPUStack Worker や、手動で起動したコンテナは対象外です。
制約
グループが誤った応答を提供する構成は、実行時ではなく保存時に拒否します。
- Prefill と Decode は同じコンテキスト長を宣言する必要があります。 エンジンはこの一致を確認せず、不一致は長いプロンプトが来るまで分かりません。
- Decode のテンソル並列度は Prefill 以上である必要があります。 エンジンも検証しますが、構成問題ではなく Decode 内部のエラーとして報告します。
- 両側で、データ型、KV キャッシュのデータ型、ブロックサイズ、KV キャッシュレイアウトが一致する必要があります。
- ロールのエンジンは、選択したモードで構成できるものに限ります。 ロール間で異なるエンジンを使うには
customモードが必要で、接続パラメーターは自分で指定します。 customモードの分離デプロイでは、拡張 KV キャッシュを併用できません。 他のすべてのモードは両者を組み合わせます(共有 KV キャッシュとの併用を参照)。customはコネクター構成を注入しないため、キャッシュを組み合わせる先がありません。組み合わせた構成を自分で記述してください。- スケジュールに基づくスケーリングと分離は併用できません。
vllm-nixl の互換性チェックには、ロール固有の環境変数も含まれます。VLLM_KV_CACHE_LAYOUT は、同等の別名(NHD/LBNHC、HND/LBHNC)を受け入れます。MLA モデルでは、VLLM_MLA_DISABLE によって互換性のある潜在 KV 表現が得られる必要があります。NIXL の再帰状態転送は、対応ランタイムで VLLM_SSM_CONV_STATE_LAYOUT=DS を必要とします。チャンク化ローカル Attention では、VLLM_ALLOW_CHUNKED_LOCAL_ATTN_WITH_HYBRID_KV_CACHE と HMA フラグを合わせて確認します。明示的な Attention バックエンドの選択も比較します。
ロールの環境が未設定の場合は、デプロイの環境を継承します。明示的な辞書は、空の辞書も含めて置き換えます。モデル依存のチェックには、ソース構成とロールの上書きを使います。不明な構成、ランタイムバージョン、転送レイアウトは pairing_unverified で報告します。ネットワークアドレス、ポート、デバイス名、性能設定は、ロール間で異なっていて構いません。実験的なレイアウトの置換には、エンジンの方向と HMA の制限が引き続き適用されます。
制限事項
- ルーターは単一のレプリカとして実行します。
customルーターは、指定したとおりにデプロイします。 組み込みモードでは、upstream ルーターのサーキットブレーカーを既定の失敗 10 リクエストのしきい値に設定します。独自のルーターは、そのイメージの既定値を維持します。- vLLM モードでは、エラーを返す Prefill メンバーをローテーションから除外しません。 upstream ルーターは転送エラーだけを Prefill 失敗として扱います。Prefill の HTTP 500 はログに記録されますが、リクエストは Decode に進み、200 を返します。12 リクエストすべてで失敗する Prefill スタブによる実測でも、サーキットは開きませんでした。SGLang モードではブレーカーに反映します。いずれの場合も、寄与しなくなったメンバーを検出するのは、グループパネルの PD Effectiveness 値です。ルーター自体のリクエストカウンターでは分かりません。
- アイドル中に停止したメンバーは、次のリクエストで初めて検出されます。 検出はリクエスト経路に沿うため、トラフィックがなければ検出の契機がありません。ルーターのバックグラウンド確認は 1 分ごとです。アイドル後の最初のリクエストは再試行され、その後は停止メンバーを避けてルーティングします。
- グループのすべてのメンバーは、同じ GPU タイプを使用する必要があります。カードの種類が混在するグループは、アトミックに受け入れられません。
- ロールは Prefill、Decode、Router に限られます。
- プレフィックスを考慮したルーティングは Prefill の選択に使い、Decode の選択には使いません。 共有プレフィックスの利点があるのは Prefill なので、組み込みモードは近似プレフィックス一致でルーティングします。Decode は生成中ずっとリクエストを保持するため、ラウンドロビンで分散します。
vllm-ascend-mooncakeを含むすべての組み込みモードで、--prefill-policy cache_awareと--decode-policy round_robinを明示的に設定します。別のポリシーが必要な場合は、ルーターロールのパラメーターで上書きしてください。