外观
vLLM 大模型推理服务:OpenAI 兼容 API 与启动参数调优
一句话定义:vLLM 是一个基于 PagedAttention 与连续批处理(continuous batching)的高吞吐大模型推理引擎,一条命令就能把开源 LLM 部署成 OpenAI 兼容的 HTTP 服务。
为什么值得动手:大模型推理和普通模型推理在工程上完全是两回事——KV Cache 的显存管理决定了你能不能扛住并发。朴素方案(比如把 Llama 装进 FastAPI 挨个推理)的吞吐低一个数量级,因为每个请求独占一块显存、串行执行。vLLM 用 PagedAttention 把 KV Cache 按页管理(见PagedAttention 论文精读),配合连续批处理,单卡就能把吞吐从个位数 QPS 提到几百 token/s。本文以 Qwen2.5-7B-Instruct 为例,走一遍完整部署、调参、压测流程。
一、vLLM 是什么,为什么快
三个核心技术:
- PagedAttention:把 KV Cache 切成固定大小的"页",按需分配,消除显存碎片与预分配浪费——显存利用率接近 100% 而非传统实现的 60%~80%;
- 连续批处理:传统批处理等一个 batch 全部生成完才释放资源;vLLM 逐 token 调度,某个序列生成完立即让位给新请求,GPU 永不空转;
- OpenAI 兼容 API:直接兼容
openaiPython SDK,业务侧零改造接入。
一句话:vLLM 是当前单卡/多卡部署开源 LLM 的事实标准。底层的 KV Cache 与显存机制详见大模型推理优化。
二、安装与模型准备
bash
# 安装(Python 3.9+)
pip install vllm # 或按官方文档装对应 CUDA 版本
# 检查 GPU
nvidia-smi # 确认显存与驱动,7B FP16 权重约 14GB + KV Cache
# 模型直接从 Hugging Face 拉取,首次会自动下载到 ~/.cache/huggingface
# 也可以先手动下载再指向本地路径
huggingface-cli download Qwen/Qwen2.5-7B-Instruct --local-dir ./Qwen2.5-7B-Instruct硬件底线先交代:Qwen2.5-7B-Instruct FP16 权重约 14.6GB,单张 24GB 显存(如 A10/4090)是起步配置;要用 32B 以上模型或更大上下文,需要多卡或量化(见第六、七节)。
三、启动 OpenAI 兼容服务
bash
vllm serve Qwen/Qwen2.5-7B-Instruct \
--gpu-memory-utilization 0.9 \
--max-model-len 8192 \
--tensor-parallel-size 1 \
--dtype float16 \
--served-model-name qwen2.5-7b \
--port 8000启动成功后默认在 http://localhost:8000/v1 暴露 OpenAI 兼容接口:/v1/chat/completions(对话)、/v1/completions(补全)、/v1/models(模型列表)。
显存估算公式
单卡可用 KV Cache 显存 ≈ GPU 总显存 × gpu-memory-utilization − 权重显存 − 激活开销。24GB 卡跑 7B FP16:24×0.9 − 14.6 ≈ 7GB KV Cache,约合 8192 上下文的并发序列 30~50 条(视实际 token 长度而定)。
四、关键启动参数详解
| 参数 | 默认值 | 作用 | 调优建议 |
|---|---|---|---|
--gpu-memory-utilization | 0.9 | 预留给 vLLM 的显存比例 | 0.85~0.95;显存紧张时降到 0.8 保 KV Cache |
--max-model-len | 模型默认(如 8192) | 单序列最大上下文长度 | 过长浪费显存;业务用不到 32K 就设 8K |
--tensor-parallel-size | 1 | 张量并行卡数 | 模型放不下时递增:2/4/8 |
--dtype | auto | 权重精度 | float16 或 bfloat16,别用 float32(显存翻倍) |
--quantization | None | 量化后端(awq/gptq/…) | 见第七节量化加载 |
--enforce-eager | False | 跳过 CUDA Graph 编译 | 启动慢/偶发编译报错时开,代价是吞吐略降 |
--enable-prefix-caching | 自动 | 跨请求复用公共前缀的 KV Cache | 对话/Agent 场景强烈建议显式开启 |
--served-model-name | 模型 ID | 对外暴露的模型名 | 换别名,业务不用改 |
--max-num-seqs | 256 | 并发序列上限 | 显存不足时调低,避免排队过多 |
--max-num-batched-tokens | 8192 | 单步最多处理的 token 数 | 小显存调小,防 OOM |
--trust-remote-code | False | 允许加载自定义模型代码 | 部分社区模型需要 |
顺序是"先保显存,再保吞吐"
--max-model-len、--max-num-seqs、--max-num-batched-tokens 三个参数共同决定显存预算。先看 --gpu-memory-utilization 分出的 KV Cache 够不够支撑目标并发,再逐项收紧,而不是一上来就拉满。
一个服务部署多个模型
vllm serve 支持一次挂多个模型(在同一个显存池里共享 KV Cache),适合"轻量模型+重模型混部":
bash
vllm serve \
Qwen/Qwen2.5-7B-Instruct \
Qwen/Qwen2.5-0.5B-Instruct \
--served-model-name qwen-7b \
--served-model-name qwen-0.5b \
--gpu-memory-utilization 0.9 \
--max-model-len 8192客户端通过 model="qwen-7b" 或 model="qwen-0.5b" 选择模型。同一实例多模型会互相抢占 KV Cache 显存,混部前先用下面的显存公式估算:7B + 0.5B 权重约 15.3GB,24GB 卡上留给两个模型共用的 KV Cache 只剩约 6GB,并发被摊薄。先给结论:混部适合"一个服务多条链路",不适合"两个都要高并发"——后者请拆成两个服务实例或加卡。
与服务编排:容器与 GPU 资源
生产环境通常不直接裸跑 vllm serve,而是放进容器由 K8s 托管:
bash
docker run --gpus all --shm-size 8g \
-v ~/.cache/huggingface:/root/.cache/huggingface \
-p 8000:8000 \
vllm/vllm-openai:latest \
--model Qwen/Qwen2.5-7B-Instruct \
--gpu-memory-utilization 0.9 \
--max-model-len 8192注意 --shm-size:vLLM 依赖共享内存做 tokenizer 与数据传递,默认 64MB 会报 /dev/shm 不足——这是容器部署最常见的启动失败原因之一。K8s 里给容器申请 nvidia.com/gpu: 1,并配 liveness/readiness 探针打 /health 端点(vLLM 自带)。
五、客户端调用:OpenAI SDK 与流式输出
python
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1", # vLLM 的 OpenAI 兼容端点
api_key="EMPTY", # vLLM 默认不校验 key
)
# 普通对话
resp = client.chat.completions.create(
model="qwen2.5-7b",
messages=[{"role": "user", "content": "用一句话解释什么是 KV Cache"}],
max_tokens=256,
temperature=0.7,
)
print(resp.choices[0].message.content)
# 流式输出:长回答场景务必用 stream,首 token 延迟显著更低
stream = client.chat.completions.create(
model="qwen2.5-7b",
messages=[{"role": "user", "content": "写一首关于模型部署的诗"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)为什么 LLM 不用朴素 FastAPI 套
把 LLM 装进 FastAPI 挨个推理:一个请求占满整卡显存跑完整生成,其余请求排队,GPU 利用率 20% 都不到;流式输出还得自己搓 SSE。vLLM 已经内置了连续批处理、流式、限流、指标,业务层直接用 OpenAI SDK 对接。结论:LLM 场景不要自研朴素服务,直接上推理引擎。 完整对比见大模型推理优化。
六、多卡张量并行
单卡放不下就用 --tensor-parallel-size 切分:
bash
# 用 2 张 24GB 卡跑 70B 量化模型,或用 4 张跑更大模型
vllm serve Qwen/Qwen2.5-72B-Instruct-AWQ \
--tensor-parallel-size 4 \
--gpu-memory-utilization 0.9 \
--max-model-len 8192三个要点:
- 卡间通信需要 NVLink/PCIe 直连,异构 GPU 混用会拖累整体(按最慢卡同步);
tensor-parallel-size必须整除注意力头数/层数的可切分维度,一般取 2/4/8;- 张量并行收益有边际:2 卡吞吐约 1.7~1.9 倍,4 卡约 3~3.5 倍,不是线性。追求性价比的另一种路是单卡量化(下一节)。
七、量化模型加载
量化是把 7B 模型塞进更小显存、换更高吞吐的关键,原理见量化。AWQ/GPTQ 是主流选择:
bash
# AWQ 量化版:24GB 卡上 72B 也能跑
vllm serve Qwen/Qwen2.5-72B-Instruct-AWQ \
--quantization awq \
--gpu-memory-utilization 0.9 \
--max-model-len 8192
# GPTQ 量化版
vllm serve TheBloke/Llama-2-7B-Chat-GPTQ \
--quantization gptq \
--max-model-len 4096量化收益与代价的实测参考(7B 模型,单卡 A10,批处理吞吐):
| 精度 | 权重显存 | 相对吞吐 | 质量影响 |
|---|---|---|---|
| FP16 | ~14.6GB | 1.0×(基准) | 无 |
| INT8 | ~7.4GB | 约 1.2× | 极小 |
| INT4 (AWQ/GPTQ) | ~4.1GB | 约 1.5~1.8× | 多数任务可接受,极端推理题略降 |
先给结论:显存是瓶颈就量化,不是就保持 FP16。INT4 模型质量损耗通常可接受,但涉及精确数学/推理类任务要做离线评测再定,别只看跑分。
八、压测:benchmark 工具与指标解读
vLLM 自带压测脚本,也可以直接用压测与容量规划里的方法:
bash
# 模拟 200 个并发请求,每个要求生成 256 token
python benchmarks/benchmark_serving.py \
--model Qwen/Qwen2.5-7B-Instruct \
--tokenizer Qwen/Qwen2.5-7B-Instruct \
--request-rate 200 \
--num-prompts 1000 \
--max-tokens 256 \
--save-result results.json输出解读三个核心指标:
- TTFT(Time To First Token):首 token 延迟。流式场景用户感知 = TTFT,目标 < 500ms~1s;
- TPOT(Time Per Output Token):每个输出 token 的时间,等于 1/解码速度,越小越流畅;
- 吞吐(Total token throughput):单位时间生成的 token 总数,同时区分输入与输出吞吐。
同配置下与朴素 Hugging Face transformers 部署的对照最能说明 vLLM 的价值(7B 模型、单卡 A10、并发 64、输出 256 token):
| 部署方式 | 总吞吐 (token/s) | 每请求生成速度 (token/s) | 显存 |
|---|---|---|---|
transformers generate 串行 | 约 300~600 | 约 15~30(单请求) | 权重+单请求 KV |
| vLLM(连续批处理) | 约 3000~5000 | 约 17~25/请求 | PagedAttention 按页分配 |
先给结论:vLLM 的吞吐来自"多个请求共享一次解码步",单请求速度并不比朴素部署快,快的是总量——所以它特别适合对话、Agent 这类多并发场景;单路超低延迟场景(单并发、每 token 15ms 内)该考虑更专门的优化(CUDA Graph、小模型)。
一组 7B/单卡参考数字(A10 24GB,并发 200):TTFT 约 300~800ms,TPOT 约 40~60ms/token(约 17~25 token/s/请求),总吞吐约 3000~5000 token/s。提高吞吐的三板斧:加大 --gpu-memory-utilization 留出更多 KV Cache、开 --enable-prefix-caching、量化。
常见问题与排查
| 问题 | 现象 | 排查 |
|---|---|---|
| 启动即 OOM | CUDA out of memory / No available memory for the cache blocks | 降 --gpu-memory-utilization、--max-model-len、--max-num-seqs |
| KV Cache 不足 | 日志 CacheConfig: gpu_memory_utilization... 报可用块不足 | 给 KV Cache 更多显存;降并发上限 |
| 请求超长 | Input ... is longer than max-model-len | 调大 --max-model-len(注意显存)或提示客户端截断 |
| 并发上限 | 请求排队、TTFT 飙升 | 看 --max-num-seqs,调大并发或用更大 KV Cache |
| 编译报错/启动极慢 | 卡在 CUDA Graph 编译 | 加 --enforce-eager 跳过,吞吐略降但稳定 |
| 模型返回 404 | model not found | --served-model-name 与实际调用名不一致 |
| 前缀命中率低 | 相同 system prompt 每次仍全量计算 | 显式 --enable-prefix-caching,且前缀要做成公共部分 |
更多生产环境坑位见常见陷阱与反模式。
延伸阅读
- PagedAttention:vLLM 系统论文 —— 本文所有调参背后的显存管理原理
- 大模型推理优化 —— KV Cache、连续批处理、并行策略的系统讲解
- 量化 —— AWQ/GPTQ/INT8 的原理与质量-性能权衡
- 性能优化与容量规划 —— TTFT/TPOT/吞吐的指标框架与容量估算
- 压测与容量规划 —— 把 benchmark 结果换算成容量与扩容决策
- 常见陷阱与反模式 —— LLM 服务在生产环境的高频坑
- FastAPI + Docker 在线服务 —— 对照:普通模型的朴素部署 vs LLM 的引擎部署
参考资料
- vLLM 官方文档:https://docs.vllm.ai/
- vLLM 论文(PagedAttention):https://arxiv.org/abs/2309.06180
- Qwen2.5-7B-Instruct 模型页:https://huggingface.co/Qwen/Qwen2.5-7B-Instruct
- OpenAI Python SDK:https://github.com/openai/openai-python