
vLLM 生产部署完整指南:从 Docker 到 K8s 的每一步
把 vLLM 从演示级推到生产级:镜像版本锁定、GPU 内存调优、前缀缓存、KEDA 自动扩缩、监控告警,一套可以直接抄的完整配置
原文来源:SitePoint — vLLM 生产部署完整指南:从 PagedAttention 原理到 Docker/K8s 部署、性能调优、监控告警的端到端教程
跑通一个 vLLM demo 很容易,一行 vllm serve 就行。但把它变成可靠、可观测、能自动扩缩的生产服务,中间隔着一条很宽的沟。这篇指南把沟里的每一步都补齐了:架构原理、Docker 部署、Kubernetes 编排、性能调优、监控告警,全部是可复制的配置。
先说结论——生产部署的八条军规:
- 锁定镜像版本:永远不用
latest,pin 到具体 release tag。 --max-model-len按实际需求设,不是模型的理论上限。--gpu-memory-utilization设在 0.85–0.95,用压测找甜点。- 开 Prometheus 指标,盯 TTFT p99、队列深度、KV 缓存占用。
- 告警:KV 缓存饱和超 95%、TTFT p99 超 SLA 都要报警。
- 限流放在反向代理层,vLLM 本身不支持按用户配额。
- 上线前用
benchmark_serving.py压测,模拟真实流量。 - 凭据走 .env / Docker secrets / K8s Secrets,绝不进命令行参数。
为什么 vLLM 快:三个核心机制
生产部署前先理解 vLLM 为什么和其他推理服务器不一样。
PagedAttention:传统推理的 KV 缓存按请求分配连续显存块,请求长度参差不齐时内部碎片严重。PagedAttention 借鉴操作系统的虚拟内存思想,把 KV 缓存切成固定大小的块(页),逻辑位置映射到不连续的物理块——碎片被回收,同一块 GPU 能塞进更多并发请求。在 80GB H100 上跑 7B FP16 模型,可能是 30 个并发和 100+ 个并发的差别。
前缀缓存:多个请求共享 system prompt 或 few-shot 前缀时,vLLM 直接复用这些 KV 块,省掉重复的 prefill 计算。聊天和 agent 场景下每个请求都带相同 system prompt,收益最明显。
V1 引擎:2025 年发布后成为默认(v0.6.0 起)。核心改动是调度时不再在 GPU/CPU 之间复制中间张量,改用 host 内存固定 + 零拷贝 DMA。更大的变化是分离 prefill 和 decode:prefill 是计算密集的,decode 是显存带宽密集的,V1 把两个阶段放进独立的调度域,长 prefill 不再阻塞在途请求的 decode——这是生产环境延迟尖峰的头号原因。配合分块 prefill,长 prompt(比如 32K token)被切成小块与 decode 批次交错,不会独占 GPU 几百毫秒。
模型支持方面:Llama 3/3.1、Mistral/Mixtral、Qwen 2/2.5、DeepSeek-V2/V3,以及 LLaVA、Qwen-VL 等多模态。量化上,AWQ(4-bit)和 GPTQ 各有校准路线,AWQ 在 vLLM 里通常吞吐高 5–15%;Hopper 的 FP8 接近无损、比 FP16 快约 2 倍;GGUF 主要适合 CPU offload 场景,GPU-first 生产不推荐。
—— 广告 ——
Docker 部署:单卡起步
镜像用 vllm/vllm-openai:<tag>,先到 GitHub releases 确认 tag 存在。单卡部署的关键参数:
docker run -d \
--name vllm-server \
--gpus '"device=0"' \
--shm-size=4g \
-p 8000:8000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env-file .env \
vllm/vllm-openai:<your-release-tag> \
--model hugging-quants/Meta-Llama-3.1-8B-Instruct-AWQ-INT4 \
--served-model-name llama-3.1-8b \
--max-model-len 8192 \
--quantization awq \
--dtype auto \
--gpu-memory-utilization 0.90 \
--enable-prefix-caching \
--port 8000.env 文件:
HUGGING_FACE_HUB_TOKEN=<your-hf-token>
VLLM_API_KEY=<your-api-key>几个坑要提前避开:
--quantization awq要求预量化的 AWQ 权重。基础版meta-llama/Llama-3.1-8B-Instruct不是 AWQ 量化过的,直接跑会报错。用hugging-quants/...-AWQ-INT4这类变体,或去掉这个 flag。--shm-size必须给够,NCCL 通信靠共享内存。多卡场景直接--ipc=host。- 凭据绝不写进
docker run命令行——docker inspect、进程列表、shell 历史里全能看到。用--env-file .env,VLLM_API_KEY环境变量会被 vLLM 自动读取。 --max-model-len设低一点是特性不是缺陷:它直接决定 KV 缓存预留多少显存,设低释放显存给更大 batch。
多卡用张量并行切分模型,--tensor-parallel-size 必须和分配的 GPU 数一致:
docker run -d \
--name vllm-server-tp4 \
--gpus '"device=0,1,2,3"' \
--shm-size=16g \
--ipc=host \
-p 8000:8000 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
--env-file .env \
-e NCCL_DEBUG=WARN \
vllm/vllm-openai:<your-release-tag> \
--model meta-llama/Llama-3.1-70B-Instruct \
--served-model-name llama-3.1-70b \
--tensor-parallel-size 4 \
--max-model-len 16384 \
--dtype auto \
--gpu-memory-utilization 0.90 \
--enable-prefix-caching \
--port 8000NCCL 在多卡间通信,共享内存不够会报出令人抓狂的运行时错误。--ipc=host 是关键。
Kubernetes 部署:生产级编排
K8s 部署有四个要点:探针、拓扑分布、PVC 缓存、KEDA 自动扩缩。
探针:vLLM 的 /health 端点加载完模型返回 200。注意 start_period: 120s——模型加载很慢,别让探针在启动期就把 pod 杀了。
拓扑分布:用 topologySpreadConstraints 让副本落在不同节点,避免单节点故障带走全部推理能力。多副本 + 共享模型缓存时 PVC 必须用 ReadWriteMany(NFS、CephFS 或云厂商 RWX 存储类),ReadWriteOnce 只允许单节点挂载。模型加载速度直接受存储吞吐影响,PVC 要用 fast SSD 存储类。
Ingress 两个关键注解:
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-buffering: "off"proxy-read-timeout 300 秒是为了容纳长生成请求(高 max_tokens);proxy-buffering 必须关——开着的话 Server-Sent Events 会被缓存到整个响应结束才发送,流式输出直接废掉。
KEDA 自动扩缩:扩缩信号用"每副本等待请求数",而不是 GPU 利用率——利用率高是正常现象,不表示容量耗尽:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: vllm-scaler
namespace: llm-serving
spec:
scaleTargetRef:
name: vllm-inference
minReplicaCount: 1
maxReplicaCount: 8
cooldownPeriod: 300
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus.monitoring:9090
metricName: vllm_requests_waiting_per_replica
query: |
sum(vllm:num_requests_waiting{namespace="llm-serving"})
/ scalar(
count(up{job="vllm", namespace="llm-serving"} == 1)
or vector(1)
)
threshold: "5"
activationThreshold: "2"cooldownPeriod: 300 防抖——GPU pod 从调度到就绪要 1–5 分钟(取决于模型大小和存储速度),快速缩容等于白烧加载时间。or vector(1) 防止全挂时除以零,保证还能从零拉起。
多节点大模型:超过单节点显存(如 Llama 3.1 405B 要 8+ 卡)需要 Ray + NCCL 跨节点,网络要求苛刻——NCCL 在普通 TCP/IP 上表现很差,生产要用 InfiniBand 或 RoCE,70B 以上模型建议至少 100 Gbps 节点间带宽。实操建议:多数场景下,多副本跑小模型(或量化版)比单模型跨节点切分吞吐更高,通信开销是主要原因。
OpenAI 兼容 API 与结构化输出
vLLM 暴露 /v1/completions、/v1/chat/completions、/v1/embeddings。--served-model-name 可以起别名(如 llama-production),现有应用代码不用改。注意 Assistants API、fine-tuning 等 OpenAI 专属端点不支持。
生产环境建议用 guided decoding 约束结构化 JSON 输出——对 tool calling 和 agent 工作流至关重要。配合流式:
from openai import OpenAI
import os, json, openai
client = OpenAI(
base_url="https://llm-api.example.com/v1",
api_key=os.environ["VLLM_API_KEY"],
)
try:
response = client.chat.completions.create(
model="llama-3.1-8b",
messages=[
{"role": "system", "content": "Extract entities as JSON."},
{"role": "user", "content": "Apple announced the M4 chip in Cupertino."},
],
max_tokens=512,
temperature=0.1,
top_p=0.95,
stream=True,
response_format={
"type": "json_schema",
"json_schema": {
"name": "entities",
"schema": {
"type": "object",
"properties": {
"entities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"type": {"type": "string"},
},
"required": ["name", "type"],
},
}
},
"required": ["entities"],
},
},
},
)
response_parts = []
for chunk in response:
if chunk.choices and chunk.choices[0].delta.content is not None:
response_parts.append(chunk.choices[0].delta.content)
print(json.loads("".join(response_parts)))
except openai.AuthenticationError as e:
raise RuntimeError(f"认证失败,检查 VLLM_API_KEY: {e}") from e
except openai.RateLimitError as e:
print(f"被限流: {e}")
raise
except openai.APIConnectionError as e:
print(f"连接错误(可重试): {e}")
raise性能调优三板斧
显存调优:--gpu-memory-utilization 默认 0.90,压测后可以微调。--enforce-eager 关闭 CUDA graph 捕获,省 5–15% 显存(30B 以上模型),接近显存上限时有用。
批处理调优:continuous batching 动态把新请求加进在途批次。--max-num-seqs 默认 256,调低减内存压力但压吞吐;--max-num-batched-tokens 控制每轮 token 预算。延迟敏感场景开分块 prefill 并调小块,防止 decode 被长 prefill 阻塞。
投机解码:decode-bound 场景(延迟主导)用一个小 draft 模型并行提议 token,主模型验证,draft 接受率 ≥0.7 时提速 1.3–2 倍。draft 模型匹配不好可能没收益甚至倒退。配置:--speculative-model + --num-speculative-tokens。
压测:用 vLLM 仓库自带的 benchmark_serving.py:
git clone https://github.com/vllm-project/vllm.git && cd vllm
python benchmarks/benchmark_serving.py \
--backend vllm \
--endpoint /v1/completions \
--model llama-3.1-8b \
--dataset-name sharegpt \
--num-prompts 500 \
--request-rate 10 \
--base-url http://localhost:8000重点对比不同 --quantization 和 --max-num-seqs 下的吞吐和 TTFT。
监控与告警
vLLM 暴露 /metrics(Prometheus 格式)。核心指标:
vllm:num_requests_running— 活跃请求数vllm:num_requests_waiting— 队列深度,容量耗尽的主要信号vllm:gpu_cache_usage_perc— KV 缓存饱和度vllm:time_to_first_token_seconds— TTFT 直方图vllm:e2e_request_latency_seconds— 端到端延迟直方图
Grafana 面板建议:请求吞吐、TTFT p50/p95/p99、KV 缓存利用率、队列深度、端到端延迟分位数。注意指标单位:gpu_cache_usage_perc 有的版本报 0–100 百分比,有的报 0.0–1.0 小数,先用 curl http://<pod>:8000/metrics | grep gpu_cache_usage_perc 确认再设阈值。
告警规则:KV 缓存占用超 95% 持续 2 分钟(容量耗尽、请求即将被拒)、TTFT p99 超 SLA、错误率异常飙升。
请求级追踪:反向代理传入自定义 X-Request-ID,在客户端、代理、推理服务器日志中关联,就能做端到端定位。vLLM 日志自带请求 ID 和时间分解。
高可用与安全检查清单
多副本 + Service 提供基本 HA,/health 让负载均衡绕开不健康的 pod。terminationGracePeriodSeconds 设 60–120 秒,让在途请求完成再杀 pod。PVC 模型缓存把冷启动从分钟级(网络下载)降到秒级(本地磁盘加载)。
安全方面:vLLM 不做按用户限流,多租户必须靠反向代理(Nginx/Envoy/API 网关)做 per-client 配额;K8s 里用 NetworkPolicy 把 vLLM pod 的入口限制为只允许反向代理访问,防止其他工作负载直连。
最后过一遍 checklist:锁定镜像 tag、按需设 --max-model-len、压测调 --gpu-memory-utilization、开指标建面板、配 KV 饱和和 TTFT 告警、反代限流、真实流量压测、凭据走环境变量。八条全过,才算生产就绪。
© 2026 四月
原文链接:https://www.aprilzz.com/tutorials/vllm-production-deployment-guide
相关文章
Docker Compose 生产环境部署完整指南:从开发到上线的每一步
从 Dockerfile 编写到 Compose 编排,从多阶段构建到健康检查,从日志管理到安全加固——一份面向开发者的 Docker Compose 生产部署实战教程
2026 本地 LLM 完全入手指南:用 Ollama 在个人电脑上跑大模型
从零开始的 Ollama 折腾教程:安装、模型管理、Python API 集成、Docker 部署,以及怎么把本地模型接入开发工作流
Self-Hosting 入门指南:2026 年为什么你应该自托管(以及如何开始)
从 Nextcloud 到 n8n,从 Vaultwarden 到 Uptime Kuma——自托管替代 SaaS 的完整路线图:为什么值得做、需要多少钱、如何一步步开始