Kubernetes クラスターの大規模言語モデル(LLM)推論サービスでは、従来のロードバランシング方法は単純なトラフィック割り当てに依存していることが多く、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 推論効率を最適化します。
KV キャッシュ
手順
次の図はフローチャートです。
inference-gateway では、ポート 8080 は標準の HTTP ルートで設定され、リクエストをバックエンド推論サービスに転送します。ポート 8081 は、リクエストを推論サービス拡張機能(LLM ルート)にルーティングし、そこからリクエストをバックエンド推論サービスに転送します。
HTTP ルートでは、InferencePool リソースを使用して、クラスター内の LLM 推論サービスワークロードのグループを宣言します。 InferenceModel を構成して、InferencePool 内で選択されたモデルのトラフィック分散ポリシーを指定します。このようにして、inference-gateway コンポーネントのポート 8081 を介してルーティングされたリクエストは、推論サービス用に設計された拡張ロードバランシングアルゴリズムを使用して、InferencePool によって指定された推論サービスワークロードに転送されます。
前提条件
GPU ノードプールを持つ ACK マネージドクラスター が作成されていること。ACK マネージドクラスターに ACK Virtual Node コンポーネントをインストールして、ACK Pro クラスターで ACS の計算能力を使用する ことができます。
手順
ステップ 1: サンプル推論サービスをデプロイする
vllm-service.yaml という名前のファイルを作成し、次の内容をファイルにコピーします。
説明このトピックで使用されているイメージについては、ACK クラスターには A10 カード、Alibaba Cloud Container Compute Service (ACS) GPU 計算能力には GN8IS (第 8 世代 GPU B) カードを使用することをお勧めします。
LLM イメージのサイズが大きいため、事前に Container Registry に転送し、内部ネットワークアドレスを使用してプルすることをお勧めします。パブリックネットワークからのプルの速度は、クラスターの Elastic IP アドレス (EIP) の帯域幅構成によって異なり、待機時間が長くなる可能性があります。
サンプル推論サービスをデプロイします。
kubectl apply -f vllm-service.yaml
ステップ 2: 推論拡張機能付きゲートウェイ コンポーネントをインストールする推論拡張機能付きゲートウェイ<script>
推論拡張機能付きゲートウェイコンポーネントをインストールする で、[Gateway API 推論拡張を有効にする] を選択します。

ステップ 3: 推論ルーティングをデプロイする
このステップでは、InferencePool リソースと InferenceModel リソースを作成します。
inference-pool.yaml という名前のファイルを作成します。
apiVersion: inference.networking.x-k8s.io/v1alpha2 kind: InferencePool metadata: name: vllm-qwen-pool spec: targetPortNumber: 8000 selector: app: qwen extensionRef: name: inference-gateway-ext-proc --- apiVersion: inference.networking.x-k8s.io/v1alpha2 kind: InferenceModel metadata: name: inferencemodel-qwen spec: modelName: /model/qwen criticality: Critical poolRef: group: inference.networking.x-k8s.io kind: InferencePool name: vllm-qwen-pool targetModels: - name: /model/qwen weight: 100推論ルーティングをデプロイします。
kubectl apply -f inference-gateway-llm.yaml
ステップ 4: ゲートウェイをデプロイして検証する
このステップでは、ポート 8080 と 8081 を含むゲートウェイを作成します。
inference-gateway.yaml という名前のファイルを作成します。
apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: name: qwen-inference-gateway-class spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: qwen-inference-gateway spec: gatewayClassName: qwen-inference-gateway-class listeners: - name: http protocol: HTTP port: 8080 - name: llm-gw protocol: HTTP port: 8081 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: qwen-backend spec: parentRefs: - name: qwen-inference-gateway sectionName: llm-gw rules: - backendRefs: - group: inference.networking.x-k8s.io kind: InferencePool name: vllm-qwen-pool matches: - path: type: PathPrefix value: / --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: qwen-backend-no-inference spec: parentRefs: - group: gateway.networking.k8s.io kind: Gateway name: qwen-inference-gateway sectionName: http rules: - backendRefs: - group: "" kind: Service name: qwen port: 8000 weight: 1 matches: - path: type: PathPrefix value: / --- apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy metadata: name: backend-timeout spec: timeout: http: requestTimeout: 1h targetRef: group: gateway.networking.k8s.io kind: Gateway name: qwen-inference-gatewayゲートウェイをデプロイします。
kubectl apply -f inference-gateway.yamlこのステップでは、
envoy-gateway-systemという名前のネームスペースと、envoy-default-inference-gateway-645xxxxxという名前のサービスがクラスターに作成されます。ゲートウェイのパブリック IP アドレスを取得します。
export GATEWAY_HOST=$(kubectl get gateway/qwen-inference-gateway -o jsonpath='{.status.addresses[0].value}')ゲートウェイがポート 8080 の標準 HTTP ルーティングを介して推論サービスにルーティングされていることを確認します。
curl -X POST ${GATEWAY_HOST}:8080/v1/chat/completions -H 'Content-Type: application/json' -d '{ "model": "/model/qwen", "max_completion_tokens": 100, "temperature": 0, "messages": [ { "role": "user", "content": "Write as if you were a critic: San Francisco" } ] }'期待される出力:
{"id":"chatcmpl-aa6438e2-d65b-4211-afb8-ae8e76e7a692","object":"chat.completion","created":1747191180,"model":"/model/qwen","choices":[{"index":0,"message":{"role":"assistant","reasoning_content":null,"content":"San Francisco, a city that has long been a beacon of innovation, culture, and diversity, continues to captivate the world with its unique charm and character. As a critic, I find myself both enamored and occasionally perplexed by the city's multifaceted personality.\n\nSan Francisco's architecture is a testament to its rich history and progressive spirit. The iconic cable cars, Victorian houses, and the Golden Gate Bridge are not just tourist attractions but symbols of the city's enduring appeal. However, the","tool_calls":[]},"logprobs":null,"finish_reason":"length","stop_reason":null}],"usage":{"prompt_tokens":39,"total_tokens":139,"completion_tokens":100,"prompt_tokens_details":null},"prompt_logprobs":null}ゲートウェイがポート 8081 の推論サービス拡張機能を介して推論サービスにルーティングされていることを確認します。
curl -X POST ${GATEWAY_HOST}:8081/v1/chat/completions -H 'Content-Type: application/json' -d '{ "model": "/model/qwen", "max_completion_tokens": 100, "temperature": 0, "messages": [ { "role": "user", "content": "Write as if you were a critic: Los Angeles" } ] }'期待される出力:
{"id":"chatcmpl-cc4fcd0a-6a66-4684-8dc9-284d4eb77bb7","object":"chat.completion","created":1747191969,"model":"/model/qwen","choices":[{"index":0,"message":{"role":"assistant","reasoning_content":null,"content":"Los Angeles, the sprawling metropolis often referred to as \"L.A.,\" is a city that defies easy description. It is a place where dreams are made and broken, where the sun never sets, and where the line between reality and fantasy is as blurred as the smog that often hangs over its valleys. As a critic, I find myself both captivated and perplexed by this city that is as much a state of mind as it is a physical place.\n\nOn one hand, Los","tool_calls":[]},"logprobs":null,"finish_reason":"length","stop_reason":null}],"usage":{"prompt_tokens":39,"total_tokens":139,"completion_tokens":100,"prompt_tokens_details":null},"prompt_logprobs":null}
(オプション) ステップ 5: LLM サービスの可観測性メトリクスとダッシュボードを構成する
クラスターで Managed Service for Prometheus を有効にして使用する必要があります。これには追加料金が発生する場合があります。
Prometheus インスタンスの デフォルトのサービスディスカバリー メカニズムを使用してメトリクスを収集するために、vLLM サービスポッドに Prometheus メトリクス収集アノテーションを追加できます。これにより、vLLM サービスの内部状態を監視します。
... annotations: prometheus.io/path: /metrics // メトリクスの公開先 HTTP パス prometheus.io/port: "8000" // メトリクスの公開ポート。vLLM サーバーのリスニングポート prometheus.io/scrape: "true" // 現在のポッドのメトリクスをスクレイプするかどうかを指定します ...次の表に、vLLM サービスによって提供されるいくつかの監視メトリクスを示します。
メトリック
説明
vllm:gpu_cache_usage_perc
vLLM による GPU キャッシュの使用率。 vLLM が起動すると、KV キャッシュのためにできるだけ多くの GPU ビデオメモリを事前に占有します。 vLLM サーバーの場合、使用率が低いほど、GPU が新しいリクエストにリソースを割り当てるためのスペースが増えます。
vllm:request_queue_time_seconds_sum
待機状態でキューに費やされた時間。 LLM 推論リクエストが vLLM サーバーに到着した後、すぐに処理されない場合があり、vLLM スケジューラがプリフィルとデコードをスケジュールするのを待つ必要があります。
vllm:num_requests_running
vllm:num_requests_waiting
vllm:num_requests_swapped
推論を実行中、待機中、メモリにスワップされたリクエストの数。これは、vLLM サービスの現在のリクエスト負荷を評価するために使用できます。
vllm:avg_generation_throughput_toks_per_s
vllm:avg_prompt_throughput_toks_per_s
プリフィルステージで消費されたトークン数/秒と、デコードステージで生成されたトークン数/秒。
vllm:time_to_first_token_seconds_bucket
リクエストが vLLM サービスに送信されてから最初のトークンが応答されるまでのレイテンシレベル。このメトリックは通常、リクエストコンテンツを出力してからクライアントが最初の応答を受信するまでにかかる時間を表し、LLM ユーザーエクスペリエンスに影響を与える重要なメトリックです。
これらのメトリクスに基づいて、特定のアラートルールを設定して、LLM サービスパフォーマンスのリアルタイム監視と異常検出を有効にできます。
LLM 推論サービスのリアルタイム監視用に Grafana ダッシュボードを構成します。 Grafana ダッシュボードを使用して、vLLM に基づいてデプロイされた LLM 推論サービスを観察できます。
LLM サービスのリクエストレートと合計トークンスループットを監視する。
推論ワークロードの内部状態を監視する。
Grafana のデータソースとして機能する Prometheus インスタンスが vLLM の監視メトリクスを収集していることを確認します。 LLM 推論サービスの可観測ダッシュボードを作成するには、次のコンテンツを Grafana にインポート します。

ヒント: ブラウザの開発者ツールを使用して、REST API のレスポンスを確認できます。

ACK クラスタの場合、vllm ベンチマーク を使用して推論サービスのストレステストを実行し、標準 HTTP ルーティングと推論拡張ルーティングの負荷分散機能を比較します。
ストレステスト ワークロードをデプロイします。
kubectl apply -f- <<EOF apiVersion: apps/v1 kind: Deployment metadata: labels: app: vllm-benchmark name: vllm-benchmark namespace: default spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: vllm-benchmark strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: creationTimestamp: null labels: app: vllm-benchmark spec: containers: - command: - sh - -c - sleep inf image: registry-cn-hangzhou.ack.aliyuncs.com/dev/llm-benchmark:random-and-qa imagePullPolicy: IfNotPresent name: vllm-benchmark resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: {} terminationGracePeriodSeconds: 30 EOFストレステストを開始します。
ゲートウェイの内部 IP アドレスを取得します。
export GW_IP=$(kubectl get svc -n envoy-gateway-system -l gateway.envoyproxy.io/owning-gateway-namespace=default,gateway.envoyproxy.io/owning-gateway-name=qwen-inference-gateway -o jsonpath='{.items[0].spec.clusterIP}')ストレステストを実行します。
標準 HTTP ルーティング
kubectl exec -it deploy/vllm-benchmark -- env GW_IP=${GW_IP} python3 /root/vllm/benchmarks/benchmark_serving.py \ --backend vllm \ --model /models/DeepSeek-R1-Distill-Qwen-7B \ --served-model-name /model/qwen \ --trust-remote-code \ --dataset-name random \ --random-prefix-len 10 \ --random-input-len 1550 \ --random-output-len 1800 \ --random-range-ratio 0.2 \ --num-prompts 3000 \ --max-concurrency 200 \ --host $GW_IP \ --port 8080 \ --endpoint /v1/completions \ --save-result \ 2>&1 | tee benchmark_serving.txt推論サービス ルーティング
kubectl exec -it deploy/vllm-benchmark -- env GW_IP=${GW_IP} python3 /root/vllm/benchmarks/benchmark_serving.py \ --backend vllm \ --model /models/DeepSeek-R1-Distill-Qwen-7B \ --served-model-name /model/qwen \ --trust-remote-code \ --dataset-name random \ --random-prefix-len 10 \ --random-input-len 1550 \ --random-output-len 1800 \ --random-range-ratio 0.2 \ --num-prompts 3000 \ --max-concurrency 200 \ --host $GW_IP \ --port 8081 \ --endpoint /v1/completions \ --save-result \ 2>&1 | tee benchmark_serving.txt
テスト後、ダッシュボードで標準 HTTP ルーティングと推論サービス拡張のルーティング機能を比較できます。

ご覧のとおり、HTTP ルートを使用するワークロードのキャッシュ使用率分布は不均一ですが、LLM ルートを使用するワークロードのキャッシュ使用率分布は正常です。
次のステップ
推論拡張機能付きゲートウェイは、さまざまな推論シナリオの要件を満たすために、さまざまな負荷分散ポリシーを提供します。InferencePool 内のポッドの負荷分散ポリシーを構成するには、inference.networking.x-k8s.io/routing-strategy アノテーションを InferencePool の構成に追加します。
次のサンプル YAML テンプレートは、app: vllm-app セレクターを使用して推論サービスポッドを選択し、推論サーバーのメトリックに基づいて動作するデフォルトの負荷分散ポリシーを使用します。
apiVersion: inference.networking.x-k8s.io/v1alpha2
kind: InferencePool
metadata:
name: vllm-app-pool
annotations:
inference.networking.x-k8s.io/routing-strategy: "DEFAULT"
spec:
targetPortNumber: 8000
selector:
app: vllm-app
extensionRef:
name: inference-gateway-ext-proc次の表に、推論拡張機能付き ACK ゲートウェイによって提供される負荷分散ポリシーを示します。
ポリシー | 説明 |
DEFAULT | 推論サービスのメトリックに基づいて動作するデフォルトの負荷分散ポリシー。このポリシーは、多次元メトリックに基づいて推論サーバーのステータスを評価し、評価されたステータスに基づいて負荷分散を実行します。メトリックには、リクエストキューの長さと GPU キャッシュ使用率が含まれます。 |
PREFIX_CACHE | リクエストプレフィックス一致負荷分散ポリシー。このポリシーは、同じプレフィックスを持つリクエストを同じ推論サーバー上のポッドに送信しようとします。このポリシーは、同じプレフィックスを持つ大量のリクエストを受信し、推論サーバーで自動プレフィックスキャッシュ機能が有効になっているシナリオに適しています。 次のリストは、典型的なシナリオを示しています。
|