
如果你最近正在折腾大模型部署应该对 vllm 这个词不陌生。它不是一个聊天应用也不是模型本身而是一个专门做大模型推理加速与服务的引擎。我最早接触 vllm 是因为一个很现实的问题用原始代码跑 7B 模型并发稍微上来一点显存直接爆掉响应时间也变得忽快忽慢。后来换成 vllm 部署大模型同样一张卡吞吐量提升好几倍这才真正体会到什么叫“推理框架选对了能省下好几块显卡的钱”。这篇是 vllm 系列教程的第 4 篇定位就是快速上手不管你有没有读过源码只要照这个流程走就能在自己机器上把一个可用的大模型推理服务跑起来并且搞清楚那些绕不开的关键参数到底在干什么。适合刚接触推理加速、想用 vllm 部署模型的人也适合已经跑起来但老是被各种报错卡住、天天盯着日志发呆的同学。1. 先搞明白 vLLM 到底解决了什么问题1.1 一个直白的比喻显存是仓库算子是搬运工很多人听到 PagedAttention、Continuous Batching 这些名词就开始头大其实它们解决的是一个非常朴素的资源管理问题。我习惯用一个仓库和搬运工的比喻来解释显存就是你的仓库模型权重是必须放在仓库里的货而每次推理产生的 KVCache键值缓存是在请求处理过程中临时堆起来的货架。传统推理框架在处理请求时会为每一个请求提前划出一整块连续仓库区域不管这个请求最后用不用得完那块区域都不能给别人用。这就好比你在仓库里预定了一大块区域结果只放了几箱货剩下的空位既不能让别的货车临时停靠也不能挪作他用。vLLM 的核心创新是把这些临时货架切成固定大小的小块按需分配给不同请求用完就回收。这就是 PagedAttention思想直接借鉴了操作系统里的虚拟内存分页。虚拟内存当年解决的是物理内存不够用的问题vLLM 用它解决的是 KVCache 碎片化和显存浪费的问题。效果非常明显在同样一张显卡上vLLM 能塞进更多的并发请求显存利用率显著提高。1.2 vLLM 的三大看家本领vLLM 能在众多推理框架里冲出来靠的不是单点优化而是把几个关键机制组合在一起。第一个就是 PagedAttention上面已经说过它把 KVCache 从连续存储变成分页管理减少内部碎片。第二个是 Continuous Batching传统的静态批处理要等一批请求全部结束后才能重新组批而 vLLM 可以在一个请求生成完第一个 token 后立刻腾出位置把另一个新请求接进来实现了“你方唱罢我登场”的动态调度GPU 的利用率自然就上去了。第三个本领是多卡并行支持。vLLM 原生支持张量并行和流水线并行简单理解就是把一个大模型的“大脑”切成几份放在多张显卡上协同推理。张量并行是把每一层的矩阵切分成块多张卡各算一部分再汇总流水线并行则是把模型按层切成段每张卡负责其中一段。对 70B、甚至更大规模的模型来说没有这套并行机制单卡物理上就放不下更别提跑了。1.3 哪些场景该用 vLLM哪些场景别强求搞清楚了 vLLM 解决了什么问题你就能判断自己是不是真的适合用它。它最擅长的场景是高并发在线推理服务也就是很多人同时访问你的模型接口希望每个请求都快速得到回应。这种场景下Continuous Batching 带来的吞吐提升非常可观。其次长上下文场景也特别适合 vLLM因为 PagedAttention 对超长上下文的 KVCache 管理更友好不会因为一个超长请求就把显存全吃光。但也要泼一盆冷水如果只是自己调试代码、跑一两个脚本做实验并发数长期为 1那 vLLM 带来的性能提升就没那么明显反而因为框架封装多了一层跑单个请求时延迟可能比直接加载模型更慢。另外如果你的显存非常小比如 6GB、8GB 这种跑超过 13B 的模型还是比较吃力这时候更重要的是考虑量化和小模型方案而不是指望推理框架变魔术。先把这些定位想清楚再往下看安装和启动就不会被各种预期落差折磨。2. 快速上手的硬件与安装别在最容易出错的环节翻车2.1 硬件需求到底怎么算一个简单的显存估算公式安装 vLLM 之前最该先做的是算清楚自己的显存够不够。这里分享一个快速估算办法模型权重占用显存 模型参数量 × 每个参数占用的字节数。如果是 FP16 精度每个参数占 2 字节所以 7B 模型大约需要 14GB 权重空间13B 模型大约 26GB70B 模型大约 140GB。再加上 KVCache、激活值、CUDA context 等额外开销实际占用会比这个数字高不少。所以如果你想用 FP16 跑 7B 模型你至少需要一张 24GB 的显卡比如 4090、A10、L4 就能凑合如果只有 16GB 甚至 12GB建议考虑量化版本比如 AWQ 或 GPTQ 量化的 4bit 模型权重直接减半以上这也解释了为什么现在很多部署教程都在教怎么用量化模型配合 vLLM。再补充一个经验显存越接近临界值越不要想着把模型塞得满满当当给自己留出 10%~15% 的余量否则跑起来容易遇到随机 OOM。2.2 安装 vLLMPython、CUDA、PyTorch 的版本搭配安装这块是很多人踩坑的重灾区。vLLM 不是一个单独就能跑的包它依赖 PyTorch、CUDA 运行时环境、各种编译工具链。如果版本不匹配很容易出现装了用不了、启动就报错的情况。先说最简单的安装方式用 pip 直接装官方预编译的 wheel 包python -m venv vllm-env source vllm-env/bin/activate pip install --upgrade pip pip install vllm前提是你的 Python 版本要在官方支持的范围内目前一般是 3.9 到 3.12。CUDA 方面vLLM 的预编译包会跟着特定 CUDA 版本走官方文档一般会给出对应的版本说明比如支持 CUDA 12.1 以上。装完之后可以用一行命令快速验证python -c import vllm; print(vllm.__version__)如果 import 成功说明基础依赖没问题。如果卡在编译阶段、或者报红字说找不到 CUDA 工具链大概率是环境不干净。我个人的建议是尽量在干净的虚拟环境里装不要和一堆深度学习项目共用同一个 conda 环境环境越乱越难排查。2.3 Windows 能不能玩 vLLM实话实说热词里有人搜“vllm windows 版”那我就直说了官方对 Windows 的支持非常有限绝大多数版本的 vLLM 没有提供 Windows 原生预编译 wheel。Windows 下想跑最省心的方案是 WSL2在里面开一个 Ubuntu 环境然后按 Linux 的方式安装。WSL2 能直接访问宿主机的 GPU配合最新驱动跑 vLLM 是完全可行的。我不太建议在 Windows 原生环境里硬碰硬因为需要自己编译一堆微软工具链编译时间很长遇到问题的概率也高。如果你只是在 Windows 桌面上做做实验不想折腾 WSL其实可以先试试 LM Studio 这类图形化推理工具它把模型下载、加载、聊天界面都打包好了适合快速体验大模型但它和 vLLM 是两回事。vLLM 追求的是服务化部署、高并发、可控参数LM Studio 则更像是本地版的 ChatGPT 客户端。两者目标不同你要按自己的需求选。3. 十五分钟跑通第一个推理服务3.1 一段最简单的启动命令逐项拆开讲环境配好之后跑通 vLLM 服务其实非常快。我以 Qwen/Qwen2.5-7B-Instruct 为例这是我在本机上测试最多、社区资料也比较全的模型。你先用 huggingface-cli 或者直接让 vLLM 启动时自动下载模型然后执行下面的命令vllm serve Qwen/Qwen2.5-7B-Instruct --host 0.0.0.0 --port 8000 --dtype auto如果你是在本机测试--host也可以不写默认 127.0.0.1但如果你想让局域网内其他机器访问就必须要0.0.0.0。--port 8000指定服务端口和 FastAPI 默认风格一致。--dtype auto的意思是让 vLLM 根据模型权重自动选择精度一般不需要你手选。启动过程中你会看到一堆日志其中最关键的是最后一段模型加载完成、KVCache 分配完成、服务启动。很多新手一看到日志里出现大量路径、版本号就慌其实只要最后看到类似Application startup complete和Uvicorn running的字样就说明服务已经起来了。整个过程中不要 CtrlC也别开两个窗口重复启动否则会碰上端口占用。3.2 用一个请求验证服务curl 和 Python 都试一遍服务启动后vLLM 默认提供一个 OpenAI 兼容的接口路径是/v1/chat/completions和/v1/completions。这意味着你之前用 OpenAI SDK 写的代码只需要把 base_url 改成http://localhost:8000/v1把 api_key 随便填一个占位符就能直接跑起来。先用最简单的 curl 命令验证一下curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: Qwen/Qwen2.5-7B-Instruct, messages: [{role: user, content: 你好请用一句话介绍你自己。}], temperature: 0.7 }这里有个特别容易踩的坑请求体里的model字段必须和你启动服务时传的模型名匹配或者和你用--served-model-name指定的别名一致。如果你启动时传的是完整路径Qwen/Qwen2.5-7B-Instruct那这里的 model 就填同样的字符串。如果你没指定别名很多人在这一步填qwen2.5-7b-instruct这种小写形式结果返回 404 或模型不存在这不是 vLLM 坏了你是你填错了名字。Python 侧的调用同样简单openai 库安装好之后from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keyEMPTY, ) response client.chat.completions.create( modelQwen/Qwen2.5-7B-Instruct, messages[ {role: user, content: 用一句话解释什么是 KVCache} ], temperature0.7, ) print(response.choices[0].message.content)如果 curl 返回了正常文本Python 脚本也能输出内容说明整个链路已经通了。接下来就可以进入真正的部署参数调优阶段。3.3 模型文件加载顺序与后台部署的小问题跑通之后你可能会遇到一类问题项目目录里明明放着模型文件启动却报各种奇怪的错误。这通常是因为模型目录不完整。vLLM 加载模型的顺序大致是先读config.json确定模型结构和参数量再读tokenizer相关文件最后加载权重*.safetensors或*.bin。这些文件缺一不可。你从一个页面手动下载模型时很容易漏掉 tokenizer 配置文件启动时日志就会提示找不到tokenizer_config.json或者tokenizer.json。解决办法很简单直接去 Hugging Face 模型仓库页看文件列表把config.json、tokenizer_config.json、tokenizer.json、vocab.json、merges.txt以及权重分片文件全部下齐。服务跑起来之后如果不想一直占着终端窗口可以用 tmux、nohup 或者 systemd 来管理。个人最推荐 tmux因为你可以随时tmux attach回来查看日志不像 nohup 把输出写到文件里还要手动 tail。关键日志保留好后面排查问题会轻松很多。4. 搞清楚这几个关键参数才算真正“会用”了4.1 max-model-len 与显存预估算给你看很多教程都是直接叫你加一行--max-model-len 8192也不解释为什么。这个参数决定了模型最多能处理多长的输入加输出序列它会直接影响 KVCache 的预留策略。KVCache 的大小和层数、注意力头数、头维度成正比序列越长、并发越高KVCache 占用就越大。拿 7B 模型为例假设它内部有几十亿参数、30 层左右的 Transformer 层如果你把max-model-len从 4096 拉到 32768KVCache 占用的显存会成倍增长。启动日志里通常会有这么一行信息告诉你为 KVCache 分配了多少 GiB比如Allocation of 10.0 GiB for KV cache。如果分配过小说明你的模型权重已经占用了太多显存KVCache 区域被压缩得厉害并发一高就容易排队等待甚至直接报错。我自己的建议是根据实际业务中最长的请求来决定这个值。如果只是做普通客服问答输入最多几百字输出最多一千字那max-model-len设 4096 或 8192 完全够用没必要无脑拉高到 32K否则显存都拿去喂 KVCache 了反而挤占了模型的并发能力。4.2 gpu-memory-utilization留给 KVCache 多少余粮--gpu-memory-utilization是用来控制 vLLM 最多能用多少比例显存的重要参数默认是 0.9也就是允许 vLLM 占满 90% 的显存。它影响的是权重加载完之后的剩余显存去向一部分给 KVCache一部分给计算过程中的临时激活值剩下的留给 CUDA context 和 PyTorch 内部开销。我在 24GB 显卡上跑 7B 模型时经常把--gpu-memory-utilization设为 0.92 甚至 0.95因为 7B 权重只占一小半剩下的空间要给 KVCache 留足余量才能支撑高并发。但在只有 16GB 显存、还跑量化模型的机器上我会保守一点设为 0.85。这个数值不是越大越好如果设成 0.99PyTorch、CUDA context 连一点缓冲都没有一旦请求波动很容易直接 OOM设得太低比如 0.7又会发现服务倒是稳定但并发稍微一高KVCache 不够请求开始排队吞吐上不去。建议先按 0.9 跑观察日志里 KVCache 的分配情况再微调。4.3 enforce-eager、quantization、trust-remote-code 这几个隐藏的坑--enforce-eager是我特别想提醒新手的一个参数。vLLM 默认启用 CUDA Graph 优化把模型执行图提前编译好推理时省去反复调用内核的开销。代价是启动时会额外花几十秒到几分钟来编译显存占用也会稍高。如果你的显存很紧张启动老报 OOM可以加上--enforce-eager试试它会关闭 CUDA Graph启动更快、显存占用更低但单请求推理吞吐会有明显下降。等调试稳定了再把参数去掉恢复正常模式。再来说--quantization。如果你下载的是 AWQ 或 GPTQ 量化模型必须在启动时告诉 vLLM 用对应的量化方式。比如模型目录里有量化配置文件你可以在启动命令里写--quantization awq或--quantization gptq。如果懒得手动指定一些模型目录自带量化配置vLLM 能自动识别但不是所有模型都能所以启动后如果看到算子加载失败的日志优先检查是不是量化格式没对上。--trust-remote-code这个参数也容易被忽略。它其实是在说“我信任这个模型仓库里的自定义 Python 代码”因为有些模型在 Hugging Face 上会放一些自定义的模型实现文件不加载这些代码就无法初始化模型。如果你用的是比较新的指令微调模型启动时经常提示需要加这个参数直接加上就好前提是你确认模型来源可信。4.4 并发、前缀缓存和服务别名生产环境最常用的几个参数生产环境里还有几个参数几乎天天要用我用一个表格总结一下参数作用我的常用值--max-num-seqs限制同时处理的序列数量保护显存和延迟64~256--enable-prefix-caching开启前缀缓存相似请求可以复用 KVCache开启--served-model-name给服务暴露一个别名方便前端统一调用qwen7b之类--tensor-parallel-size多卡张量并行传显卡数量单卡为 1--host/--port服务监听地址和端口0.0.0.0/8000--enable-prefix-caching是个容易被低估的优化。如果业务里有很多用户共享同一个系统提示词前缀相同的部分会被缓存下来后续请求可以复用 KVCache省掉重复计算对降低首 token 延迟非常明显。--served-model-name也很有用因为实际部署中前端只关心一个固定的模型名不希望以后换模型还要改前端配置这时候给服务起个别名就是最简单的解耦方式。5. 高频踩坑排查链路从报错到定位的全过程5.1 CUDA out of memory 不只是调小 max-model-lenOOM 应该是新手遇到最多的问题。很多人第一反应是把max-model-len调小这确实有用但只是治标。OOM 的根源可能是显存被历史占满之前启动的服务没有完全退出也可能是gpu-memory-utilization设置过高导致请求波动时没有缓冲空间。我遇到 OOM 时一般会按下面这个顺序排查先跑nvidia-smi看当前显存是不是已经有进程占着。如果之前启动 vLLM 的终端被你直接关闭但进程没被杀死显存就会一直被占用这时候kill -9掉对应进程再重新启动。如果显存是干净的再看启动日志里 KVCache 分配了多少。如果权重KV Cache 已经接近显存上限就考虑降并发、降低gpu-memory-utilization或开启量化。在环境变量里加一句PYTORCH_CUDA_ALLOC_CONFexpandable_segments:True可以让 PyTorch 的显存分配更灵活减少碎片实测在某些模型上能缓解 OOM。最后才考虑调小max-model-len因为它会牺牲模型处理长文本的能力。这套链路我反复用过很多次绝大多数字符串 OOM 都能解决掉。记住一个原则不要一上来就重启先看显存被谁吃了。5.2 版本错配导致的启动崩溃一套可复现的排查流程vLLM 社区的版本更新非常快几乎隔几周就会发一个新版本每个版本对 PyTorch、CUDA 的依赖都可能不一样。最典型的报错是启动时出现ModuleNotFoundError: No module named vllm._C或者一堆和 CUDA 算子相关的红色堆栈。这种十有八九是环境里的 PyTorch 版本和 vLLM 编译时使用的版本不一致。我的处理流程是先看 vLLM 当前版本对应的 requirements 文件需要哪些版本依赖然后卸载掉环境中已有的 vllm、torch全部重装。更稳妥的做法是直接用官方 Docker 镜像比如vllm/vllm-openai镜像里已经打包好了所有依赖不需要你自己操心版本搭配。如果在 Windows 上折腾就先确认 WSL2 里的 CUDA 驱动版本和容器里的 CUDA 兼容性这一步能省去后面大量调试时间。热词里有人搜“vllm构建需要多长时间”我多说一句不要轻易尝试从源码编译除非官方没有你这个平台或 CUDA 版本的预编译包否则海外下载依赖加编译的时间可能让你怀疑人生。5.3 日志里的 nccl 信息到底意味着什么很多人在启动多卡推理时会在日志里看到一行类似[pynccl.py:113] vllm is using nccl2.30.7的内容以为发生了错误。我第一次看到也愣了一下后来才确认这只是一条信息日志意思是 vLLM 正在使用 NCCL 作为多卡通信库。NCCL 对多卡并行来说非常重要尤其是张量并行时每张卡之间需要频繁同步中间计算结果NCCL 就是负责这条通信管道的核心库。如果在设置--tensor-parallel-size 2之后启动过程卡住、报 NCCL 超时或初始化失败先检查显卡之间的通信拓扑。可以用nvidia-smi topo -m看两张卡是不是通过 NVLink/NVSwitch 连接如果是普通 PCIe 链路速度会慢一些但一般也能用。遇到某些特殊环境比如虚拟化平台或者异构卡混插可能还要设NCCL_P2P_DISABLE1强制走共享内存或网络通信虽然性能会降但至少能跑起来。碰到多卡问题时先把nvidia-smi输出和 vLLM 启动日志放一起看比盲目改参数靠谱得多。6. vLLM、SGLang 与其它方案怎么选以及下一步能从哪里继续深挖6.1 vLLM 和 SGLang 的螺丝对螺丝对比热词里有人搜“sglang和vllm”说明很多人在这两个框架之间摇摆。SGLang 是另一个高性能推理框架它在调度和前缀缓存上做了很多激进设计RadixAttention 机制能把公共前缀的 KVCache 复用到极致。如果你有大量请求都带着长长的系统提示词或者模型应用里有复杂的多轮工具调用SGLang 的吞吐优势会很突出。但 vLLM 也不是吃素的。它的生态成熟度最高各种第三方库、监控工具、一键集成基本都会优先兼容 vLLM 的 OpenAI 风格接口遇到问题能在社区里搜到大量答案。对我个人来说如果没有做复杂 Agent 工作流和极致吞吐压测我一般会默认选 vLLM因为稳定、省心、资料多。如果你的业务场景真的很吃前缀缓存和多轮交互比如代码补全、复杂问答机器人可以拿 SGLang 做一轮 benchmark 对比再决定要不要迁移。框架选型从来不是“谁好谁坏”的问题而是“哪个更匹配你的负载”。6.2 用 benchmark 验证效果不要凭感觉优化很多人跑通 vLLM 之后觉得“响应挺快”就没下文了。真要优化性能还是得量化。vLLM 仓库自带 benchmark 脚本常见的有benchmarks/benchmark_serving.py用来压测在线服务的吞吐和延迟。新版本里也提供了更便捷的vllm bench serve子命令。用法大致是python benchmarks/benchmark_serving.py \ --backend vllm \ --model Qwen/Qwen2.5-7B-Instruct \ --base-url http://localhost:8000/v1 \ --num-prompts 200 \ --request-rate 10跑完之后会输出一堆指标最值得关注的是 Output token throughput也就是每秒生成的 token 数量以及 TTFTTime To First Token也就是用户发出请求后多久看到第一个字。TTFT 直接影响对话的“跟手”感吞吐则直接影响单位时间能服务多少用户。我一般会用不同的--request-rate压低测和高测画一条吞吐随并发变化的曲线找到服务的饱和点。这样调参就不是拍脑袋了。6.3 下一步深挖方向前缀缓存、LoRA、多模态与结构化输出跑通基础服务之后可以往上深入的方向其实很多。如果你对前缀缓存感兴趣就用--enable-prefix-caching打开它对比开关前后的 TTFT 数据如果要做多用户个性化可以研究 vLLM 的 LoRA 适配能力多个低秩权重可以同时驻留显存不用切换模型如果业务需要从文档里抽取结构化字段vLLM 对 JSON structured output 的支持也越来越好可以在接口里直接用response_format控制输出格式。多模态模型方面vLLM 也开始支持视觉语言模型但配置会比文本模型复杂一些需要留意官方对各视觉编码器的支持状态。我个人觉得快速上手的终点不是“能输出一段文本”而是“知道每一步日志在告诉你什么”。比如启动日志里的 KVCache 分配量、吞吐压测里的指标、显存监控曲线这些才是真正帮你做决策的信息。6.4 最后留一个实测小技巧本机玩的时候如果显存不够又不想换模型可以试试把--dtype改成--dtype half对大多数模型来说就是使用 FP16 精度在支持半精度计算的显卡上能省一半显存。如果整个模型都放不下再考虑下载 4bit 量化版用--quantization awq或--quantization gptq加载。顺序不要搞反否则你会在日志里看到一堆算子不支持的报错容易劝退。每次改完参数都建议先启动一个最小请求验证再去跑并发压测。这样能更快定位到底是模型加载的问题还是参数设置的问题而不是一头扎进一堆日志里越看越乱。