View a markdown version of this page

在 Amazon EKS 上加载和服务模型 - Amazon EKS

帮助改进此页面

要帮助改进本用户指南,请选择位于每个页面右侧窗格中的在 GitHub 上编辑此页面链接。

在 Amazon EKS 上加载和服务模型

提示

注册参加即将举办的 Amazon EKS 人工智能/机器学习讲习会。

本节中的步骤将在 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 模型。有关支持的模型列表,请参阅 vLM 文档中的 Supported models

重要

使用您在 设置用于人工智能/机器学习工作负载的 Amazon EKS 集群 部分中创建的集群。本演练中的说明对于 EKS 自动模式和自行管理的 Karpenter 均适用。

显示在 Amazon EKS 上使用 vLLM 执行推理工作流的架构图

该架构图显示了以下端到端的流程:

  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 下载模型

在此步骤中,您将部署一个 Kubernetes 作业,以从 Hugging Face 下载模型并将其上传到您在先决条件部分中创建的 S3 存储桶。

要下载模型,请应用以下作业清单:

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 深度学习容器(DLC),这是一种预装了深度学习框架的 Docker 映像,并针对 AWS 基础设施进行了性能优化。DLC 包含安全补丁、经验证的框架版本和优化版 GPU 驱动程序配置。

此部署使用了以下适用于的 vLLM 0.21.0 的 AWS DLC,并且支持 SOCI:

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 内存。该清单还创建了一个 ClusterIP 服务,在端口 8000 上公开 vLLM 端点以用于集群内访问。

应用清单:

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 秒内完成了预调配,使用 SOCI 在大约 48 秒内拉取了 8.8 GB 的容器映像。快速映像拉取缩短了大型推理容器的冷启动时间,让您可以动态扩展 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 在大约 5 秒钟内将 10.4 GB 的模型权重直接从 S3 加载到 GPU 内存中,消耗了 9.8 GiB 的 GPU 内存。

此示例中的映像下载时间是使用 g6e.4xlarge 实例的下载时间,其持续网络带宽为 20 Gbps。其他实例类型上的映像拉取和模型加载时间可能与此不同,具体取决于可用的网络带宽。

第 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 会公开若干开箱即用的 Prometheus 指标,包括请求速率、词元吞吐量、端到端延迟和 GPU KV 缓存利用率等。在本节中,您需要将这些指标与您在集群设置步骤中设置的监控堆栈结合使用,并在预调配的 Grafana 控制面板上查看。

重要

必须首先完成通过 CLI 设置集群一节的监控部分,然后再继续操作。此步骤依赖要安装的 kube-prometheus-stack 以及已经在值文件中预调配的 vLLM Grafana 控制面板。

应用 vLLM ServiceMonitor

ServiceMonitor 会告诉 Prometheus 从哪里抓取 vLLM 指标。

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_totalvllm:generation_tokens_total 指标是单调增加所服务输入和输出词元的计数器。vllm:avg_prompt_throughput_toks_per_svllm:avg_generation_throughput_toks_per_s 指标是滚动平均吞吐量指标。这些指标也将用于支持您在下一小节中打开的 Grafana 控制面板。

查看 vLLM Grafana 控制面板

监控部分中的 kube-prometheus-stack 值文件已经在 GPU Monitoring 文件夹下预调配了社区 vLLM 控制面板(gnetID 25263),因此无需额外导入。

要访问 Grafana,请启动一个指向 Grafana 服务的端口转发:

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

在浏览器中打开 http://localhost:3000 并导航到控制面板 > GPU 监控 > vLLM 指标

vLLM Grafana 控制面板

vLLM Grafana 控制面板会显示请求速率、词元吞吐量、端到端延迟和 GPU KV 缓存利用率等指标

控制面板会显示 vLLM 推理端点的请求速率、提示和生成词元吞吐量、延迟百分位数以及 GPU KV 缓存利用率等指标。

第 5 步:部署聊天应用程序

在此步骤中,您需要将 Open WebUI 部署为聊天前端以与模型进行互动。Open WebUI 是一种开源的自托管 AI 界面,支持兼容 OpenAI 的 API,并提供包含对话历史记录和 Markdown 渲染的聊天界面。由于 vLLM 公开了兼容 OpenAI 的 API,因此 Open WebUI 将作为后端直接连接到该 API。

要部署 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>)。

Open WebUI 聊天界面屏幕截图,显示了与 Ministral 模型的对话

清理

要移除您在本节中创建的工作负载资源,请删除 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

有关移除基础设施资源(例如集群、节点池和 S3 存储桶)的说明,请参阅集群设置清理