【vLLM 工程实践】大模型高效部署全流程
文章目录
- vLLM 工程实践:大模型高效部署全流程
- 一、引言
- 二、核心机制:vLLM 为什么快
- 2.1 PagedAttention:像操作系统管理内存一样管理 KV Cache
- 2.2 连续批处理(Continuous Batching)
- 2.3 其他关键能力一览
- 三、环境搭建
- 3.1 硬件与驱动前置条件
- 3.2 用 pip 安装(最简路径)
- 3.3 用 Docker 部署(生产推荐)
- 四、快速上手:离线推理与在线服务
- 4.1 离线批量推理(Offline Inference)
- 4.2 启动 OpenAI 兼容 API 服务
- 五、分布式部署:单机多卡与多机多卡
- 5.1 张量并行(Tensor Parallelism):单机多卡
- 5.2 流水线并行(Pipeline Parallelism):跨机扩展
- 5.3 分布式相关核心参数
- 六、量化与性能调优
- 6.1 量化方案对比
- 6.2 关键调优参数
- 6.3 投机解码:进一步压缩解码延迟
- 七、生产部署实践
- 7.1 容器化 + 环境变量管理
- 7.2 监控:Prometheus + Grafana
- 7.3 多 LoRA 服务:一个基座模型服务多个业务场景
- 7.4 Kubernetes 部署要点
- 八、横向对比:vLLM 与主流推理框架
- 九、常见问题速查表
- 十、总结
vLLM 工程实践:大模型高效部署全流程
一、引言
亲爱的朋友们,创作不容易,若对您有帮助的话,请点赞收藏加关注哦,您的关注是我持续创作的动力,谢谢大家!有问题请私信或联系邮箱:jasonai.fn@gmail.com
同样一张 A100,同样的 70B 模型,有的团队跑出每秒几十个 token 的可用吞吐,有的团队却发现 GPU 显存被 KV Cache 撑爆、并发一高就 OOM、单个长请求能把整个服务卡死。差别往往不在模型,而在推理引擎——原生 HuggingFacetransformers的generate()是为研究场景设计的,从来没考虑过"数百个用户同时排队请求"这种生产负载。
vLLM 的出现,解决的正是这个问题。它由 UC Berkeley Sky Computing Lab 团队在 2023 年提出,核心论文《Efficient Memory Management for Large Language Model Serving with PagedAttention》把操作系统虚拟内存分页的思想搬进了 KV Cache 管理,配合连续批处理(Continuous Batching)调度,把显存利用率和吞吐量都推到了新的量级。
本文不重复讲 PagedAttention 论文里的公式推导,而是聚焦工程落地:从环境安装到离线推理,从单机多卡到多机分布式,从量化压缩到生产环境的监控与 LoRA 多租户服务,给出一套可以直接照做的部署流程。
二、核心机制:vLLM 为什么快
在动手部署之前,先搞清楚 vLLM 快在哪里——这决定了后面每一个参数该怎么调。
2.1 PagedAttention:像操作系统管理内存一样管理 KV Cache
传统推理引擎给每个请求的 KV Cache 分配一段连续显存,且必须按最大可能长度预留。一个请求实际只用了 200 个 token,却按max_model_len=4096预留显存,中间全是浪费——这种浪费在传统方案里能达到 60%~80%。
PagedAttention 把 KV Cache 切分成固定大小的block(默认 16 个 token 一块),像操作系统的分页内存一样按需分配、非连续存储,用一张"页表"记录每个请求的 block 映射关系。显存碎片浪费被压到 4% 以内,同样的显存能塞进多得多的并发请求。
传统方式:每个请求预留 max_length 显存(大量浪费) ┌────────────────────────────────────┐ │ 请求A实际使用 ███░░░░░░░░░░░░░░░░░░░ │ ← 浪费 │ 请求B实际使用 █████░░░░░░░░░░░░░░░░ │ ← 浪费 └────────────────────────────────────┘ PagedAttention:按需分配非连续 block,用完即还 ┌───┬───┬───┬───┬───┬───┬───┬───┬───┐ │A-1│A-2│B-1│C-1│A-3│B-2│C-2│ 空闲块池 │ └───┴───┴───┴───┴───┴───┴───┴───┴───┘2.2 连续批处理(Continuous Batching)
静态批处理(static batching)要等一批请求全部生成完才能换下一批,长短请求混在一起时,短请求生成完了也要陪着长请求"陪跑",GPU 利用率忽高忽低。
vLLM 采用迭代级调度:每完成一次前向计算就检查一次批次,结束的请求立刻让位,新请求随时插入,GPU 几乎不存在空闲等待。这是 vLLM 相对原生transformers.generate()吞吐提升可达一个数量级的关键原因之一(官方论文给出的对比场景中,吞吐提升可达 24 倍,具体倍数因模型和负载而异)。
2.3 其他关键能力一览
| 能力 | 作用 |
|---|---|
| 前缀缓存(Prefix Caching) | 相同系统提示词/上下文的请求复用已计算的 KV Cache,多轮对话场景收益明显 |
| 分块预填充(Chunked Prefill) | 把长 prompt 的预填充拆成小块,与解码阶段交替执行,避免长请求"卡住"其他请求 |
| 投机解码(Speculative Decoding) | 用小模型或 n-gram 提前"猜"多个 token,大模型一次验证,减少解码轮数 |
| 量化推理 | 支持 AWQ、GPTQ、FP8、INT4/INT8 等,用精度换显存和速度 |
| 多 LoRA 服务 | 同一基座模型加载多个 LoRA 适配器,按请求动态切换,单实例服务多租户 |
三、环境搭建
3.1 硬件与驱动前置条件
| 项目 | 要求 |
|---|---|
| GPU | NVIDIA 计算能力 ≥ 7.0(V100 及以上),AMD ROCm、Intel XPU、CPU 也有对应支持 |
| CUDA | 建议 12.1 及以上,与 vLLM 发行版对应的 PyTorch CUDA 版本一致 |
| 显存 | 经验值:模型参数量(B)× 2(FP16/BF16 字节数)+ KV Cache 预留,例如 32B 模型 FP16 权重约占 64GB,还需预留 KV Cache 空间 |
| Python | 3.9–3.12(以官方发行版说明为准) |
3.2 用 pip 安装(最简路径)
# 建议使用独立虚拟环境,避免依赖冲突python-mvenv vllm-envsourcevllm-env/bin/activate# 安装 vLLM(会自动匹配对应版本的 PyTorch + CUDA)pipinstallvllm# 验证安装python-c"import vllm; print(vllm.__version__)"3.3 用 Docker 部署(生产推荐)
官方维护了开箱即用的镜像,避免本地 CUDA 版本与依赖冲突:
dockerrun--runtimenvidia--gpusall\-v~/.cache/huggingface:/root/.cache/huggingface\--env"HUGGING_FACE_HUB_TOKEN=<你的HF Token>"\-p8000:8000\--ipc=host\vllm/vllm-openai:latest\--modelQwen/Qwen2.5-7B-Instruct关键参数说明:--ipc=host避免多进程共享内存不足导致的 crash;-v挂载 HuggingFace 缓存目录,避免容器重启后重新下载模型;--gpus all暴露全部 GPU,多卡场景可用--gpus '"device=0,1"'精确指定。
四、快速上手:离线推理与在线服务
4.1 离线批量推理(Offline Inference)
适合跑评测集、批量生成等无需常驻服务的场景:
fromvllmimportLLM,SamplingParams# 初始化引擎,加载模型llm=LLM(model="Qwen/Qwen2.5-7B-Instruct",dtype="bfloat16")sampling_params=SamplingParams(temperature=0.7,top_p=0.9,max_tokens=512)prompts=["用一句话解释什么是量子纠缠。","写一个 Python 快速排序函数。",]outputs=llm.generate(prompts,sampling_params)foroutputinoutputs:print(output.outputs[0].text)vLLM 会自动把这批 prompt 组织成批次并行计算,不需要手动写 batching 逻辑。
4.2 启动 OpenAI 兼容 API 服务
生产场景更常见的是常驻服务,vLLM 内置了与 OpenAI API 完全兼容的接口,现有基于openaiSDK 的代码几乎不用改:
vllm serve Qwen/Qwen2.5-7B-Instruct\--host0.0.0.0\--port8000\--dtypebfloat16\--max-model-len8192\--gpu-memory-utilization0.9客户端调用方式:
fromopenaiimportOpenAI client=OpenAI(base_url="http://localhost:8000/v1",api_key="not-needed")response=client.chat.completions.create(model="Qwen/Qwen2.5-7B-Instruct",messages=[{"role":"user","content":"介绍一下 vLLM 的核心优势"}],temperature=0.7,)print(response.choices[0].message.content)也可以直接用 curl 验证服务是否正常:
curlhttp://localhost:8000/v1/chat/completions\-H"Content-Type: application/json"\-d'{ "model": "Qwen/Qwen2.5-7B-Instruct", "messages": [{"role": "user", "content": "你好"}] }'五、分布式部署:单机多卡与多机多卡
5.1 张量并行(Tensor Parallelism):单机多卡
当单卡显存放不下模型权重时,用张量并行把每一层的矩阵运算切分到多张卡上并行计算,--tensor-parallel-size需等于参与计算的 GPU 数量:
vllm serve Qwen/Qwen2.5-72B-Instruct\--tensor-parallel-size4\--gpu-memory-utilization0.9\--max-model-len163845.2 流水线并行(Pipeline Parallelism):跨机扩展
当单机 GPU 数量不够,需要跨节点扩展时,流水线并行把模型按层切段分布到不同节点,配合张量并行组合使用:
# 假设 2 节点 × 每节点 4 卡 = 8 卡vllm serve Qwen/Qwen2.5-72B-Instruct\--tensor-parallel-size4\--pipeline-parallel-size2多节点部署依赖Ray做跨机进程管理,需要先手动启动 Ray 集群:
# 主节点ray start--head--port=6379# 其他节点加入集群ray start--address='<主节点IP>:6379'# 确认集群节点数ray status5.3 分布式相关核心参数
| 参数 | 说明 |
|---|---|
--tensor-parallel-size | 单节点内张量并行的 GPU 数 |
--pipeline-parallel-size | 跨节点流水线并行的阶段数 |
--distributed-executor-backend | 并行后端,ray或mp(单机多进程) |
--max-num-seqs | 单批次最大并发请求数,显存充足时可调大提升吞吐 |
--swap-space | 显存不足时 KV Cache 换出到 CPU 内存的空间大小(GB) |
六、量化与性能调优
6.1 量化方案对比
显存不够、想用更小的卡跑更大的模型时,量化是最直接的手段:
| 量化方案 | 精度 | 显存节省 | 适用场景 |
|---|---|---|---|
| FP16/BF16 | 全精度 | 基准 | 显存充足、追求最佳效果 |
| AWQ | INT4 | 约 4 倍 | 权重量化精度损失小,社区预量化模型多 |
| GPTQ | INT4 | 约 4 倍 | 需要针对性校准,推理速度与 AWQ 接近 |
| FP8 | 8 位浮点 | 约 2 倍 | 依赖 Hopper(H100/H800)等新架构硬件加速 |
| INT8 (bitsandbytes) | INT8 | 约 2 倍 | 无需额外校准,兼容性好但速度提升有限 |
启用量化模型只需在启动时指定量化方式和使用已量化的模型权重:
vllm serve TheBloke/Qwen2.5-72B-Instruct-AWQ\--quantizationawq\--dtypefloat166.2 关键调优参数
| 参数 | 默认值 | 调优建议 |
|---|---|---|
--gpu-memory-utilization | 0.9 | 单卡多服务共享时调低(如 0.6),避免抢占其他进程显存 |
--max-model-len | 模型默认上下文 | 按实际业务需求设置,越大 KV Cache 占用越多,不要设置远超实际需求的值 |
--enable-chunked-prefill | 视版本而定 | 长短请求混合负载建议开启,避免长 prompt 阻塞短请求 |
--enable-prefix-caching | 关闭 | 多轮对话、固定 system prompt 场景强烈建议开启 |
--block-size | 16 | 一般无需调整,特殊硬件场景可尝试 32 |
--max-num-batched-tokens | 自动 | 限制单批次总 token 数,防止显存瞬时占用过高 |
6.3 投机解码:进一步压缩解码延迟
投机解码用一个小模型先"草拟"多个候选 token,大模型一次性并行验证,命中率高时能显著减少解码轮数:
vllm serve Qwen/Qwen2.5-72B-Instruct\--speculative-model Qwen/Qwen2.5-1.5B-Instruct\--num-speculative-tokens5也可以用无需额外模型的 n-gram 投机(适合重复模式明显的生成任务,如代码补全):
vllm serve Qwen/Qwen2.5-72B-Instruct\--speculative-model"[ngram]"\--num-speculative-tokens5\--ngram-prompt-lookup-max4七、生产部署实践
7.1 容器化 + 环境变量管理
生产环境建议用docker-compose统一管理配置,密钥不写进命令行:
services:vllm:image:vllm/vllm-openai:latestruntime:nvidiaipc:hostports:-"8000:8000"environment:-HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}volumes:-~/.cache/huggingface:/root/.cache/huggingfacedeploy:resources:reservations:devices:-driver:nvidiacount:allcapabilities:[gpu]command:>--model Qwen/Qwen2.5-7B-Instruct --gpu-memory-utilization 0.9 --max-model-len 8192 --enable-prefix-caching7.2 监控:Prometheus + Grafana
vLLM 内置/metrics端点,暴露请求吞吐、排队时长、KV Cache 使用率等核心指标,可直接接入 Prometheus:
# prometheus.yml 片段scrape_configs:-job_name:'vllm'static_configs:-targets:['vllm-host:8000']生产环境重点关注的指标包括vllm:num_requests_running(正在处理的请求数)、vllm:num_requests_waiting(排队请求数)、vllm:gpu_cache_usage_perc(KV Cache 显存使用率)——排队数持续增长通常意味着需要扩容或调大并发参数。
7.3 多 LoRA 服务:一个基座模型服务多个业务场景
不需要为每个微调版本单独起一个服务实例,vLLM 支持在同一基座模型上动态挂载多个 LoRA 适配器:
vllm serve meta-llama/Llama-3.1-8B-Instruct\--enable-lora\--lora-modules customer-service=/path/to/lora-cs sql-agent=/path/to/lora-sql\--max-lora-rank32调用时通过model字段指定使用哪个 LoRA:
response=client.chat.completions.create(model="customer-service",messages=[{"role":"user","content":"我的订单还没到货"}],)7.4 Kubernetes 部署要点
大规模生产环境通常用 K8s 管理多副本,核心是把 GPU 资源声明和存活探针配置正确:
resources:limits:nvidia.com/gpu:1readinessProbe:httpGet:path:/healthport:8000initialDelaySeconds:60# 模型加载耗时较长,探针延迟需放宽periodSeconds:10initialDelaySeconds是最容易踩的坑:大模型加载动辄几十秒到几分钟,探针延迟设置过短会导致 Pod 被误判为不健康而反复重启。
八、横向对比:vLLM 与主流推理框架
vLLM 不是唯一的推理引擎选择,工程选型时通常会和以下几个框架放在一起比较:
| 维度 | vLLM | HuggingFace TGI | NVIDIA TensorRT-LLM | SGLang |
|---|---|---|---|---|
| 核心技术 | PagedAttention + 连续批处理 | 连续批处理 + Flash Attention | 编译期算子融合与内核优化 | RadixAttention(前缀树式缓存复用) |
| 易用性 | 高,pip install即用,HF 模型基本开箱即用 | 高,与 HF 生态深度绑定 | 较低,需要针对模型做引擎编译 | 中,API 设计与 vLLM 接近 |
| 极致性能 | 优秀,通用场景表现均衡 | 良好 | 同硬件下单点吞吐/延迟往往最优,代价是编译成本和灵活性 | 复杂结构化生成、多轮对话场景有优势 |
| 生态与模型覆盖 | 覆盖面最广,社区迭代快 | 与 HF Hub 集成紧密 | 主要覆盖 NVIDIA 官方适配的模型 | 覆盖主流模型,增长中 |
| 典型选择理由 | 通用生产服务的默认选项 | 已重度使用 HF 生态的团队 | 极致压榨 NVIDIA 硬件性能、愿意投入编译调优成本的场景 | 结构化输出、Agent 类复杂调用模式 |
对于本地单机、轻量场景(如个人开发、小规模私有部署),Ollama是更轻的选择——底层基于 llama.cpp,安装和模型切换比 vLLM 简单得多,但吞吐和并发能力明显弱于 vLLM,不适合高并发生产服务。
简单结论:追求通用性 + 社区活跃度 + 开箱即用的高吞吐,vLLM 是当前的默认项;追求单硬件极限性能且能承担工程投入,可以评估 TensorRT-LLM;结构化/Agent 类复杂调用场景可以关注 SGLang;本地轻量场景用 Ollama。
九、常见问题速查表
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 启动时 OOM | gpu-memory-utilization设置过高,或max-model-len远超实际需求 | 调低--gpu-memory-utilization,收紧--max-model-len |
| 多卡启动报错找不到设备 | --tensor-parallel-size与实际可见 GPU 数量不一致 | 检查CUDA_VISIBLE_DEVICES与参数是否匹配 |
| 长 prompt 请求拖慢其他请求 | 未开启分块预填充 | 添加--enable-chunked-prefill |
| 多轮对话重复计算浪费算力 | 未开启前缀缓存 | 添加--enable-prefix-caching |
| K8s 中 Pod 反复重启 | 存活探针延迟过短,模型还没加载完就被判定失败 | 调大initialDelaySeconds |
| 多机部署互相找不到节点 | Ray 集群未正确加入 | 检查ray status,确认所有节点已加入同一集群 |
| 量化模型效果明显下降 | 量化方式或校准数据集不适配当前模型 | 优先使用社区已验证的预量化权重,或更换量化方案 |
十、总结
| 维度 | 核心要点 |
|---|---|
| 核心原理 | PagedAttention 按需分页管理 KV Cache,连续批处理消除批次间的空闲等待 |
| 快速上手 | LLM类做离线批量推理,vllm serve一行命令起 OpenAI 兼容服务 |
| 分布式扩展 | 单机多卡用张量并行,跨机扩展叠加流水线并行,多节点依赖 Ray |
| 量化调优 | AWQ/GPTQ/FP8 按硬件和精度需求选择,gpu-memory-utilization、max-model-len是最先要调的两个参数 |
| 生产实践 | Docker 容器化 + Prometheus 监控 + 多 LoRA 服务 + K8s 探针延迟放宽 |
| 横向定位 | 通用高吞吐场景的默认选择,极限性能看 TensorRT-LLM,结构化生成看 SGLang,本地轻量场景用 Ollama |
vLLM 把"如何让一张卡多装几个请求""如何让长短请求互不拖累"这些原本需要团队自研的推理优化,封装成了几个命令行参数。工程落地的关键不在于记住每个参数的默认值,而在于理解 PagedAttention 和连续批处理解决的是什么问题——理解了这一点,无论是调显存利用率、开前缀缓存,还是排查排队请求为什么突然暴涨,都有了判断的依据。
参考资料:
- vLLM 官方文档
- Efficient Memory Management for Large Language Model Serving with PagedAttention (SOSP 2023)
- vLLM GitHub 仓库
- vLLM Production Stack
- Ray 官方文档 — 分布式计算框架
