

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

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

# Amazon EKS でのモデルのロードと提供
<a name="ml-inference-load-serve-model"></a>

**ヒント**  
 今後開催予定の Amazon EKS AI/ML ワークショップに[登録](https://events.eksworkshop.com/workshops/genai/)してください。

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

このチュートリアルでは、以下のツールを使用します。
+  [vLLM](https://docs.vllm.ai/en/latest/) — LLM サービスおよび GPU メモリ管理用に最適化された高スループットの推論エンジン。
+  [Run:ai Model Streamer](https://github.com/run-ai/runai-model-streamer) — モデルの重みを Amazon S3 から GPU メモリに直接ストリーミングし、ロード時間を数分から数秒に短縮します。
+  [Open WebUI](https://openwebui.com/) — vLLM の OpenAI 互換 API に接続するセルフホスト型チャットフロントエンド。

このセクションでは、[Ministral-3-8B-Instruct-2512 モデル](https://huggingface.co/mistralai/Ministral-3-8B-Instruct-2512)を使用しますが、vLLM がサポートする任意の AI モデルをデプロイできます。サポートされているモデルのリストについては、vLLM ドキュメントの「[Supported models](https://docs.vllm.ai/en/latest/models/supported_models/#text-generation)」を参照してください。

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

![Amazon EKS での vLLM を使用した LLM 推論ワークフローを示すアーキテクチャ図](http://docs.aws.amazon.com/ja_jp/eks/latest/userguide/images/ml-inference-load-serve-model-arch.png)


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

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

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

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

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

## 前提条件
<a name="_prerequisites"></a>

「[クラスターのセットアップセクション](ml-cluster-setup.md)」のステップを完了します。

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

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

「[モデル重み S3 バケット](ml-cluster-setup-cli.md#cluster-setup-cli-model-bucket)」ステップで作成したモデル重みバケットを検索します。

```
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 からモデルをダウンロードする
<a name="_step_1_download_the_model_from_hugging_face"></a>

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

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

### モデルダウンロードジョブマニフェスト
<a name="ml-inference-step1-model-download-yaml"></a>

```
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: 推論コンテナをデプロイする
<a name="_step_2_deploy_the_inference_container"></a>

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

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

このデプロイでは、SOCI サポートで [vLLM 0.21.0](https://gallery.ecr.aws/deep-learning-containers/vllm) に以下の 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 サービスも作成します。

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

### vLLM デプロイとサービス YAML
<a name="ml-inference-step2-vllm-deployment-yaml"></a>

```
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: 推論を実行する
<a name="_step_3_run_inference"></a>

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

### モデル検証テストを実行する
<a name="_run_a_model_validation_test"></a>

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

```
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 をモニタリングする
<a name="ml-inference-load-serve-model-monitoring"></a>

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

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

### vLLM ServiceMonitor を適用する
<a name="ml-inference-load-serve-model-monitoring-servicemonitor"></a>

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
```

#### 推論トラフィックを生成する
<a name="ml-inference-load-serve-model-monitoring-traffic"></a>

ダッシュボードにメトリクスを入力するには、検証ステップでポートフォワードを介して既に公開している 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 ダッシュボードを表示する
<a name="ml-inference-load-serve-model-monitoring-dashboard"></a>

「[モニタリング](ml-cluster-setup-cli.md#cluster-setup-cli-monitoring)」セクションの kube-prometheus-stack 値ファイルには、**[GPU モニタリング]** フォルダの下にコミュニティ [vLLM ダッシュボード (gnetId 25263)](https://grafana.com/grafana/dashboards/25263-vllm-metrics/) が既にプロビジョニングされているため、追加のインポートは必要ありません。

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

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

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

 **vLLM Grafana ダッシュボード** 

![リクエストレート、トークンスループット、エンドツーエンドレイテンシー、GPU KV キャッシュ使用率を示す vLLM Grafana ダッシュボード](http://docs.aws.amazon.com/ja_jp/eks/latest/userguide/images/ml-inference-load-serve-model-vllm-monitoring.png)


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

## ステップ 5: チャットアプリケーションをデプロイする
<a name="_step_5_deploy_chat_application"></a>

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

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

### WebUI デプロイとサービス YAML を開く
<a name="ml-inference-step5-open-webui-yaml"></a>

```
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](http://localhost:8080) を開きます。

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

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

![Ministral モデルとの会話を示す Open WebUI チャットインターフェイスのスクリーンショット](http://docs.aws.amazon.com/ja_jp/eks/latest/userguide/images/ml-inference-load-serve-model-chatui.png)


## クリーンアップ
<a name="_clean_up"></a>

このセクションで作成したワークロードリソースを削除するには、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 バケットなどのインフラストラクチャリソースを削除する手順については、「[クラスターセットアップのクリーンアップ](ml-cluster-setup-cli.md#cluster-setup-cli-cleanup)」を参照してください。