TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

vLLM production-stack · LMCache · llm-d: エンジンフロントエンドとKVキャッシュ層のコードマップ

ステータス: ドラフト(2026-10-17)、一部実測済み。 基準バージョン: production-stack vllm-stack-0.1.13(コミット e8cb4959ebfa)、vLLM v0.31.0(コミット db9527a46873)、LMCache v0.5.6(コミット 3e11e10812b4)、llm-d v0.10.0(コミット f22bbaf3173a)、llm-d-router v0.11.0(コミット a5cbe600ebad)。小規模なクラスターにはvLLMが常時起動していないため、本日は新規Podの起動は行わず、実測値は2026-10-16にGPUノード1台(8GBクラスのノートPC用GPU)上で使い捨てPodとして測定した生データを、2026-10-17に再集計して引用しています。 作成ルール: 実測データを基本とし、測定できなかったものは「未測定」、確認できなかったものは「未確認」と表記します。

目次

1. 1行まとめ: 厨房は1つ、ホールマネージャーと冷蔵庫は取り替え可能

vLLMは、1枚(または複数枚)のGPU上でモデルを高速に実行する 厨房です。厨房を複数稼働させる場合、注文をどの厨房に割り振るかを決める ホールマネージャー, 厨房ごとにバラバラに管理されている下準備(KVキャッシュ)を共有する 冷蔵庫, そして厨房がどれほど忙しいかを通知する 注文ボード(メトリクス) が必要です。これら3つのプロジェクトは、厨房(vLLM)自体はそのままで、その周辺をそれぞれ異なる方式で満たします。

コンポーネント 役割 実行環境
production-stack ルーター(vllm_router) OpenAI互換のリクエストを受け付け、エンジンPodを1つ選んでそのまま転送する Deployment 1つ、Python・FastAPIプロセス
production-stack Helmチャート エンジンDeployment・ルーター・ServiceMonitor・Grafanaダッシュボードを一括インストール helm install 1回
vLLM KVコネクタ(KVConnectorBase_V1) エンジンスケジューラー・ワーカーが外部KVストレージを呼び出すインターフェース エンジンプロセス内
LMCache コネクタの実装。KVを256トークンのチャンクに分割し、CPU・ディスク・リモートに保存・取得する エンジンプロセス内、または別サーバー(MPモード)
llm-d Router(プロキシ + EPP) プロキシは接続のみを担当し、EPPがPodを選択してアドレスを返却する Envoy系ゲートウェイ + EPP Deployment(Go)
vLLM /metrics キュー長・KV使用率・プレフィックスキャッシュヒット率などをPrometheus形式で出力する エンジンAPIサーバー

KVキャッシュがなぜ必要で、LMCacheが何を節約するのかについては、 LLM KVキャッシュ解剖 シリーズで解説しています。本記事では原理の解説を省き、 どのファイルのどの関数がその処理を行っているのかに焦点を当てます。

2. 構成図

요청 경로: 앞단 → vLLM 엔진 ↔ LMCache 계층 앞단은 둘 중 하나 클라이언트 production-stack 라우터 Python · FastAPI 한 프로세스 roundrobin session prefixaware kvaware k8s 파드 감시 + /metrics 수집 llm-d Router 프록시(Envoy 등) + EPP(Go) 필터 점수 고르기 ext-proc 로 파드 주소만 돌려주고 전달은 프록시가 KV 이벤트(ZMQ) 구독 vLLM 엔진 파드 스케줄러 워커(GPU) KV 커넥터: LMCache 구현 GPU 안 KV 블록(16토큰) vLLM 엔진 파드 스케줄러 워커(GPU) KV 커넥터: LMCache 구현 GPU 안 KV 블록(16토큰) LMCache: 256토큰 청크 단위로 저장·조회 LocalCPU LocalDisk 원격 백엔드 P2P·PD 앞 계층부터 찾고, 원격에서 찾으면 CPU 에 올려 둔다 MP 모드는 이 계층을 엔진 밖 별도 서버(ZMQ)로 뺀다
図: リクエスト経路: クライアントが production-stack ルーターまたは llm-d Router を経由して vLLM エンジン Pod に到達し、エンジンは KV コネクタを介して LMCache 層(LocalCPU・LocalDisk・リモート・P2P)を呼び出す

図の読み方です。

3. コードマップ

まず、「どのリポジトリにコードが存在するか」を整理します。 llm-d/llm-d リポジトリにはサービスコードは存在しません。 v0.10.0 タグのファイル 1,315 個のうち、YAML が 834 個、Markdown が 249 個であり、Go ファイルは 0 個、Python は CI・ツール・サンプルスクリプトが 33 個のみです。デプロイガイド・Helm 値・設計文書を集めた場所であり、実際のコンポーネントは COMPONENTS.md の Roster テーブルが指し示す個別のリポジトリに存在します。EPP は llm-d-router です。以前の名前 llm-d-inference-scheduler のアドレスは現在 llm-d-router に転送されます(2026-10-17 HTTP 301 確認)。llm-d v0.10.0 の guides/env.sh が EPP イメージ v0.11.0 と Gateway API Inference Extension (GAIE) v1.5.0 CRD を固定しているため、本記事では llm-d-router v0.11.0 を別途取得して読み込みました。InferencePool などの API タイプは GAIE モジュールから取得します。

パス 内容 最初に読むファイル
production-stack src/vllm_router/ ルーター本体。リクエスト受信・サービスディスカバリー・ルーティング・統計 app.py の initialize_all
production-stack helm/ チャート。エンジン Deployment、ルーター、ServiceMonitor、ダッシュボード ConfigMap templates/deployment-vllm-multi.yaml
production-stack helm/dashboards/ Grafanaダッシュボード JSON 3種類(vLLM・モデル別・LMCache) vllm-dashboard.json
vLLM vllm/v1/metrics/ Prometheus指標の定義と記録 loggers.py の PrometheusStatLogger
vLLM vllm/distributed/kv_transfer/kv_connector/v1/ KVコネクタインターフェースと各種実装(LMCache・NIXL・Mooncakeなど) base.py
LMCache lmcache/integration/vllm/ vLLMコネクタの実装 vllm_v1_adapter.py
LMCache lmcache/v1/ キャッシュエンジン・トークンチャンク・ストレージバックエンド・MPサーバー cache_engine.py, storage_backend/__init__.py
llm-d guides/, docs/architecture/ デプロイレシピと設計文書。コードなし docs/architecture/core/router/README.md
llm-d-router pkg/epp/ EPP: ext-procサーバー、スケジューラー、フィルター・スコアラー・ピッカープラグイン scheduling/scheduler.go
llm-d-router pkg/kvevents/, pkg/kvcache/, pkg/sidecar/ KVイベント購読・ブロックインデックス、P/Dルーティングサイドカ kvevents/README.md

4. コアパスを辿る

4.1 production-stack: リクエストがエンジンに到達するまで

  1. ルーターは起動時に initialize_all において、サービスディスカバリー、エンジン統計コレクター、リクエスト統計、ルーティングロジックを順番にシングルトンとして生成します。ルーティングロジックが前者の3つが生成したテーブルを参照するため、この順序になっています。
  2. エンジンの一覧は K8sPodIPServiceDiscovery._watch_engines が作成します。ラベルスリーターでPodを watch し、準備完了状態のPodごとに _get_model_names を通じて /v1/models を問い合せ、「このIPはどのモデルを提供するのか」を記録します。ServiceではなくPod IPに直接接続する理由は、ルーターがPodを選択する必要がある一方、Serviceはその選択を kube-proxy に委譲してしまうためです。
  3. リクエストは route_chat_completion を経由して route_general_request に入ります。ここでボディ内の model により候補エンジンを絞り込み、ルーティングロジックの route_request を呼び出してURLを1つ取得した後、 process_request を通じてレスポンスをストリーミングします。失敗した場合はそのURLを除外して再選択するリトライループもこの関数内に含まれています。
  4. ルーティングロジックは RoutingLogic 列挙型の8種類です。基本軸は RoundRobinRouter, セッションキーをハッシュリングに登録する SessionRouter, プロンプト文字列を128文字のチャンクにハッシュ化してトライ木に格納する PrefixAwareRouter(HashTrie)、そしてLMCacheコントローラーに「このトークンを最も長く保持しているインスタンス」を問い合わせる KvawareRouter です。

2つのプレフィックスルーターの違いは、本プロジェクトの設計上の試行錯誤をよく表しています。 PrefixAwareRouter はルーターが 自身が送信した履歴のみに基づいて推測します。クラスのコメントに「プレフィックスキャッシュの退避はないと仮定する」と明記されている通り、エンジン側がブロックを削除してもルーター側はそれを把握できません。その代わり、外部依存がありません。 KvawareRouter は LMCache コントローラーに対して 実際に保存されている場所を問い合わせるため正確ですが、ルーターのコンテナイメージに LMCache パッケージを含める必要があります。デフォルトのルーターイメージは pyproject.toml のオプショナル依存 lmcache==0.3.11 を組み込んでおり、エンジン側とはバージョンが異なっていますが、両者のコントローラーメッセージの互換性が保たれているかは確認できていません(未確認)。

Helmチャートは、このルーターの引数をそのまま展開します。 deployment-router.yaml が routerSpec.routingLogic を --routing-logic に渡し、 deployment-vllm-multi.yaml は modelSpec ごとに vllm serve コマンドを組み立てます。LMCache を有効にすると、 --kv-transfer-config に LMCacheConnectorV1 を、サイズは LMCACHE_* 環境変数として渡します。エンジンの引数が文字列として組み立てられるため、vLLM がフラグを削除すると、チャートやチュートリアルは古い状態のまま放置され、実行時にエラーとなります。第5節でのコントリビューションはまさにこの点を対象としています。

4.2 vLLM ↔ LMCache: コネクタというインターフェース

vLLM v1 は外部 KV ストレージを KVConnectorBase_V1 として抽象化します。このクラスは、同一のオブジェクトが 2つの役割でインスタンス化されるという点が核心です。

なぜこれら2つに分割されているのでしょうか。スケジューラーはGPUテンソルを操作せずバッチ作成のみを行い、ワーカーはGPUテンソルを直接扱います。両者は異なるプロセスで実行されている可能性もあります。「どれだけロードできるか」はバッチを作成する前に把握する必要があり、実際のコピー処理は順伝播と並行して実行する必要があるため、判断と実行をメタデータの塊で結合した構造になっています。レイヤー単位の関数は、あるレイヤーを計算している間に次のレイヤーの KV を読み込んでコピー時間を隠蔽するためです。

vLLM ツリー内の LMCacheConnectorV1 は薄いラッパーです。

use_native = vllm_config.kv_transfer_config.get_from_extra_config(
    "use_native", False
)
if use_native:
    from vllm.distributed.kv_transfer.kv_connector.v1 import lmcache_integration
    _adapter = lmcache_integration.vllm_v1_adapter
    cls = _adapter.LMCacheConnectorV1Impl
else:
    from lmcache.integration.vllm.vllm_v1_adapter import (
        LMCacheConnectorV1Impl as LMCacheConnectorLatestImpl,
    )
    cls = LMCacheConnectorLatestImpl

self._lmcache_engine = cls(vllm_config, role, self)

出典: vllm v0.31.0 vllm/distributed/kv_transfer/kv_connector/v1/lmcache_connector.py(88–108行目、ログ行省略)、Apache-2.0

デフォルト値は インストールされた LMCache パッケージ側の実装です。実装を vLLM に組み込んでしまうと、LMCache が修正されるたびに vLLM のリリースを待つ必要があるため、インターフェースは vLLM が、実装は LMCache が持ちます。MP コネクターも lmcache_mp_connector.py で LMCache 側の実装を先に探し、ない場合のみツリー内のコピーを使用します。

LMCache 側の実際の実装は LMCacheConnectorV1Impl です。検索は get_num_new_matched_tokens から始まり、lookup クライアントを経由し、トークンは ChunkedTokenDatabase が _chunk_tokens に分割し、 _prefix_hash で前のチャンクのハッシュをつなぎながらキーを作成します。チャンクのデフォルト値は config.py の 256 トークンで、vLLM GPU ブロック(実測 16 トークン)の 16 倍です。リモートストレージのオブジェクト数とラウンドトリップを減らすためにより大きなチャンクが必要だからであり、その代償として 256 トークンより短い一致のプレフィックスは LMCache でヒットしません。

ストレージ階層は CreateStorageBackends が OrderedDict に投入する 順序がそのまま検索順序になります(PD、LocalCPU、P2P、NIXL、LocalDisk、GDS、Maru、リモートのうち有効なもの)。 StorageManager.batched_get は手前から探し、リモートで見つかったものは LocalCPU に書き戻します。リモート側は storage_backend/connector/ の 17 個のアダプター(Redis、Valkey、S3、ファイルシステム、Mooncake など)に分かれています。

MP モードは、この階層全体をエンジンの外部に取り出したものです。 MPCacheServer が複数のエンジンモジュールを組み立て、 MPServerConfig のデフォルト転送は ZMQ(ポート 5555)、チャンクは同じく 256、ハッシュは blake3 です。エンジンが再起動してもキャッシュが残り、複数のエンジンで共有できるようにするためです。

4.3 llm-d: EPP がポッドを選択する方法

llm-d Router はプロキシと EPP に分かれています。プロキシがリクエストを保留し、ext-proc ストリームで EPP に問い合わせると、EPP の StreamingServer.Process が受け取ってスケジューラを実行し、 generateHeaders が x-gateway-destination-endpoint ヘッダーに選択したポッドのアドレスを載せて返送します。ボディの転送、TLS、リトライを検証済みのプロキシに任せ、LLM 特化の判断のみを分離した構造であり、両方を 1 つの Python プロセスで行う production-stack ルーターとは正反対です。

スケジューラ Scheduler.Schedule は「プロファイルハンドラーが選択したプロファイルを実行し、結果に基づいてさらに選択する」ループです。1 つのプロファイルは SchedulerProfile.Run 内でフィルター → 重み付きスコアラー(runScorerPlugins) → ピッカーを経過します。プロファイル複数回実行できるようにした理由が P/D 分離です。 disagg ハンドラーの Pick はデコードプロファイルで D ポッドを先に選択し、必要に応じてプリフィルプロファイルで P ポッドを選択します。実際の P→D フローはデコードポッドの横にあるルーティングサイドカー(llm-d-router pkg/sidecar/proxy/)が担当し、P に max_tokens=1 で先に送信し、受け取った KV 転送情報を付与して D に渡します。P/D 分離自体の原理は NVIDIA Dynamo 編にあります。

スコアラーが読み取る数値は 2 つのルートから入ってきます。1 つ目はエンジン /metrics をスクレイピングする datalayer ソースで、 parseMetrics が Prometheus のテキストをパースし、エンジン種別ごとのラベルがどの名前をどの属性にマッピングするかを決定します。

var defaultEngineConfigs = []engineConfigParams{
	{
		Name:                "vllm",
		QueuedRequestsSpec:  "vllm:num_requests_waiting",
		RunningRequestsSpec: "vllm:num_requests_running",
		KVUsageSpec:         "vllm:kv_cache_usage_perc",
		LoRASpec:            "vllm:lora_requests_info",
		CacheInfoSpec:       "vllm:cache_config_info",
	},

出所: llm-d-router v0.11.0 pkg/epp/framework/plugins/datalayer/extractor/metrics/factories.go(91〜99行目)、Apache-2.0

同じテーブルに SGLang、TensorRT-LLM、Triton の名前が並んでおり、ポッドラベル llm-d.ai/engine-type でテーブルを選択します。エンジンが名前を変更しても、このテーブルの 1 箇所だけを修正すればよいように設計されています。この値を受け取って KVCacheUtilizationScorer.Score は 1 − KV 사용률 をスコアとして付与します。

もう 1 つは KV イベントです。vLLM はブロックの保存やエビクション時に BlockStored のようなイベントを ZmqEventPublisher で発行し、EPP の precise-prefix-cache プラグインは ensureSubscriber でポッドごとにサブスクライブを開きます。受け取ったメッセージは VLLMAdapter.ParseMessage がデコードしてブロックインデックスに追加、または削除します。production-stack PrefixAwareRouter の「送信履歴による推測」をエンジンが通知する事実ベースに置き換えたものであり、その代償としてエンジンで --kv-events-config を有効にし、ZMQ ポートを開く必要があります。

4.4 メトリクスパス: 定義の 1 行が 3 箇所のクエリになる

메트릭 경로: 정의 한 줄이 세 곳의 쿼리가 된다 vLLM 엔진 · vllm/v1/metrics/loggers.py Counter(name="vllm:prefix_cache_hits") GET /metrics 에서 _total · _created 가 붙어 나간다 ① PS 라우터 engine_stats.py 30초마다 직접 긁음 이름 정확 일치 ② Prometheus ServiceMonitor service-port 포트 PromQL 로 질의 ③ llm-d EPP datalayer 소스 factories.go 이름표 파드마다 폴링 라우터 /metrics 로 같은 접두사 재노출 Grafana 대시보드 JSON 3벌 KV 사용률 점수 = 1 − 사용률 이름이 바뀌면 오류 없이 0 이나 No data 가 된다 옛 이름 v0.31.0 의 이름 gpu_cache_usage_perc → kv_cache_usage_perc gpu_prefix_cache_hits/queries → prefix_cache_hits/queries time_per_output_token_seconds → inter_token_latency_seconds 실측 /metrics(v0.31.0)에 옛 이름은 하나도 없었다
図: メトリクスパス: vLLM loggers.py の定義が /metrics に出力され、production-stack ルーターパーサー、Prometheus・Grafana ダッシュボード、llm-d EPP ラベルの 3 箇所でそのまま読み取られ、名前が変更されるとエラーなく値が空になる

図の PS は production-stack です。出発点は PrometheusStatLogger コンストラクターで、 vllm: 指標が 1 行ずつ定義されています。KV 使用率は vllm:kv_cache_usage_perc、プレフィックスキャッシュは vllm:prefix_cache_queries·vllm:prefix_cache_hits、トークン間レイテンシは vllm:inter_token_latency_seconds です。ソースのカウンター名には _total がありませんが、 prometheus_client がエクスポートする際に _total と _created を付与するため、クエリを使用する側は vllm:prefix_cache_hits_total として読み取る必要があります。この違いが名前の混乱の原因の 1 つです。このレジストリは attach_router が /metrics パスにアタッチします。

vLLM はプレフィックスキャッシュを「ヒット率ゲージ」ではなく「検索・ヒットの 2 つのカウンター」として出力します。ユーザーが rate(hits[5m]) / rate(queries[5m]) のように任意の期間で比率を算出できるようにするためです(docs/design/metrics.md)。指標を削除する際は、X.Y の非推奨予告、X.Y+1 での非表示(--show-hidden-metrics-for-version で復活)、X.Y+2 での削除ルール(docs/usage/metrics.md)に従います。エンジンのルールは明確ですが、 この名前を読み取る側が別のリポジトリにあることが問題です。

最初の消費者である production-stack ルーターのスクレイパー EngineStatsScraper は、デフォルトで 30 秒ごとにエンジンごとに /metrics を GET し、 from_vllm_scrape で名前を正確に一致させて取得します。

for family in text_string_to_metric_families(vllm_scrape):
    for sample in family.samples:
        if sample.name == "vllm:num_requests_running":
            num_running_reqs = sample.value
        elif sample.name == "vllm:num_requests_waiting":
            num_queuing_reqs = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_hit_rate":
            gpu_prefix_cache_hit_rate = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_hits_total":
            gpu_prefix_cache_hits_total = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_queries_total":
            gpu_prefix_cache_queries_total = sample.value
        elif sample.name == "vllm:gpu_cache_usage_perc":
            gpu_cache_usage_perc = sample.value

出所: production-stack vllm-stack-0.1.13 src/vllm_router/stats/engine_stats.py(63〜76行目)、Apache-2.0

一致する名前がない場合、例外もログも出ずに 0 が残ります。ルーターはこの値を metrics_router.py の /metrics として のような vllm: プレフィックスを付けたゲージ(server ラベル)で再度出力します。そのため、同じ名前がエンジンとルーターの 2 つのソースからラベル違いで混在し、デフォルトダッシュボードのプレフィックスキャッシュパネルは endpoint="service-port" でフィルタリングします。 service-port は チャートがエンジン Service ポートに付与する名前であり、ルーター Service ポート名は router-sport であるため、エンジンからの時系列のみを残します。

2番目の消費者は Prometheus と Grafana です。チャートの ServiceMonitor がエンジンとルーターをスクレイピングし、ダッシュボード JSON の PromQL が名前を文字列として保持しています。3番目の消費者は 4.3 節の EPP ラベルです。3つすべてにおいて、名前が間違っているとエラーにならずに静かに 0 や「No data」になるだけです。

5. ユーザーコントリビューションとの繋がり

著者の 4 件の PR はすべて「vLLM が削除した名前を他のリポジトリがまだ呼び出している」という同じパターンです(状態は 2026-10-17 時点)。この記事では v0.31.0 タグを再びチェックアウトし、「現在は存在せず、代わりにこれが存在する」ことを確認しました。

PR 修正箇所 対応する vLLM v0.31.0 の行 状態
vllm-project/production-stack#1147 helm/dashboards/vllm-dashboard.json:826 「GPU KV Cache Hit Rate」パネルの vllm:gpu_prefix_cache_* loggers.py:600 vllm:prefix_cache_queries, loggers.py:611 vllm:prefix_cache_hits レビュー待ち(自動レビューは「修正点なし」)
vllm-project/production-stack#1148 tutorials/23-whisper-api-transcription.md:24 の --task transcription --task は存在せず、その位置を arg_utils.py:930 の --runner·--convert が引き継ぐ レビュー待ち
LMCache/LMCache#5814 ドキュメント・ベンチマーク・サンプル 12 ファイル(v0.5.6 タグの 20 箇所)の --disable-log-requests arg_utils.py:3141 には --enable-log-requests のみが存在し、薬の BooleanOptionalAction であるため、対義語は --no-enable-log-requests レビュー待ち(base dev)
llm-d/llm-d#2835 ドキュメントの vllm:time_per_output_token_seconds, vllm:gpu_prefix_cache_hit_rate loggers.py:832 vllm:inter_token_latency_seconds、ヒット率は 600・611 行目の 2 つのカウンターの比率 レビュー待ち

#1147 はダッシュボードのみを修正しており、ルーターパーサー(engine_stats.py:69–76)の同様の問題は PR 本文に記載した通り #1140・#1146 で個別に扱われます。llm-d#2835 は main 基準であるため、v0.10.0 タグでは同じ文が docs/architecture/advanced/autoscaling/wva.md:144·210줄 と DigitalOcean ダッシュボード 1544줄 付近にあり、3番目に修正したガイド(turn-priority-fairness)は v0.10.0 にはまだ存在しません。パスが異なる理由は確認していません(未確認)。

6. 実測

6.1 何をどのように測定したか

2026-10-16 に小規模クラスターの GPU ノード 1 つ(8GB 級ノートPC向け GPU)で使い捨てポッドを起動して測定しました。測定スクリプト(pod.sh)はノードを固定し、既存のイメージのみを使用し(imagePullPolicy: Never)、Hugging Face へのアクセスをブロックし、メモリ制限 3.5Gi・実行時間 90 分の制限を設けます。モデルは設定ファイルのみを作成した ランダム重み Qwen2 構造(2 層、hidden 256、 --load-format dummy)です。イメージは vllm/vllm-openai v0.31.0(PyTorch 2.13、CUDA 13.0)であり、最初の 160 トークンが同じリクエスト 5 件を送信した後に /metrics を取得しました。ルーター、LMCache、EPP は起動していないため、6.3 節は各タグのソースから抽出した名前をこの出力と照合した 静的突合です。

6.2 /metrics 出力の規模

項目 値
総行数 587
# TYPE ファミリー 88 (vllm 68, http 10, process 6, python 4)
vllm: ファミリー中の _created サブゲージ 31
vllm: 実際の指標 37(カウンター 15、ヒストグラム 16、ゲージ 6)
vllm:prefix_cache_queries_total / _hits_total 805 / 640 → ヒット率 0.795
vllm:inter_token_latency_seconds_count 75
vllm:cache_config_info block_size="16", num_gpu_blocks="144272", gpu_memory_utilization="0.3"
旧名(gpu_prefix_cache*, gpu_cache_usage_perc, time_per_output_token_seconds) 0 個

数値は一致しています。リクエスト 5 件 × 161 トークン = 805 検索であり、最初のリクエストはすべてミスし、残りの 4 件が 160 トークン(10 ブロック)ずつヒットして 640 になります。16 トークンずつ作成されたリクエスト 5 件のトークン間の間隔は 15 × 5 = 75 です。ブロック数 144,272 × 16 = 2,308,352 も同じ行の kv_cache_size_tokens と一致します(モデルが非常に小さいためブロック数が多くなっています)。

6.3 消費者が探している名前が常に出力に含まれているか

消費者(タグソース) 検索する vllm: 名 出力に含まれているか 含まれていない理由
production-stack ルーターパーサー engine_stats.py 6 2 4: gpu_prefix_cache_hit_rate·_hits_total·_queries_total·gpu_cache_usage_perc、v0.31.0 ソースに定義なし
production-stack vllm-dashboard.json 13 9 4: current_qps·healthy_pods_total はルーター自身の指標(正常)、クエリ内の gpu_prefix_cache_* 2 つは削除された名前(#1147)
production-stack vllm-model-metrics-dashboard.json 21 21 0
llm-d-router EPP デフォルト vLLM ラベル 5 4 1: lora_requests_info、LoRA を有効にした場合のみ出現する条件付き指標(loggers.py:993)
llm-d v0.10.0 ドキュメント・ガイド全体 71 40 31: 27 個はオフロードや NIXL コネクターを有効にした場合のみ出現する条件付き指標、4 個は v0.31.0 ソースに定義なし

llm-d ドキュメントの「定義なし」4 個は time_per_output_token_seconds 系 3 個(#2835 が修正した項目)と vllm:kv_offload_tiering_block_hits_total 1 個です。最後の項目は 8 節のコントリビューション候補として残しました。

6.4 削除されたフラグ

コマンド 結果
vllm serve <작은 모델> --disable-log-requests unrecognized arguments、終了コード 2
vllm serve <작은 모델> --swap-space 0 unrecognized arguments、終了コード 2
vllm serve 全体のヘルプから抽出したフラグ一覧(約 400 個) --task·--disable-log-requests·--swap-space なし、クエリ内の --enable-log-requests·--no-enable-log-requests·--runner·--kv-transfer-config·--kv-events-config·--show-hidden-metrics-for-version あり
--task なしでランダム重み Whisper 構造で vllm serve ログ Supported tasks: ['transcription'], /v1/audio/transcriptions HTTP 200

同日に確認したチャートのデフォルトエンジンイメージ(lmcache/vllm-openai:latest)は vLLM 0.29.0・LMCache 0.5.5 であり、その中の loggers.py にも gpu_prefix_cache は存在しませんでした。クエリ内の チャートのデフォルト値のままであっても ルーターパーサーとプレフィックスキャッシュパネルには 0 または No data が表示されます。実際にチャートをインストールして Grafana を確認したわけではありません(未実測)。

7. どこから読むべきか

  1. vllm/v1/metrics/loggers.py の PrometheusStatLogger コンストラクターを上から下にざっと確認します。エンジンが何をカウントしているかが、他のすべての入力になります。
  2. vllm_router/app.py の initialize_all から始めて、クエリ内の request.py の route_general_request で 1 つのリクエストを最後まで追いかけます。
  3. routing_logic.py で RoutingInterface と実装をいくつか比較します。テスト(クエリ内のsrc/tests/test_*_router.py)を合わせて確認すると、期待される動作がすぐに把握できます。
  4. kv_connector/v1/base.py 一番上のコメント(スケジューラー側・ワーカー側の関数一覧)を読んだ後、LMCache の vllm_v1_adapter.py の同名関数にジャンプします。
  5. llm-d は docs/architecture/core/router/README.md で用語を把握し、コードは llm-d-router の scheduler.go とクエリ内の factories.go を確認します。

8. 初めての貢献におすすめの箇所

ソースを読んでいて見つけたものです。すでに開いているPRやissueがないかは確認していないため、投稿する前にまず検索する必要があります。

  1. production-stackのクラウド別ダッシュボードのコピー。 ヘルムチャートのダッシュボードは #989 と #1147 で修正を進めているところですが、 tutorials/terraform/{coreweave,eks,nebius}/config/ の vllm-dashboard*.json の4つには vllm:gpu_cache_usage_perc と vllm:gpu_prefix_cache_* がどちらも10か所残っています(例: eks/config/vllm-dashboard.json:732).
  2. production-stackベンチマークチュートリアルの --swap-space 0. 07-benchmark-multi-round-qa-single-gpu.md:51 など、07・08番のチュートリアル4つが extraArgs: ["--swap-space", 0] を与えます。v0.31.0はこのフラグで終了コード 2 で停止します(6.4節)。
  3. llm-d P2Pガイドの指標名。 guides/p2p-kv-cache-sharing/benchmarking/README.md:129 は vllm:kv_offload_tiering_block_hits_total を使っていますが、v0.31.0には vllm:kv_offload_tiering_chunk_hits のみが定義されています。ガイドが依存しているvLLMのバージョン(vllm#48798以降)で名前が変更されたのかどうかをまず確認する必要があります。
  4. KvawareRouter のチャットリクエスト。 route_request は request_json.get("prompt", "") のみをトークン化し、 TODO: Handle chat completions を残しました。 /v1/chat/completions リクエストは空のプロンプトとして参照され、セッション・QPSルーティングに落ちると予想されます。同じファイル PrefixAwareRouter の messages の処理を参考にすることができます。
  5. デフォルトダッシュボードの混ざった時系列(未確認)。 「Number of Running Requests」パネルはラベルなしで vllm:num_requests_running を描画します。ルーターも同じ名前を出力するため(4.4節)、2つの時系列が一緒に描画される可能性があります。実際の画面をまず確認する必要があります。

プロジェクト案内: production-stackは大きな変更なら proposals/ に提案書から、コミットにはDCO Signed-off-by を求めます。LMCacheはデフォルトブランチが devで、DCOが必須であり、入門用issueのまとめ(LMCache#627)があり、 AGENTS.md とコーディング基準に「AIが生成したコードのシグナル」というレビュー項目を設けています。llm-dはAIを使った貢献も同様の基準で受け付けるが、提出者が内容を直接説明できなければならないと記載しています。筆者のPRはすべて本文の最後にAI支援の事実を明らかにしています。

9. 制限事項

一言。 厨房は同じなのにメニューの名前だけ変わっても、ホールマネージャーは「そんな料理はありません」の代わりに空のお皿を出してしまいます。だからこそ、名前を1つ修正するPRが思った以上に大きな穴を防いでくれるのです!

参考

閲覧日はすべて2026-10-17です。

引用したコード: vLLM・production-stack・llm-d-routerはいずれもApache License 2.0です。

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます