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

Alibaba Cloud Service Mesh:キュー、KV キャッシュ、LoRA を活用したスマートルーティング

最終更新日:Mar 12, 2026

従来の負荷分散はリクエストをバックエンド間で均等に分配しますが、大規模言語モデル(LLM)推論ワークロードは本質的に不均一です。短いプロンプトはミリ秒単位で完了する一方、長い出力は GPU を数秒間占有します。Service Mesh (ASM) は、各 vLLM バックエンドのリアルタイム状態(リクエストキューの深さおよび KV キャッシュ使用率)に基づいてリクエストをルーティングすることでこの課題を解決します。その結果、最初のトークンまでの時間(TTFT)が短縮され、スループットが向上し、推論クラスター全体の GPU 使用率が均等化されます。

このトピックでは、vLLM ベースの Llama 2 推論サービスのデプロイ、ASM を使った LLM 対応型ルーティングの構成、および推論トラフィックを監視するための可観測性ダッシュボードの設定手順を説明します。

重要

現在、vLLM ベースの LLM 推論サービスのみがサポートされています。

背景情報

大規模言語モデル(LLM)

大規模言語モデル(LLM)は、GPT、Qwen、Llama などに代表される、数十億パラメーターを持つニューラルネットワークベースの言語モデルです。これらのモデルは、Web テキスト、専門文献、コードなど多様かつ膨大なデータセットでトレーニングされており、主に補完や対話などのテキスト生成タスクに使用されます。

LLM を活用してアプリケーションを構築するには、以下の方法があります。

  • OpenAI、Alibaba Cloud Model Studio、Moonshot などのプラットフォームが提供する外部 LLM API サービスを利用する。

  • vLLM などのオープンソースまたは独自のモデル・フレームワークを使用して、独自の LLM 推論サービスを構築し、Kubernetes クラスターにデプロイする。このアプローチは、推論サービスに対する制御や LLM 推論機能の高度なカスタマイズが必要なシナリオに適しています。

vLLM

vLLM は、効率的かつユーザーフレンドリな LLM 推論サービスの構築を目的としたフレームワークです。Qwen を含むさまざまな大規模言語モデルをサポートし、PagedAttention、動的バッチ推論(Continuous Batching)、モデル量子化などの技術により推論効率を最適化します。

LLM 対応型負荷分散の仕組み

LLM 推論における従来型負荷分散の限界

ラウンドロビンや最小接続数などの古典的なアルゴリズムは、各リクエストが同程度の負荷をもたらすことを前提としています。しかし、LLM 推論はこの前提を崩します。

  • 処理時間のばらつき:各リクエストは、プロンプトをエンコードするプレフィル段階と、トークンを逐次生成するデコード段階の 2 つのフェーズを経ます。デコード段階の長さは予測不可能であり、リクエストごとに生成されるトークン数が異なるためです。

  • GPU メモリ競合:vLLM は事前に GPU メモリを KV キャッシュ用に割り当てます。キャッシュが満杯になると、サーバーは新しいリクエストをキューに追加するか、CPU メモリにスワップします。これによりレイテンシが急激に増加します。

これらの要因を考慮しない場合、一部のバックエンドにリクエストが集中し、他のバックエンドはアイドル状態になるため、テールレイテンシが増加し、GPU リソースが無駄になります。

ASM による LLM トラフィックのルーティング

ASM は、各 vLLM バックエンドから取得される多次元メトリクスを評価してルーティングを決定します。

メトリクスソースルーティング信号
リクエストキューの深さvllm:num_requests_waitingキュー内のリクエスト数が少ないほど、処理が高速であることを示す
KV キャッシュ使用率vllm:gpu_cache_usage_perc使用率が低いほど、新しいリクエスト用の GPU メモリがより多く利用可能であることを意味する

新しいリクエストが到着すると、ASM はこれらの信号の最適な組み合わせを持つバックエンドを選択します。これにより、推論レプリカ間の GPU 負荷が均等化され、TTFT が短縮され、従来のアルゴリズムと比較して全体的なスループットが向上します。

LLM トラフィックの可観測性

標準的なプロキシは HTTP ヘッダーや URL パスを解析しますが、リクエストボディは無視します。LLM 推論 API(OpenAI 互換フォーマット)は、モデル名やトークン関連パラメーターをリクエストボディに含めるため、従来の可観測性では重要なディメンションを見逃します。

ASM は LLM 推論トラフィック向けに可観測性を拡張します。

  • アクセスログには、リクエストごとのモデル名および入出力トークン数が含まれます。

  • 監視メトリクスには、モデルごとの分析を可能にする model ディメンションが追加されます。

  • トークンメトリクスasm_llm_proxy_prompt_tokensasm_llm_proxy_completion_tokens)により、ワークロード全体のトークン消費量を追跡できます。

前提条件

作業を開始する前に、以下の環境が整っていることを確認してください。

ステップ 1:サンプル vLLM 推論サービスのデプロイ

複数の LoRA アダプターを備えた vLLM で提供される Llama 2 モデルをデプロイします。このデプロイには、Kubernetes Service、チャットテンプレート用の ConfigMap、および 3 つの GPU バックエンドレプリカを持つ Deployment が含まれます。

説明

コンテナイメージには、16 GiB を超えるビデオメモリを搭載した GPU が必要です。ACK クラスターでは A10 GPU タイプを、ACS クラスターでは第 8 世代 GPU B を使用してください。T4(16 GiB)ではメモリが不足します。モデルの詳細については、チケットを送信してください。

LLM イメージはサイズが大きいため、Alibaba Cloud Container Registry (ACR) に事前に格納し、内部ネットワーク経由でプルすることを推奨します。パブリックエンドポイント経由ではダウンロードが遅くなる可能性があります。

  1. 以下の内容で vllm-service.yaml という名前のファイルを作成します。

    ACK クラスター

       apiVersion: v1
       kind: Service
       metadata:
         name: vllm-llama2-7b-pool
       spec:
         selector:
           app: vllm-llama2-7b-pool
         ports:
         - protocol: TCP
           port: 8000
           targetPort: 8000
         type: ClusterIP
       ---
       apiVersion: v1
       kind: ConfigMap
       metadata:
         name: chat-template
       data:
         llama-2-chat.jinja: |
           {% if messages[0]['role'] == 'system' %}
             {% set system_message = '<<SYS>>\n' + messages[0]['content'] | trim + '\n<</SYS>>\n\n' %}
             {% set messages = messages[1:] %}
           {% else %}
               {% set system_message = '' %}
           {% endif %}
    
           {% for message in messages %}
               {% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}
                   {{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}
               {% endif %}
    
               {% if loop.index0 == 0 %}
                   {% set content = system_message + message['content'] %}
               {% else %}
                   {% set content = message['content'] %}
               {% endif %}
               {% if message['role'] == 'user' %}
                   {{ bos_token + '[INST] ' + content | trim + ' [/INST]' }}
               {% elif message['role'] == 'assistant' %}
                   {{ ' ' + content | trim + ' ' + eos_token }}
               {% endif %}
           {% endfor %}
       ---
       apiVersion: apps/v1
       kind: Deployment
       metadata:
         name: vllm-llama2-7b-pool
         namespace: default
       spec:
         replicas: 3
         selector:
           matchLabels:
             app: vllm-llama2-7b-pool
         template:
           metadata:
             annotations:
               prometheus.io/path: /metrics
               prometheus.io/port: '8000'
               prometheus.io/scrape: 'true'
             labels:
               app: vllm-llama2-7b-pool
           spec:
             containers:
               - name: lora
                 image: "registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/llama2-with-lora:v0.2"
                 imagePullPolicy: IfNotPresent
                 command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
                 args:
                 - "--model"
                 - "/model/llama2"
                 - "--tensor-parallel-size"
                 - "1"
                 - "--port"
                 - "8000"
                 - '--gpu_memory_utilization'
                 - '0.8'
                 - "--enable-lora"
                 - "--max-loras"
                 - "4"
                 - "--max-cpu-loras"
                 - "12"
                 - "--lora-modules"
                 - 'sql-lora=/adapters/yard1/llama-2-7b-sql-lora-test_0'
                 - 'sql-lora-1=/adapters/yard1/llama-2-7b-sql-lora-test_1'
                 - 'sql-lora-2=/adapters/yard1/llama-2-7b-sql-lora-test_2'
                 - 'sql-lora-3=/adapters/yard1/llama-2-7b-sql-lora-test_3'
                 - 'sql-lora-4=/adapters/yard1/llama-2-7b-sql-lora-test_4'
                 - 'tweet-summary=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_0'
                 - 'tweet-summary-1=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_1'
                 - 'tweet-summary-2=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_2'
                 - 'tweet-summary-3=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_3'
                 - 'tweet-summary-4=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_4'
                 - '--chat-template'
                 - '/etc/vllm/llama-2-chat.jinja'
                 env:
                   - name: PORT
                     value: "8000"
                 ports:
                   - containerPort: 8000
                     name: http
                     protocol: TCP
                 livenessProbe:
                   failureThreshold: 2400
                   httpGet:
                     path: /health
                     port: http
                     scheme: HTTP
                   initialDelaySeconds: 5
                   periodSeconds: 5
                   successThreshold: 1
                   timeoutSeconds: 1
                 readinessProbe:
                   failureThreshold: 6000
                   httpGet:
                     path: /health
                     port: http
                     scheme: HTTP
                   initialDelaySeconds: 5
                   periodSeconds: 5
                   successThreshold: 1
                   timeoutSeconds: 1
                 resources:
                   limits:
                     nvidia.com/gpu: 1
                   requests:
                     nvidia.com/gpu: 1
                 volumeMounts:
                   - mountPath: /data
                     name: data
                   - mountPath: /dev/shm
                     name: shm
                   - mountPath: /etc/vllm
                     name: chat-template
             restartPolicy: Always
             schedulerName: default-scheduler
             terminationGracePeriodSeconds: 30
             volumes:
               - name: data
                 emptyDir: {}
               - name: shm
                 emptyDir:
                   medium: Memory
               - name: chat-template
                 configMap:
                   name: chat-template

    ACS クラスター

       apiVersion: v1
       kind: Service
       metadata:
         name: vllm-llama2-7b-pool
       spec:
         selector:
           app: vllm-llama2-7b-pool
         ports:
         - protocol: TCP
           port: 8000
           targetPort: 8000
         type: ClusterIP
       ---
       apiVersion: v1
       kind: ConfigMap
       metadata:
         name: chat-template
       data:
         llama-2-chat.jinja: |
           {% if messages[0]['role'] == 'system' %}
             {% set system_message = '<<SYS>>\n' + messages[0]['content'] | trim + '\n<</SYS>>\n\n' %}
             {% set messages = messages[1:] %}
           {% else %}
               {% set system_message = '' %}
           {% endif %}
    
           {% for message in messages %}
               {% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}
                   {{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}
               {% endif %}
    
               {% if loop.index0 == 0 %}
                   {% set content = system_message + message['content'] %}
               {% else %}
                   {% set content = message['content'] %}
               {% endif %}
               {% if message['role'] == 'user' %}
                   {{ bos_token + '[INST] ' + content | trim + ' [/INST]' }}
               {% elif message['role'] == 'assistant' %}
                   {{ ' ' + content | trim + ' ' + eos_token }}
               {% endif %}
           {% endfor %}
       ---
       apiVersion: apps/v1
       kind: Deployment
       metadata:
         name: vllm-llama2-7b-pool
         namespace: default
       spec:
         replicas: 3
         selector:
           matchLabels:
             app: vllm-llama2-7b-pool
         template:
           metadata:
             annotations:
               prometheus.io/path: /metrics
               prometheus.io/port: '8000'
               prometheus.io/scrape: 'true'
             labels:
               app: vllm-llama2-7b-pool
               alibabacloud.com/compute-class: gpu  # GPU コンピューティング能力を指定
               alibabacloud.com/compute-qos: default
               alibabacloud.com/gpu-model-series: "example-model" # ご利用の GPU モデルシリーズに置き換え
           spec:
             containers:
               - name: lora
                 image: "registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/llama2-with-lora:v0.2"
                 imagePullPolicy: IfNotPresent
                 command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
                 args:
                 - "--model"
                 - "/model/llama2"
                 - "--tensor-parallel-size"
                 - "1"
                 - "--port"
                 - "8000"
                 - '--gpu_memory_utilization'
                 - '0.8'
                 - "--enable-lora"
                 - "--max-loras"
                 - "4"
                 - "--max-cpu-loras"
                 - "12"
                 - "--lora-modules"
                 - 'sql-lora=/adapters/yard1/llama-2-7b-sql-lora-test_0'
                 - 'sql-lora-1=/adapters/yard1/llama-2-7b-sql-lora-test_1'
                 - 'sql-lora-2=/adapters/yard1/llama-2-7b-sql-lora-test_2'
                 - 'sql-lora-3=/adapters/yard1/llama-2-7b-sql-lora-test_3'
                 - 'sql-lora-4=/adapters/yard1/llama-2-7b-sql-lora-test_4'
                 - 'tweet-summary=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_0'
                 - 'tweet-summary-1=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_1'
                 - 'tweet-summary-2=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_2'
                 - 'tweet-summary-3=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_3'
                 - 'tweet-summary-4=/adapters/vineetsharma/qlora-adapter-Llama-2-7b-hf-TweetSumm_4'
                 - '--chat-template'
                 - '/etc/vllm/llama-2-chat.jinja'
                 env:
                   - name: PORT
                     value: "8000"
                 ports:
                   - containerPort: 8000
                     name: http
                     protocol: TCP
                 livenessProbe:
                   failureThreshold: 2400
                   httpGet:
                     path: /health
                     port: http
                     scheme: HTTP
                   initialDelaySeconds: 5
                   periodSeconds: 5
                   successThreshold: 1
                   timeoutSeconds: 1
                 readinessProbe:
                   failureThreshold: 6000
                   httpGet:
                     path: /health
                     port: http
                     scheme: HTTP
                   initialDelaySeconds: 5
                   periodSeconds: 5
                   successThreshold: 1
                   timeoutSeconds: 1
                 resources:
                   limits:
                     cpu: 16
                     memory: 64Gi
                     nvidia.com/gpu: 1
                   requests:
                     cpu: 8
                     memory: 30Gi
                     nvidia.com/gpu: 1
                 volumeMounts:
                   - mountPath: /data
                     name: data
                   - mountPath: /dev/shm
                     name: shm
                   - mountPath: /etc/vllm
                     name: chat-template
             restartPolicy: Always
             schedulerName: default-scheduler
             terminationGracePeriodSeconds: 30
             volumes:
               - name: data
                 emptyDir: {}
               - name: shm
                 emptyDir:
                   medium: Memory
               - name: chat-template
                 configMap:
                   name: chat-template
  2. 推論サービスをデプロイします。

       kubectl apply -f vllm-service.yaml
  3. 3 つのレプリカすべてが準備完了になるまで待ちます。LLM イメージはサイズが大きいため、初回プルには数分かかる場合があります。次のコマンドを実行し、3 つの Pod すべてが Running ステータスになり、1/1 のコンテナが準備完了になってから次のステップに進んでください。

       kubectl get pods -l app=vllm-llama2-7b-pool -w

ステップ 2:ASM ゲートウェイルールの構成

ASM イングレスゲートウェイのポート 8080 で HTTP トラフィックを有効にする Gateway リソースを作成します。

  1. 以下の内容で gateway.yaml という名前のファイルを作成します。

       apiVersion: networking.istio.io/v1
       kind: Gateway
       metadata:
         name: llm-inference-gateway
         namespace: default
       spec:
         selector:
           istio: ingressgateway
         servers:
           - hosts:
               - '*'
             port:
               name: http-service
               number: 8080
               protocol: HTTP
  2. Gateway リソースを適用します。

       kubectl apply -f gateway.yaml

ステップ 3:LLM 推論サービスのルーティングと負荷分散の構成

説明

LLM 対応型負荷分散と従来型負荷分散を比較するには、次のステップを実行する前に「(任意)従来型負荷分散との比較」の手順を完了してください。

このステップでは、ASM ゲートウェイを vLLM バックエンドに LLM 対応型ルーティングで接続する以下の 3 つのリソースを作成します。

リソース目的
InferencePoolラベルセレクターで vLLM Pod をグループ化し、推論ポートを指定する
InferenceModelリクエストボディのモデル名をバックエンド Pod にマッピングし、トラフィック配分を定義する
LLMRouteASM ゲートウェイと InferencePool を接続して LLM 対応型ルーティングを実現する

LLM 推論ルーティングの有効化

ASM インスタンスの kubeconfig を使用して、以下のコマンドを実行します。

kubectl patch asmmeshconfig default --type=merge \
  --patch='{"spec":{"gatewayAPIInferenceExtension":{"enabled":true}}}'

InferencePool の作成

InferencePool はラベルセレクターで vLLM Pod をグループ化し、推論ポートを指定します。

  1. 以下の内容で inferencepool.yaml という名前のファイルを作成します。

    フィールド説明
    .spec.targetPortNumber各 Pod で推論リクエストを処理するポート
    .spec.selector推論 Pod に一致するラベルセレクター。キーは app で、値は対応する Service 名と一致する必要があります
       apiVersion: inference.networking.x-k8s.io/v1alpha1
       kind: InferencePool
       metadata:
         name: vllm-llama2-7b-pool
       spec:
         targetPortNumber: 8000
         selector:
           app: vllm-llama2-7b-pool
  2. データプレーンクラスターの kubeconfig を使用して InferencePool を適用します。

       kubectl apply -f inferencepool.yaml
  3. InferencePool がアクティブになっていることを確認します。次のコマンドを実行し、ステータスに Accepted=True および ResolvedRefs=True が含まれていることを確認してから次のステップに進んでください。

       kubectl get inferencepool vllm-llama2-7b-pool -o yaml

InferenceModel の作成

InferenceModel は、受信リクエストの model パラメーターを特定のバックエンド Pod にマッピングし、トラフィック配分の重みを定義します。

  1. 以下の内容で inferencemodel.yaml という名前のファイルを作成します。

    フィールド説明
    .spec.modelNameリクエストボディの model パラメーターに一致
    .spec.targetModelsトラフィックルーティングルールを定義。この例では、model: tweet-summary を含むすべてのリクエストが該当モデルを実行中の Pod にルーティングされる
       apiVersion: inference.networking.x-k8s.io/v1alpha1
       kind: InferenceModel
       metadata:
         name: inferencemodel-sample
       spec:
         modelName: tweet-summary
         poolRef:
           group: inference.networking.x-k8s.io
           kind: InferencePool
           name: vllm-llama2-7b-pool
         targetModels:
         - name: tweet-summary
           weight: 100
  2. InferenceModel を適用します。

       kubectl apply -f inferencemodel.yaml
  3. InferenceModel がアクティブになっていることを確認します。ステータスに Accepted=True および ResolvedRefs=True が含まれていることを確認してください。

       kubectl get inferencemodel inferencemodel-sample -o yaml

LLMRoute の作成

LLMRoute は ASM ゲートウェイを InferencePool に接続し、ポート 8080 のすべてのリクエストを LLM 対応型負荷分散で推論サービスに転送します。

  1. 以下の内容で llmroute.yaml という名前のファイルを作成します。

       apiVersion: istio.alibabacloud.com/v1
       kind: LLMRoute
       metadata:
         name: test-llm-route
       spec:
         gateways:
         - llm-inference-gateway
         host: test.com
         rules:
         - backendRefs:
           - backendRef:
               group: inference.networking.x-k8s.io
               kind: InferencePool
               name: vllm-llama2-7b-pool
  2. LLMRoute を適用します。

       kubectl apply -f llmroute.yaml

ステップ 4:構成の検証

ASM ゲートウェイ経由でテストリクエストを送信し、LLM 対応型ルーティングが正常に動作していることを確認します。

curl -v \
  -H "host: test.com" \
  -H "Content-Type: application/json" \
  http://${ASM_GATEWAY_IP}:8080/v1/completions \
  -d '{
    "model": "tweet-summary",
    "prompt": "Write as if you were a critic: San Francisco",
    "max_tokens": 100,
    "temperature": 0
  }'

${ASM_GATEWAY_IP} は、ご利用の ASM イングレスゲートウェイの IP アドレスに置き換えてください。

成功した応答は以下のようになります。

{
  "id": "cmpl-2fc9a351-d866-422b-b561-874a30843a6b",
  "object": "text_completion",
  "created": 1736933141,
  "model": "tweet-summary",
  "choices": [
    {
      "index": 0,
      "text": "...",
      "logprobs": null,
      "finish_reason": "length",
      "stop_reason": null,
      "prompt_logprobs": null
    }
  ],
  "usage": {
    "prompt_tokens": 2,
    "total_tokens": 102,
    "completion_tokens": 100,
    "prompt_tokens_details": null
  }
}

コマンドを複数回実行し、リクエストが各バックエンド Pod のリアルタイム負荷に基づいて分散されることを確認してください。

(任意)ステップ 5:LLM 推論サービスの可観測性の設定

InferencePool、InferenceModel、および LLMRoute の構成後、推論リクエストレート、トークンスループット、vLLM バックエンドの健全性を追跡するためのモニタリングを設定します。

ASM での LLM トラフィック可観測性の有効化

  1. ASM コンソールで、LLM 固有のログフィールド、メトリクス、およびメトリックディメンションを有効化します。設定の詳細については、「トラフィック観測:ASM を使用した LLM トラフィックの効率的な管理」をご参照ください。設定後、ASM 監視メトリクスには model ディメンションが含まれます。これらのメトリクスは、以下のいずれかを使用して収集します:

  2. ASM の LLM 固有のトークンメトリック(asm_llm_proxy_prompt_tokens および asm_llm_proxy_completion_tokens)のスクレイプルールを、Prometheus 構成に追加します。設定の詳細については、「その他の Prometheus サービス検出構成」をご参照ください。

       scrape_configs:
       - job_name: asm-envoy-stats-llm
         scrape_interval: 30s
         scrape_timeout: 30s
         metrics_path: /stats/prometheus
         scheme: http
         kubernetes_sd_configs:
         - role: pod
         relabel_configs:
         - source_labels:
           - __meta_kubernetes_pod_container_port_name
           action: keep
           regex: .*-envoy-prom
         - source_labels:
           - __address__
           - __meta_kubernetes_pod_annotation_prometheus_io_port
           action: replace
           regex: ([^:]+)(?::\d+)?;(\d+)
           replacement: $1:15090
           target_label: __address__
         - action: labelmap
           regex: __meta_kubernetes_pod_label_(.+)
         - source_labels:
           - __meta_kubernetes_namespace
           action: replace
           target_label: namespace
         - source_labels:
           - __meta_kubernetes_pod_name
           action: replace
           target_label: pod_name
         metric_relabel_configs:
         - action: keep
           source_labels:
           - __name__
           regex: asm_llm_.*

vLLM バックエンドメトリクスの収集

vLLM サービスは、ポート 8000 の /metrics で Prometheus メトリクスを公開します。サンプル Deployment には、すでに必要なアノテーションが含まれています。

annotations:
  prometheus.io/path: /metrics
  prometheus.io/port: "8000"
  prometheus.io/scrape: "true"

Prometheus は、デフォルトのサービス検出メカニズムを通じて、これらのエンドポイントを自動的に検出し、スクレイプします。詳細については、「デフォルトの Pod サービス検出」をご参照ください。

モニタリングすべき主要な vLLM メトリクス:

メトリクス説明
vllm:gpu_cache_usage_percKV キャッシュ使用率。値が低いほど、新しいリクエスト用の GPU メモリがより多く利用可能であることを示す
vllm:request_queue_time_seconds_sumvLLM スケジューラがプレフィルおよびデコードを実行する前に、リクエストがキュー内で待機する時間
vllm:num_requests_runningvllm:num_requests_waitingvllm:num_requests_swapped推論を実行中、キューで待機中、または CPU メモリにスワップされたリクエスト数。これらを使用してバックエンドの負荷状況を評価できる
vllm:avg_generation_throughput_toks_per_svllm:avg_prompt_throughput_toks_per_sデコード段階およびプレフィル段階における 1 秒あたりのトークンスループット
vllm:time_to_first_token_seconds_bucketTTFT 分布。ユーザーがリクエスト送信後に最初のトークンを受信するまでの時間を測定するもので、ユーザーが感じるレイテンシの重要な指標となる

Grafana ダッシュボードの作成

ASM メトリクス(リクエストレート、トークンスループット)と vLLM メトリクス(GPU キャッシュ、キューの深さ、TTFT)を統合した Grafana ダッシュボードを設定し、推論クラスターの統一ビューを実現します。

  1. Grafana の Prometheus データソースが、ASM メトリクスと vLLM メトリクスの両方を収集していることを確認します。

  2. 以下に示すダッシュボード JSON を Grafana にインポートします。完全なダッシュボード JSON は、ASM ドキュメントページからダウンロードしてください(そのページで [Dashboard JSON] を展開して、すべての内容をコピーします)。

  3. Grafana で Dashboards > Import に移動し、JSON を貼り付けて Prometheus データソースを選択します。

Grafana dashboard for LLM inference monitoring

(任意)従来型負荷分散との比較

可観測性ダッシュボードを使用して、LLM 対応型負荷分散と従来型負荷分散の違いを測定します。ステップ 3 で LLM 対応型ルーティングを構成する前に、この比較を実施してください。

説明

すでにステップ 3 を完了している場合は、まず LLM ルーティングリソースをクリーンアップしてください。

kubectl delete inferencemodel --all
kubectl delete inferencepool --all
kubectl delete llmroute --all
  1. 従来のラウンドロビン負荷分散を使用してトラフィックをルーティングする VirtualService を作成します。

       kubectl apply -f- <<EOF
       apiVersion: networking.istio.io/v1
       kind: VirtualService
       metadata:
         name: llm-vs
         namespace: default
       spec:
         gateways:
           - default/llm-inference-gateway
         hosts:
           - '*'
         http:
           - name: any-host
             route:
               - destination:
                   host: vllm-llama2-7b-pool.default.svc.cluster.local
                   port:
                     number: 8000
       EOF
  2. llmperf などのツールを使用して、推論サービスに対して負荷テストを実行します。

  3. VirtualService を削除し、ステップ 3 に従って LLM 対応型ルーティングを構成します。次のステップに進む前に、VirtualService リソースが残っていないことを確認してください。同じ負荷テストを再度実行します。

  4. Grafana ダッシュボードで結果を比較します。LLM 対応型負荷分散では、以下の効果が得られます。

    • TTFT(最初のトークンまでの時間)が短縮される

    • トークンスループットが向上する

    • レプリカ間の KV キャッシュ使用率がより均等になる

Performance comparison: traditional vs. LLM-aware load balancing

クリーンアップ

このチュートリアルで作成されたすべてのリソースを削除するには:

# LLM ルーティングリソースの削除
kubectl delete inferencemodel --all
kubectl delete inferencepool --all
kubectl delete llmroute --all

# ゲートウェイの削除
kubectl delete -f gateway.yaml

# 推論サービスの削除
kubectl delete -f vllm-service.yaml

関連トピック