View a markdown version of this page

Amazon EKS でのモデルのロードと提供 - Amazon EKS

このページの改善にご協力ください

このユーザーガイドに貢献するには、すべてのページの右側のペインにある「GitHub でこのページを編集する」リンクを選択してください。

Amazon EKS でのモデルのロードと提供

ヒント

今後開催予定の Amazon EKS AI/ML ワークショップに登録してください。

このセクションのステップでは、Amazon EKS に大規模言語モデル (LLM) をデプロイし、vLLM で提供して、推論エンドポイントとやり取りします。

このチュートリアルでは、以下のツールを使用します。

  • vLLM — LLM サービスおよび GPU メモリ管理用に最適化された高スループットの推論エンジン。

  • Run:ai Model Streamer — モデルの重みを Amazon S3 から GPU メモリに直接ストリーミングし、ロード時間を数分から数秒に短縮します。

  • Open WebUI — vLLM の OpenAI 互換 API に接続するセルフホスト型チャットフロントエンド。

このセクションでは、Ministral-3-8B-Instruct-2512 モデルを使用しますが、vLLM がサポートする任意の AI モデルをデプロイできます。サポートされているモデルのリストについては、vLLM ドキュメントの「Supported models」を参照してください。

重要

AI/ML ワークロード用の Amazon EKS クラスターを設定する のセクションで作成したクラスターを使用します。このチュートリアルの手順は、EKS Auto Mode とセルフマネージド Karpenter の両方で機能します。

Amazon EKS での vLLM を使用した LLM 推論ワークフローを示すアーキテクチャ図

アーキテクチャ図はエンドツーエンドのフローを示しています。

  1. モデルの重みは Hugging Face から Amazon S3 にダウンロードされます。

  2. vLLM は Run:ai Model Streamer を使用して、モデルを S3 から GPU メモリに直接ストリーミングします。

  3. ユーザーは vLLM エンドポイントに推論リクエストを送信します。

これらのステップを完了すると、チャットフロントエンドアプリケーションを介して Ministral モデルを操作するために使用できる vLLM 推論エンドポイントが利用可能になります。

前提条件

クラスターのセットアップセクション」のステップを完了します。

新しいターミナルを開いた場合は、「CLI によるクラスターのセットアップ」セクションで使用したクラスター名とリージョンを設定します。

export CLUSTER_NAME=ai-eks-docs export AWS_REGION=us-east-2

モデル重み S3 バケット」ステップで作成したモデル重みバケットを検索します。

MODEL_BUCKET=$(aws s3api list-buckets \ --query "Buckets[?starts_with(Name, '${CLUSTER_NAME}-models-')].Name | [0]" \ --output text) echo "Model bucket: ${MODEL_BUCKET}"

ステップ 1: Hugging Face からモデルをダウンロードする

このステップでは、Hugging Face からモデルをダウンロードし、前提条件セクションで作成した S3 バケットにアップロードする Kubernetes ジョブをデプロイします。

モデルをダウンロードするには、以下のジョブマニフェストを適用します。

cat << EOF | kubectl apply -f - apiVersion: batch/v1 kind: Job metadata: name: model-download namespace: default labels: guide: ai-eks-docs spec: backoffLimit: 10 activeDeadlineSeconds: 3600 ttlSecondsAfterFinished: 86400 template: spec: restartPolicy: Never serviceAccountName: model-storage-sa containers: - name: downloader image: python:3.11-slim command: ["/bin/bash", "-c"] args: - | set -e pip install -q huggingface_hub boto3 echo "Downloading Ministral-3-8B-Instruct-2512 from Hugging Face..." python3 -c "from huggingface_hub import snapshot_download; snapshot_download('mistralai/Ministral-3-8B-Instruct-2512', local_dir='/tmp/mistral', allow_patterns=['*.json', '*.txt', '*.md', 'consolidated.safetensors'], ignore_patterns=['model-*.safetensors', 'model.safetensors.index.json'])" echo "Uploading to S3 bucket: \${MODEL_BUCKET}" python3 << 'PYTHON' import boto3 import os from pathlib import Path s3 = boto3.client('s3') bucket = os.environ.get('MODEL_BUCKET') local_dir = Path("/tmp/mistral") for file_path in local_dir.rglob("*"): if file_path.is_file(): if '.cache' in file_path.parts: continue s3_key = f"Ministral-3-8B-Instruct-2512/{file_path.relative_to(local_dir)}" print(f"Uploading {file_path.name}...") s3.upload_file(str(file_path), bucket, s3_key) print("Upload complete!") PYTHON env: - name: MODEL_BUCKET value: "${MODEL_BUCKET}" - name: HF_HUB_DISABLE_XET value: "1" resources: requests: memory: "2Gi" cpu: "1" limits: memory: "4Gi" cpu: "2" EOF

ジョブが完了するまで待ちます。モデルの重み (consolidated.safetensors) は約 10.4 GB で、このステップには通常 3~5 分かかります。

kubectl wait --for=condition=complete job/model-download --timeout=600s

正常な出力:

job.batch/model-download condition met

モデルの重みが S3 にアップロードされたことを確認します。

aws s3 ls s3://$(kubectl get job model-download -o jsonpath='{.spec.template.spec.containers[0].env[?(@.name=="MODEL_BUCKET")].value}')/Ministral-3-8B-Instruct-2512/ --recursive

正常な出力:

2026-05-18 10:29:53 20311 Ministral-3-8B-Instruct-2512/README.md 2026-05-18 10:29:53 2361 Ministral-3-8B-Instruct-2512/SYSTEM_PROMPT.txt 2026-05-18 10:29:53 1903 Ministral-3-8B-Instruct-2512/config.json 2026-05-18 10:29:54 10420633176 Ministral-3-8B-Instruct-2512/consolidated.safetensors 2026-05-18 10:29:53 131 Ministral-3-8B-Instruct-2512/generation_config.json 2026-05-18 10:29:53 1185 Ministral-3-8B-Instruct-2512/params.json 2026-05-18 10:29:53 976 Ministral-3-8B-Instruct-2512/processor_config.json 2026-05-18 10:29:53 16753777 Ministral-3-8B-Instruct-2512/tekken.json 2026-05-18 10:29:53 17077402 Ministral-3-8B-Instruct-2512/tokenizer.json 2026-05-18 10:29:53 21168 Ministral-3-8B-Instruct-2512/tokenizer_config.json

consolidated.safetensors ファイルには、モデルの重み (約 10.4 GB) が含まれています。残りのファイルは、vLLM がモデルを提供するために必要とする設定ファイルとトークナイザファイルです。

ステップ 2: 推論コンテナをデプロイする

このセクションでは、vLLM を Kubernetes デプロイとしてデプロイして、Amazon S3 にアップロードしたモデルを提供します。

このセクションでは、深層学習フレームワークがプリインストールされ、AWS インフラストラクチャのパフォーマンスに最適化された Docker イメージである AWS Deep Learning Containers (DLC) を使用します。DLC には、セキュリティパッチ、検証済みフレームワークバージョン、最適化された GPU ドライバー設定が含まれます。

このデプロイでは、SOCI サポートで vLLM 0.21.0 に以下の AWS DLC を使用します。

public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci

イメージタグは、GPU をサポートする vLLM 0.21.0、Python 3.12、CUDA 13.0、Ubuntu 22.04 を示し、EC2 ベースのワークロード用に最適化され、コンテナの起動を高速化するための SOCI が有効化されています。

このマニフェストは、GPU ノードで vLLM を実行するデプロイを作成し、Run:ai Model Streamer を使用して S3 から GPU メモリに直接モデルをストリーミングします。マニフェストは、クラスター内アクセスのためにポート 8000 で vLLM エンドポイントを公開する ClusterIP サービスも作成します。

マニフェストを適用します。

cat << EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: vllm-inference-app labels: guide: ai-eks-docs spec: replicas: 1 selector: matchLabels: app: vllm-inference-app template: metadata: labels: app: vllm-inference-app guide: ai-eks-docs spec: serviceAccountName: model-storage-sa tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule nodeSelector: karpenter.sh/nodepool: gpu-inf containers: - name: vllm-inference image: public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci ports: - containerPort: 8000 args: - "--model=s3://${MODEL_BUCKET}/Ministral-3-8B-Instruct-2512/" - "--host=0.0.0.0" - "--port=8000" - "--tensor-parallel-size=1" - "--gpu-memory-utilization=0.9" - "--max-model-len=8192" - "--max-num-seqs=1" - "--load-format=runai_streamer" - "--enforce-eager" - "--tokenizer_mode=mistral" - "--config_format=mistral" - "--enable-auto-tool-choice" - "--tool-call-parser=mistral" resources: limits: nvidia.com/gpu: 1 requests: memory: "40Gi" cpu: "8" --- apiVersion: v1 kind: Service metadata: name: vllm-inference-svc namespace: default labels: app: vllm-inference-app spec: selector: app: vllm-inference-app ports: - name: http port: 8000 targetPort: 8000 protocol: TCP EOF

vLLM ポッドが準備完了状態になっていることを確認してください。

kubectl get pod -l app=vllm-inference-app -w

正常な出力:

NAME READY STATUS RESTARTS AGE vllm-inference-app-65df5fddc8-5kmjm 1/1 Running 0 86s

コンテナイメージがプルされ、vLLM がモデルの重みを S3 から GPU メモリにストリーミングするまでに約 2 分かかる場合があります。ポッドの READY 列に 1/1 が表示されるまで待ってから続行します。

EKS、SOCI、Run:ai Model Streamer の組み合わせにより、ポッドの高速起動が可能になります。各ステージの起動時間を確認するには、ポッドイベントを表示します。

kubectl describe pod -l app=vllm-inference-app | grep -A 20 "Events:"

正常な出力:

Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 86s default-scheduler 0/2 nodes are available: 2 node(s) had untolerated taint(s). Normal Nominated 85s eks-auto-mode/compute Pod should schedule on: nodeclaim/gpu-inf-kqkq6 Normal Scheduled 55s default-scheduler Successfully assigned default/vllm-inference-app-d9d54586d-csmd7 to i-04f8792414384d2d3 Normal Pulling 52s kubelet Pulling image "public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci" Normal Pulled 4s kubelet Successfully pulled image "public.ecr.aws/deep-learning-containers/vllm:0.21.0-gpu-py312-cu130-ubuntu22.04-ec2-v1.0-soci" in 48.376s (48.376s including waiting). Image size: 8802823997 bytes. Normal Created 4s kubelet Created container vllm-inference Normal Started 4s kubelet Started container vllm-inference

この例では、GPU ノードが 30 秒でプロビジョニングされ、8.8 GB コンテナイメージが SOCI を使用して約 48 秒でプルされました。高速イメージプルは、大規模な推論コンテナのコールドスタート時間を短縮します。これにより、アイドル状態の GPU キャパシティを過剰にプロビジョニングするのではなく、GPU ポッドを動的にスケールできます。

次に、vLLM ログをチェックしてモデルのロード時間を確認します。

kubectl logs $(kubectl get pod -l app=vllm-inference-app -o jsonpath='{.items[0].metadata.name}') | grep -i 'Model loading took'

正常な出力:

INFO 05-18 18:41:49 [gpu_model_runner.py:4959] Model loading took 9.81 GiB memory and 5.023344 seconds

ログでは、Run:ai Model Streamer が 10.4 GB モデルの重みを S3 から直接 GPU メモリに約 5 秒でロードし、9.8 GiB の GPU メモリを消費したことを確認できます。

この例のイメージのダウンロード時間は、20 Gbps の持続的なネットワーク帯域幅を持つ g6e.4xlarge インスタンスを使用したものです。他のインスタンスタイプでは、利用可能なネットワーク帯域幅に応じて、イメージのプルとモデルのロード時間が異なります。

ステップ 3: 推論を実行する

vLLM デプロイが実行されている状態で、推論エンドポイントを検証し、チャットフロントエンドをデプロイしてモデルとやり取りします。

モデル検証テストを実行する

ポートフォワードを介して推論エンドポイントを公開します。

kubectl port-forward svc/vllm-inference-svc 8000:8000

新しいターミナルウィンドウを開き、推論コンテナが応答していることを確認します。

curl -sI -X GET http://localhost:8000/health

正常な出力:

HTTP/1.1 200 OK date: Fri, 18 May 2026 00:39:23 GMT server: uvicorn content-length: 0

ステップ 4: vLLM をモニタリングする

vLLM は、リクエストレート、トークンスループット、エンドツーエンドのレイテンシー、GPU KV キャッシュ使用率など、Prometheus メトリクスをすぐに公開します。このセクションでは、「AI/ML ワークロード用の Amazon EKS クラスターを設定する」の手順で設定したモニタリングスタックでこれらのメトリクスを使用し、事前プロビジョニングされた Grafana ダッシュボードで表示します。

重要

続行する前に、「CLI を使用して AI/ML ワークロード用の Amazon EKS クラスターを設定する」の「モニタリング」サブセクションを完了する必要があります。このステップは、インストールされている kube-prometheus-stack と、値ファイルに既にプロビジョニングされている vLLM Grafana ダッシュボードによって異なります。

vLLM ServiceMonitor を適用する

ServiceMonitor は、vLLM メトリクスをスクレイプする場所を Prometheus に指示します。

cat << EOF | kubectl apply -f - apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: vllm-inference-app namespace: default labels: release: kube-prometheus-stack spec: selector: matchLabels: app: vllm-inference-app endpoints: - port: http path: /metrics interval: 15s EOF

ServiceMonitor が作成されたことを確認します。

kubectl get servicemonitor vllm-inference-app

正常な出力:

NAME AGE vllm-inference-app 5s

ダッシュボードにメトリクスを入力するには、検証ステップでポートフォワードを介して既に公開している vLLM エンドポイントに対して推論トラフィックを生成します。

提供されているモデル名を見つけます。

MODEL_NAME=$(curl -s http://localhost:8000/v1/models | jq -r '.data[0].id') echo "Using model: $MODEL_NAME"

50 件のチャット完了リクエストを並行して送信します。

for i in $(seq 1 50); do curl -s -X POST http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d "{\"model\": \"$MODEL_NAME\", \"messages\": [{\"role\": \"user\", \"content\": \"Write a short poem about Kubernetes.\"}], \"max_tokens\": 128}" \ > /dev/null & done wait

トラフィックが流れる間 (またはその直後)、vLLM /metrics エンドポイントから直接トークンスループットメトリクスを確認します。

curl -s http://localhost:8000/metrics | grep -E '^vllm:(prompt_tokens_total|generation_tokens_total|avg_generation_throughput_toks_per_s|avg_prompt_throughput_toks_per_s)' | head

vllm:prompt_tokens_total および vllm:generation_tokens_total メトリクスは、提供された入力トークンと出力トークンの単調増加カウンターです。vllm:avg_prompt_throughput_toks_per_s および vllm:avg_generation_throughput_toks_per_s メトリクスは、ローリング平均スループットゲージです。これらの同じメトリクスは、以下のサブセクションで開いた Grafana ダッシュボードの基盤となるものです。

vLLM Grafana ダッシュボードを表示する

モニタリング」セクションの kube-prometheus-stack 値ファイルには、[GPU モニタリング] フォルダの下にコミュニティ vLLM ダッシュボード (gnetId 25263) が既にプロビジョニングされているため、追加のインポートは必要ありません。

Grafana にアクセスするには、Grafana サービスへのポートフォワードを開始します。

kubectl port-forward svc/kube-prometheus-stack-grafana 3000:80 -n monitoring

ブラウザで http://localhost:3000 を開き、[ダッシュボード] > [GPU モニタリング] > [vLLM メトリクス] に移動します。

vLLM Grafana ダッシュボード

リクエストレート、トークンスループット、エンドツーエンドレイテンシー、GPU KV キャッシュ使用率を示す vLLM Grafana ダッシュボード

ダッシュボードには、vLLM 推論エンドポイントのリクエストレート、プロンプトと生成トークンのスループット、レイテンシーパーセンタイル、GPU KV キャッシュ使用率が表示されます。

ステップ 5: チャットアプリケーションをデプロイする

このステップでは、Open WebUI をチャットフロントエンドとしてデプロイし、モデルとやり取りします。Open WebUI は、オープンソースのセルフホスト型 AI インターフェイスであり、OpenAI 互換 API をサポートし、会話履歴とマークダウンレンダリングを備えたチャットインターフェイスを提供します。vLLM は OpenAI 互換 API を公開するため、Open WebUI はバックエンドとして直接接続します。

Open WebUI アプリケーションをデプロイするには、以下のマニフェストを適用します。

cat << 'EOF' | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: open-webui namespace: default labels: app: open-webui guide: ai-eks-docs spec: replicas: 1 selector: matchLabels: app: open-webui template: metadata: labels: app: open-webui guide: ai-eks-docs spec: containers: - name: open-webui image: ghcr.io/open-webui/open-webui:v0.9.2 ports: - containerPort: 8080 resources: requests: cpu: "500m" memory: "500Mi" limits: cpu: "1000m" memory: "1Gi" env: - name: OPENAI_API_BASE_URLS value: "http://vllm-inference-svc:8000/v1" - name: OPENAI_API_KEY value: "dummy" - name: WEBUI_AUTH value: "False" - name: ENABLE_OLLAMA_API value: "False" - name: ENABLE_EVALUATION_ARENA_MODELS value: "False" volumeMounts: - name: webui-volume mountPath: /app/backend/data volumes: - name: webui-volume emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: open-webui namespace: default labels: app: open-webui spec: type: ClusterIP selector: app: open-webui ports: - protocol: TCP port: 80 targetPort: 8080 EOF

Open WebUI ポッドの準備ができるまで待ちます。

kubectl wait --for=condition=ready pod -l app=open-webui --timeout=300s

正常な出力:

pod/open-webui-6cbfc9867f-jf9w9 condition met

アプリケーションにアクセスするには、ポート転送を設定し、ブラウザでアプリケーションを開きます。

kubectl port-forward svc/open-webui 8080:80 & sleep 5 echo "Open WebUI: http://localhost:8080"

ブラウザで http://localhost:8080 を開きます。

チャットインターフェイスが表示され、Ministral モデルを操作できます。

テストが完了したら、kill %1 %2 を実行してバックグラウンドのポートフォワードプロセスを停止します (または、jobs を実行してそれらを一覧表示し、それぞれに対して kill %<jobspec> を実行します)。

Ministral モデルとの会話を示す Open WebUI チャットインターフェイスのスクリーンショット

クリーンアップ

このセクションで作成したワークロードリソースを削除するには、Open WebUI アプリケーション、vLLM 推論サーバー、およびモデルダウンロードジョブを削除します。

kubectl delete deployment open-webui kubectl delete service open-webui kubectl delete deployment vllm-inference-app kubectl delete service vllm-inference-svc kubectl delete servicemonitor vllm-inference-app kubectl delete job model-download

クラスター、NodePool、S3 バケットなどのインフラストラクチャリソースを削除する手順については、「クラスターセットアップのクリーンアップ」を参照してください。