vLLM 生产环境部署踩坑实录:DeepSeek-R1 / Qwen3 多模型并发避坑指南
vLLM 生产环境部署踩坑实录:DeepSeek-R1 / Qwen3 多模型并发避坑指南
基于生产环境真实部署经验整理,涵盖多模型并发、显存优化、常见报错排查。
一、背景:为什么要在一台机器上跑多个模型?
在 AI 工程化实践中,我们经常遇到这样的场景:
- 不同任务需要不同模型:Embedding、Reranker、对话模型各有分工
- GPU 资源有限:单台 8 卡机器要承载多个服务
- ** latency 敏感**:某些场景需要本地部署,不能走公网 API
vLLM 凭借其 PagedAttention 和连续批处理(continuous batching)成为首选。但在生产环境部署多模型时,踩坑远比想象中多。
二、硬件规划:单台 8 卡服务器能部署多少模型?
2.1 服务器配置参考
| 配置项 | 规格 |
|---|---|
| 机型 | 8 卡服务器(如 31.2 机器) |
| GPU | 8× A100 80G / 4090 24G(根据实际调整) |
| 内存 | 256G+ |
| 硬盘 | 本地 SSD 存放模型权重 |
2.2 模型部署规划
以 8×A100 80G 为例,实际可并行部署:
| 模型 | 用途 | 卡数 | 显存占用 | 端口 |
|---|---|---|---|---|
| DeepSeek-R1-AWQ | 对话/推理 | 8(TP=8) | ~70G | 9981 |
| Qwen2.5-32B | 通用对话 | 2(TP=2) | ~60G | 9984 |
| Qwen2.5-14B | 轻量对话 | 1(TP=1) | ~28G | 9985 |
| Qwen3-Embedding-8B | 向量化 | 1 | ~16G | 9988 |
| Qwen3-Reranker-8B | 重排序 | 1 | ~16G | 9989 |
| BGE-large-zh | 向量化(备用) | 1 | ~16G | 9986 |
关键原则:Embedding 和 Reranker 可共用一张卡(错峰调度),对话模型独占。
三、多模型并发部署实战
3.1 启动命令模板
# === 对话模型 ===
# DeepSeek-R1-AWQ(8卡)
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 nohup python -m vllm.entrypoints.openai.api_server \
--host 0.0.0.0 --port 9981 \
--tensor-parallel-size 8 \
--gpu-memory-utilization 0.95 \
--max-model-len 131076 \
--max-num-batched-tokens 131076 \
--enable-prefix-caching \
--trust-remote-code \
--model /home/llm/DeepSeek-R1-0528-AWQ \
--served-model-name ds671 \
--enable-auto-tool-choice \
--tool-call-parser llama3_json \
> /home/llm/logs/ds671.log 2>&1 &
# Qwen2.5-32B(2卡)
CUDA_VISIBLE_DEVICES=0,1 nohup vllm serve \
/home/llm/qwen2.5-32 \
--host 0.0.0.0 --port 9984 \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.91 \
--max-model-len 30720 \
--enable-prefix-caching \
> /home/llm/logs/qwen32.log 2>&1 &
# Qwen2.5-14B(1卡)
CUDA_VISIBLE_DEVICES=2 nohup vllm serve \
/home/llm/qwen2.5-14 \
--host 0.0.0.0 --port 9985 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.91 \
--max-model-len 30720 \
--max-num-seqs 256 \
--enable-prefix-caching \
> /home/llm/logs/qwen14.log 2>&1 &
3.2 Embedding / Reranker 部署
# BGE-large-zh(向量化)
CUDA_VISIBLE_DEVICES=6 nohup vllm serve \
/home/llm/bge-large-zh-v1.5 \
--host 0.0.0.0 --port 9986 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.9 \
--task embedding \
--served-model-name bge-large \
> /home/llm/logs/bge-large.log 2>&1 &
# Qwen3-Reranker-8B(重排序)
CUDA_VISIBLE_DEVICES=6 nohup vllm serve \
/home/llm/qwen3-reranker-8b \
--host 0.0.0.0 --port 9989 \
--tensor-parallel-size 1 \
--gpu-memory-utilization 0.4 \
--max-model-len 10240 \
--hf-overrides '{"architectures": ["Qwen3ForSequenceClassification"],"classifier_from_token": ["no", "yes"],"is_original_qwen3_reranker": true}' \
--task score \
--served-model-name qwen3-reranker \
> /home/llm/logs/qwen3-reranker.log 2>&1 &
注意:
--task embedding和--task score是 vLLM 0.6+ 新增参数,用于自动选择模型执行路径,无需手动指定--model路径(但实际测试中仍需--model指向本地路径)。
四、踩坑实录(按严重程度排序)
坑1:Qwen3-Reranker 启动后接口报错
现象:容器启动成功,但调用 /v1/score 返回 500,日志报 KeyError: 'score' 或 AttributeError。
根因:Qwen3-Reranker 的配置文件 config.json 中 architectures 字段与 vLLM 内置 registry 不匹配。vLLM 期望 Qwen3ForSequenceClassification,但部分模型权重包缺少该字段。
解决方案:
# 方法A:通过 --hf-overrides 强制指定架构(推荐)
vllm serve ... \
--hf-overrides '{"architectures": ["Qwen3ForSequenceClassification"],"classifier_from_token": ["no", "yes"],"is_original_qwen3_reranker": true}' \
--task score
# 方法B:直接替换 vLLM 源码中的 registry 文件(根治)
# 找到 conda 环境下的 vLLM 模型执行器目录:
ls /root/anaconda3/envs/qwen3reranker/lib/python3.10/site-packages/vllm/model_executor/models/
# 替换 qwen3.py 和 registry.py 为官方最新版本
验证:替换后重启,调用 /v1/score 正常返回分数。
坑2:多模型共用 GPU 时的显存泄漏
现象:连续运行 24 小时后,显存占用逐渐上涨,最终 OOM。
根因:某些模型(特别是开启了 --enable-prefix-caching 的)在 KV cache 管理上存在边界情况,长序列缓存未正确释放。
解决方案:
# 1. 限制最大序列数
--max-num-seqs 128 # 不要设太高
# 2. 启用 prefix-caching 时,定期重启(生产环境可做健康检查)
# 3. 监控脚本:每小时检查显存,超过阈值自动重启
坑3:模型热更新时的进程残留
现象:pkill -f "vllm serve" 后,GPU 显存未释放,新模型启动报 CUDA out of memory。
根因:vLLM 使用 multiprocessing 启动 worker 进程,主进程被杀后子进程可能变成孤儿进程。
解决方案:
# 暴力清理(生产环境慎用,仅维护窗口期)
pkill -9 -f "vllm serve"
# 确认 GPU 释放
nvidia-smi
# 更优雅的方式:通过 API 关闭
curl http://localhost:9981/v1/stop
坑4:AWQ 模型与 BF16 混用导致精度异常
现象:同一模型,不同机器上生成质量差异大。
根因:某些 AWQ 模型在 --dtype bfloat16 下表现不稳定,而 vLLM 默认行为随版本变化。
解决方案:
# 明确指定 dtype
--dtype bfloat16 # A100 推荐
--dtype float16 # 4090 推荐
# 如果模型本身是 AWQ 量化,无需额外指定 dtype,vLLM 会自动识别
五、关键参数调优经验
5.1 显存与吞吐量权衡
| 参数 | 低显存模式 | 高吞吐模式 |
|---|---|---|
--gpu-memory-utilization |
0.85 | 0.95 |
--max-model-len |
8192 | 65536 |
--max-num-seqs |
64 | 256 |
--block-size |
32 | 64 |
--enable-prefix-caching |
False | True |
5.2 多模型并发卡分配策略
# 推荐策略:对话模型独占,Embedding/Reranker 共享末卡
CUDA_VISIBLE_DEVICES=0,1,2,3 # 对话模型A(TP=4)
CUDA_VISIBLE_DEVICES=4,5,6,7 # 对话模型B(TP=4)
CUDA_VISIBLE_DEVICES=6 # Embedding(与模型B错峰)
CUDA_VISIBLE_DEVICES=7 # Reranker
注意:
CUDA_VISIBLE_DEVICES是进程级环境变量,不同终端窗口互不影响。
六、运维建议
6.1 日志管理
# 按天轮转日志
logrotate /home/llm/logs/vllm.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
}
6.2 健康检查脚本
#!/bin/bash
# check_vllm.sh
PORTS=(9981 9984 9985 9986 9989)
for p in ${PORTS[@]}; do
if ! curl -s -o /dev/null -w "%{http_code}" http://localhost:$p/health | grep -q "200"; then
echo "ALERT: vLLM on port $p is down"
# 触发告警或重启
fi
done
6.3 性能监控
# 实时查看 GPU 利用率
watch -n 1 nvidia-smi
# 查看 vLLM 内部统计
curl http://localhost:9981/metrics | grep -E "vllm:|gpu:"
七、总结
vLLM 在单机多模型场景下已经相当成熟,但仍需注意:
- 显存规划要留余量:建议预留 10-15% 显存给 KV cache 突发增长
- Reranker 单独处理:Qwen3-Reranker 需要特殊配置,按官方文档替换 registry 文件最稳妥
- 进程管理要彻底:
pkill可能留下孤儿进程,维护窗口期建议直接kill -9 - prefix-caching 是双刃剑:长对话场景收益明显,但会增加显存压力,需根据实际吞吐量调整
本文基于生产环境真实部署经验整理,如有疑问欢迎交流。
做自动化这几年,最耗时间的从来不是写代码,是摸清每个平台的脾气。
这篇里提到的坑,都是真金白银踩出来的。
如果你手上也有重复度很高的活儿——批量发布、数据搬运、有固定规则的机械操作
——可以在评论区说说你的场景,我看看能不能自动化掉。

浙公网安备 33010602011771号