KV Cache 対応ロードバランシングは、生成 AI 推論シナリオ向けに設計されています。リクエストを最適な計算ノードに動的に割り当てることで、大規模言語モデル (LLM) サービスの効率を大幅に向上させます。このトピックでは、Gateway with Inference Extension コンポーネントを使用して KV Cache 対応ロードバランシングポリシーを実装する方法について説明します。
背景情報
vLLM
vLLM は、LLM 推論サービスを効率的かつユーザーフレンドリに構築するために設計されたフレームワークです。Qwen を含むさまざまな大規模言語モデルをサポートし、PagedAttention、動的バッチ推論 (継続的バッチ処理)、モデル量子化などの技術を通じて LLM 推論の効率を最適化します。
KV キャッシュ
vLLM での自動プレフィックスキャッシュ
vLLM は自動プレフィックスキャッシュをサポートしています。自動プレフィックスキャッシュ (APC) は、vLLM がすでに処理したリクエストの KV キャッシュをキャッシュします。新しいリクエストが以前のリクエストと同じプレフィックスを持つ場合、既存の KV キャッシュを再利用できます。これにより、新しいリクエストは共有プレフィックスの KV キャッシュ計算をスキップでき、LLM 推論リクエストの処理が高速化されます。
KV Cache 対応とプレフィックス対応のロードバランシングポリシーの関係
KV Cache 対応ロードバランシングポリシーは次のように機能します:
各 vLLM ワークロードは、キャッシュされた KV キャッシュブロック情報をイベントメッセージを通じて Gateway with Inference Extension に報告します。各 vLLM ワークロードの KV キャッシュブロック情報を使用して、ゲートウェイはリクエストの内容に基づいて、キャッシュヒット率が最も高い vLLM ワークロードに新しいリクエストをスケジュールできます。このプロセスにより、プレフィックスキャッシュのヒット率が最大化され、応答時間が短縮されます。このポリシーは、主に多くのリクエストがプレフィックスを共有するシナリオに適しています。このポリシーがビジネスシナリオに適しているかどうかを判断する必要があります。
プレフィックス対応ロードバランシングと同様に、KV Cache 対応ロードバランシングも推論サービスフレームワークのプレフィックスキャッシュ機構を使用して、プレフィックスキャッシュのヒット率を最大化します。
KV Cache 対応ロードバランシングは、KV キャッシュブロックの配布情報を直接受信できます。これにより、ゲートウェイはリクエストのキャッシュステータスをより正確に判断し、プレフィックスキャッシュのヒット率を最大化できます。ただし、このポリシーでは、推論サービスが vLLM v0.10.0 以降を使用し、起動時に KV キャッシュイベントレポート情報を構成する必要があります。
プレフィックス対応ロードバランシングは推論エンジンから分離されていますが、KV キャッシュの配布を正確に検出することはできません。
KV Cache 対応ロードバランシングを使用するには、推論サービスを vLLM v0.10.0 以降のフレームワークで構築する必要があります。また、vLLM の起動パラメーターで KV キャッシュイベントパラメーターを指定する必要もあります。設定の詳細については、このトピックの例をご参照ください。
前提条件
「ACK マネージドクラスターの作成」で説明されているように、GPU ノードプールを持つ ACK マネージドクラスターを作成済みであること。また、「ACK で ACS GPU 計算能力を使用する例」で説明されているように、ACK マネージドクラスターに ACK 仮想ノードコンポーネントをインストールして ACS GPU 計算能力を使用することもできます。
このトピックでは、Qwen-32B モデルサービスを例として使用します。このサービスには 64 GB を超える GPU メモリが必要です。ecs.gn8is-2x.8xlarge インスタンスタイプを推奨します。ACS 仮想ノードの場合、GU8TFカードタイプを推奨します。
バージョン 1.4.0-aliyun.3 以降の Gateway with Inference Extension をインストールし、[Gateway API Inference Extension を有効化] を選択済みであること。詳細については、「Gateway with Inference Extension コンポーネントをインストールする」をご参照ください。
モデルサービスのデプロイ
ステップ 1: Qwen3-32B モデルファイルの準備
ModelScope から Qwen-32B モデルをダウンロードします。
git-lfs プラグインがインストールされていることを確認してください。インストールされていない場合は、
yum install git-lfsまたはapt-get install git-lfsを実行してインストールできます。インストール方法の詳細については、「Git Large File Storage のインストール」をご参照ください。git lfs install GIT_LFS_SKIP_SMUDGE=1 git clone https://www.modelscope.cn/Qwen/Qwen3-32B.git cd Qwen3-32B/ git lfs pullOSS にフォルダを作成し、モデルファイルをアップロードします。
ossutil のインストールと使用方法については、「ossutil のインストール」をご参照ください。
ossutil mkdir oss://<your-bucket-name>/Qwen3-32B ossutil cp -r ./Qwen3-32B oss://<your-bucket-name>/Qwen3-32Bターゲットクラスター用に
llm-modelという名前の永続ボリューム (PV) と永続ボリューム要求 (PVC) を作成します。詳細については、「ossfs 1.0 を使用して静的にプロビジョニングされたボリュームを作成する」をご参照ください。llm-model.yaml ファイルを作成します。このファイルには、Secret、静的にプロビジョニングされた PV、および静的にプロビジョニングされた PVC の構成が含まれています。
apiVersion: v1 kind: Secret metadata: name: oss-secret stringData: akId: <your-oss-ak> # OSS へのアクセスに使用される AccessKey ID akSecret: <your-oss-sk> # OSS へのアクセスに使用される AccessKey Secret --- apiVersion: v1 kind: PersistentVolume metadata: name: llm-model labels: alicloud-pvname: llm-model spec: capacity: storage: 30 Gi accessModes: - ReadOnlyMany persistentVolumeReclaimPolicy: Retain csi: driver: ossplugin.csi.alibabacloud.com volumeHandle: llm-model nodePublishSecretRef: name: oss-secret namespace: default volumeAttributes: bucket: <your-bucket-name> # バケットの名前。 url: <your-bucket-endpoint> # oss-cn-hangzhou-internal.aliyuncs.com などのエンドポイント情報。 otherOpts: "-o umask=022 -o max_stat_cache_size=0 -o allow_other" path: <your-model-path> # この例では、パスは /Qwen3-32B/ です。 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: llm-model spec: accessModes: - ReadOnlyMany resources: requests: storage: 30 Gi selector: matchLabels: alicloud-pvname: llm-modelSecret、静的にプロビジョニングされた PV、および静的にプロビジョニングされた PVC を作成します。
kubectl create -f llm-model.yaml
ステップ 2: vLLM 推論サービスのデプロイ
`vllm.yaml` ファイルを作成します。
次の表に、一部の起動パラメーターと環境変数を示します。
起動パラメーター/環境変数
説明
--kv-events-config
KV キャッシュイベントを公開するための構成。有効な JSON 文字列または個別に渡される JSON キーである必要があります。
値の例:
{"enable_kv_cache_events":true,"publisher":"zmq","endpoint":"tcp://epp-default-qwen-inference-pool.envoy-gateway-system.svc.cluster.local:5557","topic":"kv@${POD_IP}@Qwen3-32B"}詳細:
endpoint: 推論拡張機能の ZMQ サーバーエンドポイントに設定する必要があります。命名規則は
tcp://epp-<InferencePool namespace>-<InferencePool name>.envoy-gateway-system.<cluster local domain>:5557です。この例では、InferencePoolという名前のqwen-inference-poolがdefault名前空間に作成されます。このため、エンドポイントはtcp://epp-default-qwen-inference-pool.envoy-gateway-system.svc.cluster.local:5557に設定されます。topic: 命名規則は
kv@${POD_IP}@<served model name>です。この例では、vLLM 起動パラメーター--served-model-name Qwen3-32Bが指定されているため、トピックはkv@${POD_IP}@Qwen3-32Bに設定されます。
--prefix-caching-hash-algo
KV キャッシュプレフィックスキャッシュブロックのハッシュ値を計算するために使用されるアルゴリズム。
sha256_cbor_64bitに設定する必要があります。--block-size
各 KV キャッシュプレフィックスキャッシュブロックに格納されるトークンの数。この例では、
64に設定されています。PYTHONHASHSEED
Python がハッシュアルゴリズムを実行するときに使用されるシード。ゼロ以外の値に設定する必要があります。この例では、
42に設定されています。vLLM 推論サービスをデプロイします。
kubectl create -f vllm.yaml
推論ルーティングのデプロイ
ステップ 1: 推論ルーティングポリシーのデプロイ
`inference-policy.yaml` ファイルを作成します。
# InferencePool は、ワークロードに対して推論ルーティングが有効になっていることを宣言します。 apiVersion: inference.networking.x-k8s.io/v1alpha2 kind: InferencePool metadata: name: qwen-inference-pool spec: targetPortNumber: 8000 selector: app: qwen3 --- # InferenceTrafficPolicy は、InferencePool に適用されるトラフィックポリシーを指定します。 apiVersion: inferenceextension.alibabacloud.com/v1alpha1 kind: InferenceTrafficPolicy metadata: name: inference-policy spec: poolRef: name: qwen-inference-pool profile: single: # バックエンド推論サービスがスタンドアロンの vLLM デプロイメントであることを指定します。 trafficPolicy: # 推論サービスのロードバランシングポリシーを指定します。 prefixCache: mode: tracking # KV Cache 対応ロードバランシングを有効にします。 trackingConfig: indexerConfig: tokenProcessorConfig: blockSize: 64 # vLLM の block-size 起動パラメーターと一致している必要があります。 hashSeed: 42 # vLLM の PYTHONHASHSEED 環境変数と一致している必要があります。 model: Qwen/Qwen3-32B # ModelScope での推論サービスモデルの公式名を指定します。推論ルーティングポリシーをデプロイします。
kubectl apply -f inference-policy.yaml
ステップ 2: ゲートウェイとゲートウェイルーティングルールのデプロイ
`inference-gateway.yaml` ファイルを作成します。このファイルには、ゲートウェイ、ゲートウェイルーティングルール、およびバックエンドタイムアウトルールが含まれています。
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: inference-gateway spec: gatewayClassName: ack-gateway listeners: - name: http-llm protocol: HTTP port: 8080 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: inference-route spec: parentRefs: - name: inference-gateway rules: - matches: - path: type: PathPrefix value: /v1 backendRefs: - name: qwen-inference-pool kind: InferencePool group: inference.networking.x-k8s.io --- apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy metadata: name: backend-timeout spec: timeout: http: requestTimeout: 24h targetRef: group: gateway.networking.k8s.io kind: Gateway name: inference-gatewayゲートウェイとルーティングルールをデプロイします。
kubectl apply -f inference-gateway.yaml
ステップ 3: ルーティングルールの検証
`round1.txt` と `round2.txt` を作成します。両方のファイルには、同じテキストを含む
contentセクションが含まれています。`round1.txt` の内容、次に `round2.txt` の内容を LLM リクエストの本文として送信します。その後、推論拡張機能のログをチェックして、KV Cache 対応ロードバランシング機能がトリガーされたことを確認します。round1.txt:
echo '{"max_tokens":24,"messages":[{"content":"こんにちは、こちらがシステムプロンプトです: hi hi hi hi hi hi hi hi hi hi.ユーザー 3 について、こちらがその他のコンテキストです: hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi-あなたの知能をテストしたいと思います。この目的のために、zork というゲームをプレイしてもらいます。コマンドを入力することでゲームと対話できます。これらのコマンドをゲームに転送し、応答を入力します。準備はいいですか?","role":"user"}],"model":"Qwen3-32B","stream":true,"stream_options":{"include_usage":true},"temperature":0}' > round1.txtround2.txt:
echo '{"max_tokens":3,"messages":[{"content":"Hi, here'\''s some system prompt: hi hi hi hi hi hi hi hi hi hi.For user 3, here are some other context: hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi.I would like to test your intelligence. for this purpose I would like you to play zork. you can interact with the game by typing in commands. I will forward these commands to the game and type in any response. are you ready?","role":"user"},{"content":"Hi there! It looks like you're setting up a fun test. I'm ready to play Zork! You can","role":"assistant"},{"content":"% zork\nWelcome to Dungeon. This version created 11-MAR-91.\nYou are in an open field west of a big white house with a boarded\nfront door.\nThere is a small mailbox here.\n>","role":"user"},{"content":"Great!","role":"assistant"},{"content":"Opening the mailbox reveals:\n A leaflet.\n>","role":"user"}],"model":"Qwen3-32B","stream":true,"stream_options":{"include_usage":true},"temperature":0}' > round2.txtゲートウェイのパブリック IP アドレスを取得します。
export GATEWAY_IP=$(kubectl get gateway/inference-gateway -o jsonpath='{.status.addresses[0].value}')2 つのセッションリクエストを送信して、複数ラウンドの会話シナリオをシミュレートします。
curl -X POST $GATEWAY_IP:8080/v1/chat/completions -H 'Content-Type: application/json' -d @./round1.txt curl -X POST $GATEWAY_IP:8080/v1/chat/completions -H 'Content-Type: application/json' -d @./round2.txtログをチェックして、KV Cache 対応ロードバランシングが有効であることを確認します。
kubectl logs deploy/epp-default-qwen-inference-pool -n envoy-gateway-system|grep "handled"期待される出力:
2025-08-19T10:16:12Z LEVEL(-2) requestcontrol/director.go:278 Request handled {"x-request-id": "00d5c24e-b3c8-461d-9848-7bb233243eb9", "model": "Qwen3-32B", "resolvedTargetModel": "Qwen3-32B", "criticality": "Critical", "model": "Qwen3-32B", "targetModel": "Qwen3-32B", "endpoint": "{NamespacedName:default/qwen3-779c54544f-9c4vz Address:10.0.0.5 Labels:map[app:qwen3 pod-template-hash:779c54544f]}"} 2025-08-19T10:16:19Z LEVEL(-2) requestcontrol/director.go:278 Request handled {"x-request-id": "401925f5-fe65-46e3-8494-5afd83921ba5", "model": "Qwen3-32B", "resolvedTargetModel": "Qwen3-32B", "criticality": "Critical", "model": "Qwen3-32B", "targetModel": "Qwen3-32B", "endpoint": "{NamespacedName:default/qwen3-779c54544f-9c4vz Address:10.0.0.5 Labels:map[app:qwen3 pod-template-hash:779c54544f]}"}出力は、同じプレフィックスを持つ 2 つのリクエストが同じワークロードに転送されたことを示しています。これは、KV Cache 対応ロードバランシングポリシーが有効であることを示しています。