本文在一台具备 NVIDIA GPU 的 Linux 服务器上,用 Docker 部署 vLLM OpenAI 兼容接口,然后完成健康检查、功能验证、真实长度压测、显存调优和回滚。命令以单机单卡为起点;模型、镜像和参数必须按你的许可证、GPU 与 vLLM 版本调整。
1. 部署目标与验收标准
目标端口只监听本机,由网关对外提供认证和 TLS。验收不仅是容器 Running,还包括:容器能识别 GPU;模型接口正常;非法模型和超长请求返回明确错误;P95 首 token 延迟、每 token 延迟、错误率与峰值显存满足预算;旧版本可以恢复。
2. 检查 GPU、驱动、Docker 与磁盘
nvidia-smi
docker version
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
df -h /var/lib/docker /srv/model-cache
nvidia-smi topo -m预期:主机和测试容器显示相同 GPU;缓存盘有权重、临时分片和至少一个回滚版本的空间。主机能执行 nvidia-smi 但容器失败时,优先检查 NVIDIA Container Toolkit,而不是修改 vLLM 参数。记录驱动版本和 GPU 拓扑,后续多卡性能异常时需要这些证据。
3. 固定镜像与模型版本
不要直接在生产使用 latest。先查当前 vLLM 文档与目标模型卡,选择明确镜像版本或 digest;模型使用固定 revision。示例模型只是教学占位,部署前确认许可证和显存。
export VLLM_IMAGE="vllm/vllm-openai:v0.8.5"
export MODEL_ID="Qwen/Qwen2.5-7B-Instruct"
docker pull "$VLLM_IMAGE"
docker image inspect "$VLLM_IMAGE" --format '{{index .RepoDigests 0}}'
mkdir -p /srv/model-cache把输出 digest 写入发布记录。访问令牌通过 secret 注入,不写入 Dockerfile、Compose 或 shell 历史。
4. 用 Docker Compose 固化配置
services:
vllm:
image: vllm/vllm-openai:v0.8.5
command:
- --model=Qwen/Qwen2.5-7B-Instruct
- --served-model-name=local-chat
- --dtype=auto
- --max-model-len=8192
- --gpu-memory-utilization=0.88
ports:
- "127.0.0.1:8000:8000"
volumes:
- /srv/model-cache:/root/.cache/huggingface
ipc: host
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 5s
retries: 5
start_period: 180s先执行 docker compose config,确认展开后的镜像、端口和命令。ipc: host 常用于共享内存通信,但会扩大边界;受限环境应评估显式 shm_size。端口绑定 127.0.0.1,避免未认证接口暴露公网。
docker compose config
docker compose up -d
docker compose ps
docker compose logs -f --tail=200 vllm加载阶段同时观察 watch -n 1 nvidia-smi。若反复退出,保存第一次错误,不要无界重启。
5. 验证模型列表和聊天接口
curl -fsS http://127.0.0.1:8000/v1/models | jq .
curl -fsS http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model":"local-chat",
"temperature":0,
"max_tokens":128,
"messages":[{"role":"user","content":"用三点说明健康检查的作用"}]
}' | tee /tmp/vllm-smoke.json | jq .检查 model、choices、finish_reason、usage 和内容。随后提交不存在的模型、缺失 messages、超过上限的输入,确认服务返回 4xx 且容器保持健康。功能测试使用固定提示,后续升级可以直接对比。
6. 理解显存预算与关键参数
显存由权重、KV Cache、运行时工作区和并发请求共同占用。量化只减少部分权重占用,KV Cache 仍随序列长度和并发增长。不要因为模型标称支持 32K,就把 max-model-len 直接设为 32K。
| 参数 | 主要影响 | 调优方法 |
|---|---|---|
| max-model-len | 最大上下文与 KV Cache 预算 | 按业务 P99 长度加余量 |
| gpu-memory-utilization | 引擎可使用的显存比例 | 从保守值开始,保留故障余量 |
| max-num-seqs | 同时调度的序列数 | 结合长短请求混合压测 |
| tensor-parallel-size | 模型跨 GPU 切分 | 与可见 GPU 和拓扑一致 |
7. 用真实长度分布进行压测
从脱敏流量统计输入/输出 token 的 P50、P90、P99,构造短、中、长三组。下面脚本测并发流式请求的 TTFT 和总耗时;正式报告还要从 usage 获取 token 数并计算 TPOT。
import asyncio, statistics, time, httpx
URL = "http://127.0.0.1:8000/v1/chat/completions"
PROMPTS = ["解释幂等性并给出 API 示例。" * n for n in (1, 20, 80)]
async def call(client, prompt):
start = time.perf_counter(); first = None
async with client.stream("POST", URL, json={
"model":"local-chat","stream":True,"temperature":0,"max_tokens":256,
"messages":[{"role":"user","content":prompt}]}) as r:
r.raise_for_status()
async for line in r.aiter_lines():
if line.startswith("data:") and line != "data: [DONE]":
first = first or time.perf_counter()
end = time.perf_counter()
return ((first-start)*1000, (end-start)*1000)
async def main():
async with httpx.AsyncClient(timeout=180) as c:
rows = await asyncio.gather(*(call(c, PROMPTS[i % 3]) for i in range(30)))
print("TTFT median(ms):", statistics.median(x[0] for x in rows))
print("total p95(ms):", sorted(x[1] for x in rows)[int(len(rows)*.95)-1])
asyncio.run(main())每个并发级别持续足够时间并重复至少三次,记录错误率、队列、TTFT、TPOT、吞吐、GPU 利用率、温度、功耗和峰值显存。出现排队拐点后继续加压只会恶化尾延迟。
8. 根据症状调优
| 症状 | 先检查 | 处理顺序 |
|---|---|---|
| 加载时 OOM | 精度、权重、可见 GPU | 合适量化、多卡或更小模型 |
| 运行后 OOM | 长请求、并发、KV Cache | 限制长度、降低 max-num-seqs、调整显存比例 |
| TTFT 高、TPOT 正常 | 队列、输入长度、prefill | 限流、长短请求隔离、扩容 |
| TPOT 变慢 | GPU 降频、batch、通信 | 检查温度功耗、调度和拓扑 |
| 输出格式异常 | chat template、tokenizer | 固定模板并回归 |
一次只修改一个参数,并保留同一压测输入。否则无法判断改善来自哪里。
9. 网关、安全与灰度发布
vLLM 前必须有网关负责 TLS、身份、租户限流、请求体和上下文上限。流式接口禁用代理缓冲。模型服务账户只读访问模型缓存,不持有业务数据库凭据。
location /v1/ {
proxy_pass http://vllm_upstream;
proxy_http_version 1.1;
proxy_buffering off;
proxy_connect_timeout 3s;
proxy_read_timeout 180s;
client_max_body_size 2m;
}新旧实例并存:先运行固定评测和影子流量,再放 1%–5% 灰度。发布前定义自动停止条件,例如 P95、错误率、质量或显存超阈值。回滚切回旧 upstream 与完整旧配置,不在故障中临时猜参数。
10. 总结
可靠的 vLLM 部署必须同时固定版本、验证 GPU、限制暴露面、用真实长度压测并准备回滚。性能优化的核心是用 TTFT、TPOT、队列和显存证据定位瓶颈,而不是把显存比例和并发盲目调到最大。