用 vLLM 搭建高吞吐大模型推理服务(OpenAI 兼容 API 实战)
导语
不知道你有没有遇到过这种情况:手里有一张 24G 显存的显卡,跑一个 7B 的对话模型,压测下来 QPS 只有个位数,nvidia-smi 一看 GPU 利用率常年徘徊在个位数。钱花在硬件上,算力却白白浪费——这类高吞吐推理难题,vLLM 正是为把显存和 GPU 同时吃满而设计的。
问题往往不在模型,而在推理框架。自回归生成时,传统做法会给每个请求预留一整段最大长度的键值缓存,结果大量显存被碎片吃掉,batch size 稍大就 OOM,GPU 大部分时间在"等数据"。
本文带你把这件事做对:先讲清它为什么快(PagedAttention 与连续批处理),再给出可一键复制的部署命令,以及 OpenAI SDK 零改造迁移代码,最后落到量化、多 LoRA 与生产落地清单。照着做完,你的 GPU 利用率可以稳定拉满。
声明:本文基于个人使用体验,非商业推广。
摘要:本文以"原理 → 部署 → 调用 → 进阶 → 生产"为主线,讲解如何用 vLLM 搭建高吞吐、显存高效的大模型推理服务,并暴露 OpenAI 兼容 API,使现有业务代码零改造迁移。包含 4 段可运行代码与 6 张原理配图,覆盖 PagedAttention、continuous batching、量化部署与生产排查清单。
文章目录

一、为什么需要 vLLM:高吞吐推理的痛点与核心原理
我们先看传统推理框架的显存浪费从哪来。
在大语言模型的自回归生成过程中,每生成一个新 token,都要用到前面所有 token 的 Key/Value 向量,这部分被缓存下来、反复复用,就叫 KV cache(键值缓存)。它是推理阶段显存占用的大头,长度随上下文线性增长。
传统框架的做法很"粗暴":为每个请求按最大序列长度(max_length)一次性划好一块连续显存。问题是绝大多数请求根本用不到那么长——短提问只占了一小块,剩下一大半成了内部碎片;不同请求长度不一,碎片之间又无法互相借用,显存利用率常常只有 20% 左右。
PagedAttention 借用了操作系统分页管理的思想:它把 KV cache 切成固定大小的 block(页),像虚拟内存一样按需分配、动态映射。一个请求的 KV cache 不需要在物理显存上连续,缺多少补多少页,还能在不同请求间共享同一份前缀页。结果是显存利用率从约 20% 提升到 90% 以上。
continuous batching(连续批处理) 解决的是"等齐批"的浪费。老式 batch 要等一个 batch 里所有请求都生成完才能进下一批,短请求会被长请求拖住。连续批处理改为在 token 维度动态拼批:哪个请求产出了新 token,就立刻把空出的 slot 让给新请求,GPU 几乎始终有活干。
把 PagedAttention 与 continuous batching 合在一起看:前者让同样大小的显存能塞进更多请求,后者让 GPU 在每个时刻都有新 token 可算,二者叠加才能把显存和算力同时吃满。再加上 prefix caching(前缀缓存),当多个请求共享同样的系统提示或上下文前缀时,KV cache 可以直接复用,不必重复计算,对客服、Agent 等多轮对话场景尤其有效。
| 维度 | 传统自回归框架 | 该推理引擎 |
|---|---|---|
| 显存利用率 | 约 20% | 90%+ |
| 批处理方式 | 请求级等齐批 | token 级连续批 |
| 典型 QPS(7B/单卡) | 个位数 | 数十到上百 |
| KV cache 管理 | 连续预分配 | 分页按需 |
除此之外,该引擎还提供 prefix caching(复用相同系统提示的缓存)、多种量化后端,以及对 NVIDIA / AMD / 华为昇腾等多硬件的支持,对外则统一暴露一套 OpenAI 兼容接口。原理清楚后,部署其实很简单。

二、环境准备与一键部署 vLLM 推理服务
部署前确认三件事:一张支持 CUDA 的 GPU、Python 3.10 及以上、已安装对应版本的 CUDA 驱动。
安装建议用 uv(一个极快的 Python 包管理器),也可以直接 pip。如果你不想处理本地 CUDA 依赖,官方还提供了预装环境的 Docker 镜像,一条命令就能拉起。
# 安装(Python 3.10+,建议用 uv)
uv pip install vllm
# 若用 pip
# pip install vllm
# 或使用 Docker(已含 CUDA / PyTorch 依赖)
# docker run --runtime nvidia --gpus all -p 8000:8000 vllm/vllm-openai:latest \
# --model Qwen/Qwen2.5-7B-Instruct
启动只需要一条命令。下面以 Qwen2.5-7B-Instruct 为例,暴露默认的 OpenAI 兼容 /v1 端点:
# 启动 OpenAI 兼容 API 服务(以 Qwen2.5-7B-Instruct 为例)
vllm serve Qwen/Qwen2.5-7B-Instruct \
--host 0.0.0.0 \
--port 8000 \
--gpu-memory-utilization 0.90 \
--max-model-len 8192 \
--dtype auto \
--enable-prefix-caching \
--api-key EMPTY
几个常用参数说明:--gpu-memory-utilization 0.90 表示最多占用 90% 显存(留余量防 OOM);--max-model-len 是模型支持的大上下文长度;--enable-prefix-caching 对多轮对话里相同的系统提示有明显加速;--api-key EMPTY 是关闭鉴权(生产环境务必换成真实 key 或用反向代理鉴权)。
服务起来后用一条 curl 验证:
# 验证服务已起来
curl http://localhost:8000/v1/models
返回里能看到模型名即代表成功。也可以直接浏览器打开 http://localhost:8000/docs 查看自动生成的接口文档。
遇到 “chat template not found” 类报错时,通常是模型自带的对话模板没有自动识别。你可以在启动时显式指定 --chat-template 参数,指向本地一个 jinja 模板文件;大多数 Instruct 模型在 HuggingFace 仓库里会自带 tokenizer_config.json 中的模板,升级模型文件或框架版本通常也能解决。
| 参数 | 作用 | 建议 |
|---|---|---|
| –host/–port | 监听地址与端口 | 0.0.0.0:8000 |
| –gpu-memory-utilization | 显存占用上限 | 0.85~0.92 |
| –max-model-len | 大上下文长度 | 按显存与需求 |
| –dtype | 计算精度 | auto |
| –enable-prefix-caching | 前缀缓存 | 多轮对话开启 |

三、用 OpenAI SDK 调用 vLLM 服务(无缝迁移)
如果你的业务原本基于 OpenAI Python SDK 开发,迁移到自托管模型几乎零成本。唯一要改的只有一行:把 base_url 指向本地服务地址。
from openai import OpenAI
# 唯一改动:base_url 指向本地推理服务,api_key 随意填
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="EMPTY",
)
# 非流式
resp = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": "用一句话解释 PagedAttention。"}],
temperature=0.7,
)
print(resp.choices[0].message.content)
# 流式(降低首字延迟,适合对话)
stream = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": "写一个快速排序。"}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
注意 model 字段要填你启动时的模型名(HuggingFace 仓库名或本地路径)。非流式一次返回完整结果,适合后台任务;把 stream=True 打开后,token 逐个到达,首字延迟(TTFT)显著下降,对话体验更顺。你还可以通过 max_tokens 限制单条返回长度,避免长输出拖慢整体 batch。
如果你不想引入 SDK,也可以直接用 curl 对照:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [{"role": "user", "content": "你好"}],
"stream": false
}'
并发方面,服务端会自动用 continuous batching 把同时到达的请求合并进同一个 batch,所以客户端直接并发发请求即可,不需要自己攒 batch。

四、进阶实战:批处理、量化与多模型/LoRA
当单条请求跑通后,真正的收益在批处理与压缩。
并发批处理:下面这段客户端代码并发提交 20 条 prompt,服务端的连续批处理会把它们拼成一批高效计算:
import concurrent.futures
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
prompts = [f"把第 {i} 句话翻译成英文:今天天气真好" for i in range(20)]
def ask(p):
r = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": p}],
)
return r.choices[0].message.content
# 并发提交,服务端用 continuous batching 自动合并
with concurrent.futures.ThreadPoolExecutor(max_workers=20) as ex:
results = list(ex.map(ask, prompts))
print(f"完成 {len(results)} 条请求")
观察服务日志或 /metrics 里的 QPS 与显存曲线,你会看到 GPU 利用率明显比单条请求更高。
量化部署:当显存吃紧或想进一步提吞吐,可以用量化权重启动。以 AWQ 为例:
# 假设已准备好 AWQ 量化权重(如 Qwen/Qwen2.5-7B-Instruct-AWQ)
vllm serve Qwen/Qwen2.5-7B-Instruct-AWQ \
--host 0.0.0.0 --port 8000 \
--quantization awq \
--gpu-memory-utilization 0.90 \
--max-model-len 8192 \
--dtype auto \
--enable-prefix-caching
量化方式各有适用场景,对比如下:
| 量化方式 | 显存节省 | 适用条件 |
|---|---|---|
| AWQ | 高 | 需提前量化好的权重 |
| GPTQ | 高 | 需提前量化好的权重 |
| FP8 | 中 | 需 Hopper 及以上架构(如 H100) |
多 LoRA:通过 --enable-lora 加运行时 /v1/load_lora_adapter 可在同一服务里切换多个微调适配器。但官方文档注明该能力主要面向本地开发;生产上更稳妥的是为每个模型或适配器起独立实例,或用多模型部署。
prefix caching 已在前面开启,对"系统提示固定 + 多轮对话"的场景收益明显,建议常开。

五、生产落地 Checklist 与常见问题排查
要上线到生产,建议逐项核对下面清单:
- 显存估算:用
--gpu-memory-utilization和--max-model-len做压测,确认峰值不 OOM。 - 健康检查:请求
GET /health做探针,接入 K8s liveness/readiness。 - 鉴权与限流:用反向代理(如 Nginx)加 API key 与速率限制,别把裸服务暴露公网。
- 监控:盯住 QPS、TTFT(首字延迟)、TPOT(每 token 延迟)与显存占用。该引擎默认在
/metrics暴露 Prometheus 指标,可以直接接入 Grafana 或自建的监控面板。 - 日志:打开
--log-level INFO观察 batch 合并情况;遇到 OOM 时优先看显存峰值与当前 batch 长度。 - 多卡扩展:单机多卡用
--tensor-parallel-size;横向扩用 Ray Serve 或 V1 引擎的多副本。
常见报错对照:
| 现象 | 可能原因 | 处理 |
|---|---|---|
| CUDA out of memory | 显存不足 | 降低 utilization / 减小 max-model-len / 用量化 |
| 端口被占用 | 8000 已被占 | 换 --port |
| chat template 不匹配 | 模板缺失 | 加 --chat-template 指定 |
| 模型下载慢 | 网络/源 | 配镜像源或预下载到本地 |
| 请求卡住无返回 | batch 内出现极长输出 | 限制 max_tokens / 开启 timeout / 拆分请求 |
收尾给一个"下一步":当你把 PagedAttention、连续批处理、量化三块串起来,一个 7B 模型在单卡上跑出数十倍吞吐并不罕见。建议从量化 + prefix caching 两个低风险的优化入手,再逐步压测上线。如果业务量继续增长,优先横向扩展多个无状态实例,而不是无限调大单卡的 batch size,这样故障域更小、回滚也更快。
相关阅读:如果你在显存与性能之间权衡,可延伸阅读 大模型量化部署从入门到实战(站内文章,互为补充)。

总结
本文从传统推理框架的显存浪费讲起,拆解了 PagedAttention 与 continuous batching 为何能拉满 GPU;随后给出 vllm serve 一键部署、OpenAI SDK 零改造调用、并发批处理与量化部署四段可运行代码;后用生产 checklist 与排查表收尾。核心结论:高吞吐的关键不在堆硬件,而在让显存和 GPU 时刻"有活干"。
参考资料
- 官网:https://vllm.ai(官方文档见站点内 docs 栏目)
© 2026 | 转载请注明出处
更多推荐

所有评论(0)