vLLM 实战:高吞吐大模型推理服务部署

当你把 Qwen、Llama 这类大模型接到线上业务,最先撞上的往往不是准确率,而是吞吐量。原生 HuggingFace transformers 逐请求推理,GPU 显存里大量的 KV Cache 因为碎片化和预留而浪费,并发一高就 OOM 或排队。vLLM 正是为解决大模型推理的高吞吐与高显存利用率而生:它用 PagedAttention 把 KV Cache 像操作系统虚拟内存一样分页管理,让显存利用率逼近理论上限,单卡吞吐常常能翻 2–5 倍。

一、为什么需要 vLLM:大模型推理的吞吐瓶颈

自回归生成是“一个 token 接一个 token”的串行过程。每生成一个新 token,都要把前面所有 token 的 Key/Value 缓存(KV Cache)读一遍。传统推理框架为每个请求预先分配一段连续显存,长度按 max_length 估算,结果大量显存被“预留但没用到”;同时请求之间无法共享相同前缀的缓存。并发一上来,显存迅速见底,要么 OOM,要么请求堆积、延迟飙升。这正是线上大模型服务最常见的性能天花板。

vLLM 由 UC Berkeley 团队开源,核心创新是 PagedAttention。它借鉴操作系统的虚拟内存分页思想,把 KV Cache 切成固定大小的 block 按需分配,使显存利用率从传统方案的 20%–40% 提升到 90% 以上,从而在同硬件下支撑数倍并发。下面从原理到落地完整走一遍。

二、核心原理:PagedAttention 做了什么

PagedAttention 把 KV Cache 拆成若干固定大小的“页(block)”,每个 block 可存放在显存任意位置,通过一张映射表记录逻辑块到物理块的对应关系。好处有两点:

  • 消除碎片:不再需要连续大块显存,按需分配、用完即回收,显存利用率接近 100%。
  • 共享前缀:在并行采样(如 beam search、对同一 prompt 的多路补全)时,相同前缀的 block 可被多个序列安全共享,进一步省显存。

这与操作系统的分页内存、写时复制(Copy-on-Write)思路一脉相承,因此 vLLM 在批量并发、长上下文场景下收益最明显。

三、快速安装与单机启动

vLLM 需要较新的 CUDA 驱动与 NVIDIA 显卡。推荐用官方 PyPI 包安装,再用内置的 OpenAI 兼容服务入口启动:

pip install vllm

# 启动 OpenAI 兼容 API 服务(模型可换成本地路径或 HuggingFace 仓库名)
vllm serve Qwen/Qwen2.5-7B-Instruct --host 0.0.0.0 --port 8000

服务起来后,默认在 http://localhost:8000/v1 提供与 OpenAI 完全一致的接口,老代码几乎零改动即可接入。注意不同 vLLM 版本的启动命令略有差异,旧版用 python -m vllm.entrypoints.openai.api_server,新版统一为 vllm serve。

四、用 OpenAI 兼容 API 调用

因为接口兼容 OpenAI,直接用官方 SDK 把 base_url 指向本地即可:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="EMPTY",   # vLLM 默认不校验 key
)

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)

不写代码时,用 curl 也能快速验证服务是否存活:

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"Qwen/Qwen2.5-7B-Instruct","messages":[{"role":"user","content":"你好"}]}'

五、关键启动参数调优

vLLM 的吞吐很大程度取决于启动参数。下面几个最常用:

参数作用建议
–tensor-parallel-size多卡张量并行切分设为真实可见 GPU 数
–gpu-memory-utilization显存占用上限(默认 0.9)单模型 0.9;多模型共卡可下调
–max-num-seqs最大并发序列数默认 256,按显存与延迟权衡
–max-model-len最大上下文长度按业务裁剪,能显著省显存
–dtype计算精度 auto/half/bfloat16默认 auto
–enable-prefix-caching开启前缀缓存相同 system prompt 场景必开

一个典型的多卡高并发启动命令:

vllm serve Qwen/Qwen2.5-7B-Instruct \
  --tensor-parallel-size 2 \
  --gpu-memory-utilization 0.9 \
  --max-num-seqs 128 \
  --enable-prefix-caching

六、量化部署:在更小显存上跑更大模型

7B 模型用 FP16 约需 14GB 显存,量化能显著压缩。vLLM 原生支持多种量化后端:

量化方式显存占用质量使用方式
AWQ (4bit)约 4–5GB高–quantization awq + 对应量化权重
GPTQ (4bit)约 4–5GB高–quantization gptq
bitsandbytes (NF4)约减半中加载时 quantization=bitsandbytes
FP8接近 FP16高支持的后端用 –dtype fp8
# 需先准备好量化权重(如社区已发布的 -AWQ 版本或 AutoAWQ 自行量化)
vllm serve Qwen/Qwen2.5-7B-Instruct-AWQ --quantization awq

量化是“用少量精度换显存”,对话、摘要类任务通常几乎无感;但数学、代码等强推理场景建议先小流量对比效果再全量。

七、用 Docker 容器化部署

生产环境推荐用官方镜像,避免本机 CUDA 版本错配:

docker run --gpus all -p 8000:8000 \
  vllm/vllm-openai:latest \
  --model Qwen/Qwen2.5-7B-Instruct \
  --host 0.0.0.0 --port 8000

多实例前面再挂一层 Nginx 反向代理 做统一入口、TLS 与限流,可参考本站的 Docker 部署 实践。容器编排也可交给 GitHub Actions 做构建与冒烟。

八、吞吐压测与监控

上线前务必用接近真实的 prompt 分布压测,重点关注四个指标:

指标含义健康参考
吞吐 tokens/s总生成速度越高越好
首 token 延迟 TTFT用户感知的首次响应对话场景尽量 < 1s
每 token 延迟 TPOT后续 token 稳定度越小越流畅
GPU 显存占用是否触及瓶颈建议 < 95%
import concurrent.futures
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")

def ask(i):
    r = client.chat.completions.create(
        model="Qwen/Qwen2.5-7B-Instruct",
        messages=[{"role":"user","content":f"第{i}个问题:用三句话介绍 Kubernetes。"}],
    )
    return len(r.choices[0].message.content)

with concurrent.futures.ThreadPoolExecutor(max_workers=32) as ex:
    print("完成请求数:", len(list(ex.map(ask, range(200)))))

九、vLLM 与 Xinference / LiteLLM 怎么选

三者定位不同,常常组合使用:

工具定位适合场景
vLLM单模型高吞吐 serving追求极致推理性能
Xinference多模型统一纳管一个平台管多种模型
LiteLLM多厂商网关 + 成本控制统一 API 接多家大模型

典型架构是用 LiteLLM 做统一网关,后端接多个 Xinference 或 vLLM 实例,既拿到 vLLM 的高吞吐,又保留多模型路由与预算管控能力。

十、在 CI 中做模型服务冒烟测试

把模型服务纳入 GitHub Actions 流水线,每次发布镜像都做一次冒烟,避免“能启动但答非所问”:

name: llm-smoke
on: [push]
jobs:
  smoke:
    runs-on: ubuntu-latest
    steps:
      - name: Start vLLM
        run: |
          docker run -d --gpus all -p 8000:8000 vllm/vllm-openai:latest \
            --model Qwen/Qwen2.5-0.5B-Instruct --host 0.0.0.0 --port 8000
          sleep 60
      - name: Smoke test
        run: |
          curl -f http://localhost:8000/v1/models
          curl -f http://localhost:8000/v1/chat/completions \
            -H "Content-Type: application/json" \
            -d '{"model":"Qwen/Qwen2.5-0.5B-Instruct","messages":[{"role":"user","content":"ping"}]}'

十一、常见坑与排查

现象原因解决
启动报 CUDA OOM模型或并发过大下调 –gpu-memory-utilization 或启用量化
TP 与实际 GPU 不符卡数错配–tensor-parallel-size 设为真实可见 GPU 数
中文乱码 / 输出截断tokenizer 不匹配用官方同名权重,勿混版本
API 返回 400字段名随版本变动升级 OpenAI SDK 到最新版
并发上不去–max-num-seqs 太小调大并监控显存

十二、落地清单

步骤要点
选型7B/14B 优先量化;72B 需多卡 TP
部署Docker + Nginx 反代 + 健康检查
调优开 prefix caching,设合理 max-num-seqs
压测用真实 prompt 分布测 TTFT / TPOT
上线CI 冒烟 + 监控 + 限流

vLLM 把“大模型推理服务”从调包变成了一门可量化、可调优的工程:分页显存管理带来数倍吞吐,OpenAI 兼容接口几乎零迁移成本,配合 LiteLLM 网关与 Xinference 纳管,就能搭出一套稳定、省钱、可观测的推理底座。先从一个 7B 量化模型跑通冒烟,再按上面的清单逐步加压,是最稳的落地路径。

十三、生产环境还要注意什么

除了吞吐与显存,还有几处会直接决定线上体验。其一,连续批处理(continuous batching)是 vLLM 高吞吐的另一根支柱:它不像静态 batch 那样等齐一批再算,而是每个迭代动态把新到的请求塞进空闲槽位,GPU 利用率因此显著高于传统方案。调度时应尽量让请求自然汇聚成批,避免人为拆成过小的请求。

其二,长上下文场景要权衡 --max-model-len 与显存:上下文越长,KV Cache 占用越大,必要时配合 chunked prefill 把长 prompt 分块处理,降低首包显存峰值。其三,多模型共用一张卡时,用 --gpu-memory-utilization 给彼此留出余量,否则会相互抢显存、集体降级。

其四,监控不能只盯吞吐和延迟,还要看 KV Cache 命中率与调度队列长度——它们往往是是否需要扩容的最早信号。把这四项接进现有监控体系,再配合前面提到的 GitHub Actions 冒烟,整套推理服务才算真正可运维。

上一篇 WebSocket 实时通信实战:用 Socket.IO 落地
下一篇 OpenWebUI 实战:本地部署私有大模型对话平台
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]