従来の負荷分散はリクエストをバックエンド間で均等に分配しますが、大規模言語モデル(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_tokens、asm_llm_proxy_completion_tokens)により、ワークロード全体のトークン消費量を追跡できます。
前提条件
作業を開始する前に、以下の環境が整っていることを確認してください。
Container Service for Kubernetes (ACK) で管理される GPU ノードプールを備えたクラスター、または GPU 計算能力向けの推奨ゾーン 内の Container Compute Service (ACS) クラスター
ACK クラスターの場合は、「ACK マネージドクラスターの作成」をご参照ください。
ACS クラスターについては、「ACS クラスターを作成する」をご参照ください。
(任意) ACK クラスターで ACS GPU 計算能力を使用するには、ACK Virtual Node コンポーネントをインストールします。詳細については、「ACS GPU 計算能力を ACK で使用する」をご参照ください。
ASM インスタンス v1.24 以降(ご自身のクラスターが追加済み)。詳細については、「ASM インスタンスにクラスターを追加する」をご参照ください。
ポート 8080 で HTTP サービスが有効化されたイングレスゲートウェイ。詳細については、「イングレスゲートウェイの作成」をご参照ください。
(オプション) 可観測性には、
default名前空間でサイドカーインジェクションを有効にする必要があります。「自動サイドカープロキシ注入を有効にする」をご参照ください。
ステップ 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) に事前に格納し、内部ネットワーク経由でプルすることを推奨します。パブリックエンドポイント経由ではダウンロードが遅くなる可能性があります。
以下の内容で
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-templateACS クラスター
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推論サービスをデプロイします。
kubectl apply -f vllm-service.yaml3 つのレプリカすべてが準備完了になるまで待ちます。LLM イメージはサイズが大きいため、初回プルには数分かかる場合があります。次のコマンドを実行し、3 つの Pod すべてが
Runningステータスになり、1/1のコンテナが準備完了になってから次のステップに進んでください。kubectl get pods -l app=vllm-llama2-7b-pool -w
ステップ 2:ASM ゲートウェイルールの構成
ASM イングレスゲートウェイのポート 8080 で HTTP トラフィックを有効にする Gateway リソースを作成します。
以下の内容で
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: HTTPGateway リソースを適用します。
kubectl apply -f gateway.yaml
ステップ 3:LLM 推論サービスのルーティングと負荷分散の構成
LLM 対応型負荷分散と従来型負荷分散を比較するには、次のステップを実行する前に「(任意)従来型負荷分散との比較」の手順を完了してください。
このステップでは、ASM ゲートウェイを vLLM バックエンドに LLM 対応型ルーティングで接続する以下の 3 つのリソースを作成します。
| リソース | 目的 |
|---|---|
| InferencePool | ラベルセレクターで vLLM Pod をグループ化し、推論ポートを指定する |
| InferenceModel | リクエストボディのモデル名をバックエンド Pod にマッピングし、トラフィック配分を定義する |
| LLMRoute | ASM ゲートウェイと InferencePool を接続して LLM 対応型ルーティングを実現する |
LLM 推論ルーティングの有効化
ASM インスタンスの kubeconfig を使用して、以下のコマンドを実行します。
kubectl patch asmmeshconfig default --type=merge \
--patch='{"spec":{"gatewayAPIInferenceExtension":{"enabled":true}}}'InferencePool の作成
InferencePool はラベルセレクターで vLLM Pod をグループ化し、推論ポートを指定します。
以下の内容で
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データプレーンクラスターの kubeconfig を使用して InferencePool を適用します。
kubectl apply -f inferencepool.yamlInferencePool がアクティブになっていることを確認します。次のコマンドを実行し、ステータスに
Accepted=TrueおよびResolvedRefs=Trueが含まれていることを確認してから次のステップに進んでください。kubectl get inferencepool vllm-llama2-7b-pool -o yaml
InferenceModel の作成
InferenceModel は、受信リクエストの model パラメーターを特定のバックエンド Pod にマッピングし、トラフィック配分の重みを定義します。
以下の内容で
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: 100InferenceModel を適用します。
kubectl apply -f inferencemodel.yamlInferenceModel がアクティブになっていることを確認します。ステータスに
Accepted=TrueおよびResolvedRefs=Trueが含まれていることを確認してください。kubectl get inferencemodel inferencemodel-sample -o yaml
LLMRoute の作成
LLMRoute は ASM ゲートウェイを InferencePool に接続し、ポート 8080 のすべてのリクエストを LLM 対応型負荷分散で推論サービスに転送します。
以下の内容で
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-poolLLMRoute を適用します。
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 トラフィック可観測性の有効化
ASM コンソールで、LLM 固有のログフィールド、メトリクス、およびメトリックディメンションを有効化します。設定の詳細については、「トラフィック観測:ASM を使用した LLM トラフィックの効率的な管理」をご参照ください。設定後、ASM 監視メトリクスには
modelディメンションが含まれます。これらのメトリクスは、以下のいずれかを使用して収集します: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_perc | KV キャッシュ使用率。値が低いほど、新しいリクエスト用の GPU メモリがより多く利用可能であることを示す |
vllm:request_queue_time_seconds_sum | vLLM スケジューラがプレフィルおよびデコードを実行する前に、リクエストがキュー内で待機する時間 |
vllm:num_requests_running、vllm:num_requests_waiting、vllm:num_requests_swapped | 推論を実行中、キューで待機中、または CPU メモリにスワップされたリクエスト数。これらを使用してバックエンドの負荷状況を評価できる |
vllm:avg_generation_throughput_toks_per_s、vllm:avg_prompt_throughput_toks_per_s | デコード段階およびプレフィル段階における 1 秒あたりのトークンスループット |
vllm:time_to_first_token_seconds_bucket | TTFT 分布。ユーザーがリクエスト送信後に最初のトークンを受信するまでの時間を測定するもので、ユーザーが感じるレイテンシの重要な指標となる |
Grafana ダッシュボードの作成
ASM メトリクス(リクエストレート、トークンスループット)と vLLM メトリクス(GPU キャッシュ、キューの深さ、TTFT)を統合した Grafana ダッシュボードを設定し、推論クラスターの統一ビューを実現します。
Grafana の Prometheus データソースが、ASM メトリクスと vLLM メトリクスの両方を収集していることを確認します。
以下に示すダッシュボード JSON を Grafana にインポートします。完全なダッシュボード JSON は、ASM ドキュメントページからダウンロードしてください(そのページで [Dashboard JSON] を展開して、すべての内容をコピーします)。
Grafana で Dashboards > Import に移動し、JSON を貼り付けて Prometheus データソースを選択します。

(任意)従来型負荷分散との比較
可観測性ダッシュボードを使用して、LLM 対応型負荷分散と従来型負荷分散の違いを測定します。ステップ 3 で LLM 対応型ルーティングを構成する前に、この比較を実施してください。
すでにステップ 3 を完了している場合は、まず LLM ルーティングリソースをクリーンアップしてください。
kubectl delete inferencemodel --all
kubectl delete inferencepool --all
kubectl delete llmroute --all従来のラウンドロビン負荷分散を使用してトラフィックをルーティングする 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 EOFllmperf などのツールを使用して、推論サービスに対して負荷テストを実行します。
VirtualService を削除し、ステップ 3 に従って LLM 対応型ルーティングを構成します。次のステップに進む前に、VirtualService リソースが残っていないことを確認してください。同じ負荷テストを再度実行します。
Grafana ダッシュボードで結果を比較します。LLM 対応型負荷分散では、以下の効果が得られます。
TTFT(最初のトークンまでの時間)が短縮される
トークンスループットが向上する
レプリカ間の KV キャッシュ使用率がより均等になる

クリーンアップ
このチュートリアルで作成されたすべてのリソースを削除するには:
# LLM ルーティングリソースの削除
kubectl delete inferencemodel --all
kubectl delete inferencepool --all
kubectl delete llmroute --all
# ゲートウェイの削除
kubectl delete -f gateway.yaml
# 推論サービスの削除
kubectl delete -f vllm-service.yaml