すべてのプロダクト
Search
ドキュメントセンター

Container Service for Kubernetes:スマート推論ルーティングを使用した KV Cache 対応ロードバランシングの実装

最終更新日:Nov 09, 2025

KV Cache 対応ロードバランシングは、生成 AI 推論シナリオ向けに設計されています。リクエストを最適な計算ノードに動的に割り当てることで、大規模言語モデル (LLM) サービスの効率を大幅に向上させます。このトピックでは、Gateway with Inference Extension コンポーネントを使用して KV Cache 対応ロードバランシングポリシーを実装する方法について説明します。

背景情報

vLLM

vLLM は、LLM 推論サービスを効率的かつユーザーフレンドリに構築するために設計されたフレームワークです。Qwen を含むさまざまな大規模言語モデルをサポートし、PagedAttention、動的バッチ推論 (継続的バッチ処理)、モデル量子化などの技術を通じて LLM 推論の効率を最適化します。

KV キャッシュ

推論プロセス中に、モデルによって生成されたキーと値をキャッシュして、過去のリクエストのコンテキスト情報に迅速にアクセスします。これにより、モデルによるテキスト生成の効率が向上します。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 キャッシュイベントパラメーターを指定する必要もあります。設定の詳細については、このトピックの例をご参照ください。

前提条件

モデルサービスのデプロイ

ステップ 1: Qwen3-32B モデルファイルの準備

  1. 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 pull
  2. OSS にフォルダを作成し、モデルファイルをアップロードします。

    ossutil のインストールと使用方法については、「ossutil のインストール」をご参照ください。
    ossutil mkdir oss://<your-bucket-name>/Qwen3-32B
    ossutil cp -r ./Qwen3-32B oss://<your-bucket-name>/Qwen3-32B
  3. ターゲットクラスター用に llm-model という名前の永続ボリューム (PV) と永続ボリューム要求 (PVC) を作成します。詳細については、「ossfs 1.0 を使用して静的にプロビジョニングされたボリュームを作成する」をご参照ください。

    1. 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-model
    2. Secret、静的にプロビジョニングされた PV、および静的にプロビジョニングされた PVC を作成します。

      kubectl create -f llm-model.yaml

ステップ 2: vLLM 推論サービスのデプロイ

  1. `vllm.yaml` ファイルを作成します。

    クリックして YAML コンテンツを表示

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: qwen3
      name: qwen3
    spec:
      progressDeadlineSeconds: 600
      replicas: 3
      revisionHistoryLimit: 10
      selector:
        matchLabels:
          app: qwen3
      strategy:
        rollingUpdate:
          maxSurge: 25%
          maxUnavailable: 25%
        type: RollingUpdate
      template:
        metadata:
          annotations:
            prometheus.io/path: /metrics
            prometheus.io/port: '8000'
            prometheus.io/scrape: 'true'
          creationTimestamp: null
          labels:
            app: qwen3
        spec:
          containers:
            - command:
                - sh
                - '-c'
                - >-
                  vllm serve /models/Qwen3-32B --served-model-name Qwen3-32B
                  --trust-remote-code --port=8000 --max-model-len 8192
                  --gpu-memory-utilization 0.95 --enforce-eager --kv-events-config
                  "{\"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\"}"
                  --prefix-caching-hash-algo sha256_cbor_64bit --block-size 64
              env:
                - name: POD_IP
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: status.podIP
                - name: PYTHONHASHSEED
                  value: '42'
              image: 'registry-cn-hangzhou.ack.aliyuncs.com/dev/vllm:0.10.0'
              imagePullPolicy: IfNotPresent
              name: vllm
              ports:
                - containerPort: 8000
                  name: restful
                  protocol: TCP
              readinessProbe:
                failureThreshold: 3
                initialDelaySeconds: 30
                periodSeconds: 30
                successThreshold: 1
                tcpSocket:
                  port: 8000
                timeoutSeconds: 1
              resources:
                limits:
                  nvidia.com/gpu: '1'
                requests:
                  nvidia.com/gpu: '1'
              terminationMessagePath: /dev/termination-log
              terminationMessagePolicy: File
              volumeMounts:
                - mountPath: /models/Qwen3-32B
                  name: model
                - mountPath: /dev/shm
                  name: dshm
          dnsPolicy: ClusterFirst
          restartPolicy: Always
          schedulerName: default-scheduler
          securityContext: {}
          terminationGracePeriodSeconds: 30
          volumes:
            - name: model
              persistentVolumeClaim:
                claimName: llm-model
            - emptyDir:
                medium: Memory
                sizeLimit: 30Gi
              name: dshm
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: qwen3
      name: qwen3
    spec:
      ports:
        - name: http-serving
          port: 8000
          protocol: TCP
          targetPort: 8000
      selector:
        app: qwen3
      type: ClusterIP

    次の表に、一部の起動パラメーターと環境変数を示します。

    起動パラメーター/環境変数

    説明

    --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-pooldefault 名前空間に作成されます。このため、エンドポイントは 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 に設定されています。

  2. vLLM 推論サービスをデプロイします。

    kubectl create -f vllm.yaml

推論ルーティングのデプロイ

ステップ 1: 推論ルーティングポリシーのデプロイ

  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 での推論サービスモデルの公式名を指定します。
  2. 推論ルーティングポリシーをデプロイします。

    kubectl apply -f inference-policy.yaml

ステップ 2: ゲートウェイとゲートウェイルーティングルールのデプロイ

  1. `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
  2. ゲートウェイとルーティングルールをデプロイします。

    kubectl apply -f inference-gateway.yaml

ステップ 3: ルーティングルールの検証

  1. `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.txt

    round2.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
  2. ゲートウェイのパブリック IP アドレスを取得します。

    export GATEWAY_IP=$(kubectl get gateway/inference-gateway -o jsonpath='{.status.addresses[0].value}')
  3. 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
  4. ログをチェックして、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 対応ロードバランシングポリシーが有効であることを示しています。