
1. 为什么我最终把推理服务从其他方案迁到了 vLLM第一次接触 vLLM 是在一个需要同时跑多个并发请求的大模型推理场景里。当时用常规的推理框架单张卡上并发一上来吞吐量就断崖式下跌显存也像漏水的桶一样被 KV Cache 一点点吃光。后来换成 vLLM同样的硬件、同样的模型吞吐量直接翻了好几倍显存占用反而更可控。这个反差让我意识到vLLM 不是一个又一个推理框架而是从底层重新设计了显存管理和请求调度的一整套方案。vLLM 的核心价值简单说就是三件事高吞吐、低显存浪费、易部署。它最出名的技术是PagedAttention把 KV Cache 像操作系统管理内存分页那样切成块来管理避免了传统实现里因为预留连续显存而造成的碎片和浪费。再配合Continuous Batching连续批处理新请求可以随时插进正在运行的批次里不用等整批跑完GPU 利用率被拉得很高。这篇文章适合谁看如果你手里有一张或几张 GPU想把开源大模型跑成一个能对外提供服务的接口又不想被显存和并发问题反复折磨那这篇就是写给你的。我会从零开始讲清楚环境怎么装、服务怎么起、显存怎么调、踩过的坑有哪些。全程按我实际操作的顺序来不跳步也不堆砌没用的概念。需要先说明一点vLLM 的迭代速度非常快命令参数和默认行为在不同版本之间会有差异。我下面给出的操作以较新的稳定版本为准但你在实际执行时最好先确认自己装的版本号遇到参数不识别的情况优先查对应版本的官方文档而不是照搬旧教程。2. 装 vLLM 之前先把环境这关想明白2.1 硬件与驱动的硬性门槛vLLM 对硬件不是能跑就行它对 GPU 的计算能力和显存有比较明确的要求。我整理了一张表方便你对照自己的机器判断项目最低可用推荐配置说明GPU 架构计算能力 7.0 及以上8.0 及以上太老的卡很多算子不支持单卡显存16GB 起24GB 及以上决定能跑多大的模型驱动版本较新的生产版驱动与 CUDA 版本匹配驱动太旧会报找不到设备CUDA 版本11.8 / 12.1 等12.1 及以上要和 PyTorch 编译版本对齐系统内存32GB64GB 及以上加载模型权重时会占用这里有个特别容易被忽略的点CUDA 版本、PyTorch 版本、vLLM 版本三者必须对齐。我见过太多人装完之后报undefined symbol或者no kernel image is available八成就是这三者没对上。vLLM 的 wheel 包在编译时就绑定了特定的 PyTorch 和 CUDA你如果自己手动升级了 PyTorch很可能就把 vLLM 搞崩了。提示装 vLLM 时不要先手动装 PyTorch 再装 vLLM让 pip 自己解析依赖或者严格按官方给出的版本组合来。手动干预依赖是这类报错的头号来源。2.2 用虚拟环境隔离别污染系统 Python我强烈建议用 conda 或者 venv 建一个独立环境。原因很实际vLLM 依赖的 PyTorch、transformers、numpy 版本都比较挑一旦和你系统里其他项目的依赖打架排查起来非常痛苦。用 conda 的话大致是这样conda create -n vllm python3.10 -y conda activate vllmPython 版本我推荐 3.10 或 3.11这两个版本在各类依赖上的兼容性最稳。3.12 虽然也能用但偶尔会遇到某些包还没出对应 wheel 的情况需要现场编译费时费力。2.3 安装命令与国内网络的处理官方推荐的安装方式就是 pippip install vllm但如果你在国内直接从默认源拉取会非常慢甚至中途断掉。我的做法是换用国内镜像源同时把超时时间调长pip install vllm -i https://pypi.tuna.tsinghua.edu.cn/simple --timeout 120这里有个经验vLLM 的 wheel 包体积不小加上它依赖的 PyTorch 动辄几个 G整个安装过程下载量很大。如果你网络不稳定建议分步来——先单独把 torch 装好用镜像源再装 vllm这样断了也不用从头再来。装完之后一定要验证一下python -c import vllm; print(vllm.__version__)能打印出版本号说明基本环境没问题。如果这一步就报错别急着往下走先把报错信息看仔细通常是 CUDA 或 PyTorch 的问题。2.4 关于 Windows 用户要提前知道的事vLLM 的原生支持是面向 Linux 的Windows 上直接 pip 安装经常会卡在编译环节。热词里出现的vllm windows 社区版其实反映的就是这个痛点。我的建议很直接如果你要在 Windows 上用 vLLM优先考虑 WSL2在 WSL 里按 Linux 的方式装成功率最高。实在要用原生 Windows就得做好折腾编译工具链的准备性价比不高。3. 把服务跑起来启动命令背后的每个参数都值得说清楚3.1 最小可用启动命令先把最简单的跑通再谈优化。假设你本地已经下载好了模型权重比如放在/models/Qwen目录下那么一条命令就能起服务python -m vllm.entrypoints.openai.api_server \ --model /models/Qwen \ --served-model-name qwen \ --host 0.0.0.0 \ --port 8000这条命令起的是一个兼容 OpenAI 接口的服务也就是说任何原本调用 OpenAI API 的客户端只要把 base_url 改成你的地址就能直接对接。这是 vLLM 特别讨喜的一点迁移成本几乎为零。几个参数解释一下--model指向模型路径可以是本地目录也可以是 Hugging Face 上的模型名--served-model-name是你对外暴露的模型名客户端请求时用的就是这个名字--host 0.0.0.0表示监听所有网卡方便局域网内其他机器访问。3.2 显存相关的关键参数真正决定你能不能跑起来、能跑多快的是显存参数。最核心的是这个--gpu-memory-utilization 0.9这个参数控制 vLLM 最多能用掉单卡显存的百分比默认是 0.9。为什么不是 1.0因为你要给系统、CUDA 上下文、以及一些临时张量留出余量。设成 1.0 很容易在运行中 OOM。我一般从 0.85 到 0.9 之间试如果模型大、并发高就往下调一点。另一个关键参数是--max-model-len 8192它限制单个请求的最大上下文长度。这个值直接决定了 KV Cache 的峰值占用。很多人启动就 OOM就是因为模型默认的最大长度太大比如 32K而显存根本撑不住。把 max-model-len 调小是解决启动 OOM 最有效的手段之一。还有一个常被忽略的--tensor-parallel-size 2这是张量并行度等于你用几张卡来分摊一个模型。单卡就设 1两张卡设 2。注意它要求卡数能被整除而且卡之间最好有高速互联否则通信开销会拖慢整体速度。3.3 显存不够时的几种降级策略我把实际用过的降级手段按优先级列一下从代价最小到最大降低 max-model-len最直接代价是支持的上下文变短。降低 gpu-memory-utilization给系统留更多余量但可用 KV Cache 变少并发能力下降。启用量化比如用 AWQ 或 GPTQ 量化后的权重显存占用能降到原来的三分之一到一半。开启 CPU 卸载--cpu-offload-gb可以把部分权重放到内存但速度会明显变慢。换更小的模型实在不行就降规格这是最后的办法。这里我要强调一个反直觉的点显存调优不是把利用率拉满就好。我曾经把 gpu-memory-utilization 设到 0.95结果服务跑一段时间后偶发 OOM排查半天才发现是某些请求的输入特别长KV Cache 瞬间膨胀。后来降到 0.88稳定运行再没出过问题。所以留余量不是浪费是给突发流量买保险。3.4 验证服务是否正常服务起来后别急着接业务先用 curl 测一下curl http://localhost:8000/v1/models能返回模型列表说明服务活着。再发一个补全请求curl http://localhost:8000/v1/completions \ -H Content-Type: application/json \ -d { model: qwen, prompt: 你好请介绍一下你自己, max_tokens: 128 }如果能看到正常的生成结果恭喜最小闭环打通了。这一步看着简单但它是后面所有调优的基础一定要先确认它稳。4. 显存调优的实战思路从能跑到跑得好4.1 先搞清楚显存到底被谁吃了调优的前提是知道显存花在哪。vLLM 的显存大致分三块模型权重、KV Cache、激活值和临时缓冲。模型权重是固定的KV Cache 是动态的临时缓冲则和批大小、序列长度相关。启动日志里其实会打印这些信息比如 GPU memory usage 和 KV cache blocks 之类的字样。我建议你养成看启动日志的习惯它会告诉你模型权重占了多少、还剩多少给 KV Cache、能容纳多少个 block。这些数字是调优的直接依据。一个粗略的估算公式是KV Cache 显存 ≈ 2 × 层数 × 注意力头数 × 头维度 × 序列长度 × 批大小 × 数据类型字节数。你不需要精确算但要有个概念——序列长度和批大小是乘数关系任何一个翻倍KV Cache 就翻倍。这就是为什么长上下文和高并发很难同时满足。4.2 量化显存不够时最划算的一招如果你的卡显存有限又想跑稍大的模型量化几乎是必选项。vLLM 对 AWQ 和 GPTQ 的支持都比较好。用法上你只需要加载量化后的权重python -m vllm.entrypoints.openai.api_server \ --model /models/Qwen-AWQ \ --quantization awq \ --gpu-memory-utilization 0.9量化带来的收益很直观一个原本需要 24GB 显存的模型量化后可能 12GB 就能跑。代价是精度会有轻微损失但在大多数对话和生成任务上这种损失几乎感知不到。我的经验是优先选 AWQ它在推理速度和精度保持上比较均衡而且社区里现成的量化权重多不用自己折腾。GPTQ 也可以但不同实现之间的兼容性偶尔会有小问题。4.3 批处理与并发的平衡vLLM 的 Continuous Batching 是它的看家本领但并发不是越高越好。并发太高KV Cache 被占满新请求就得排队延迟反而上升。这里有个实用的观察指标看服务的排队情况。如果请求经常在队列里等很久说明并发超过了显存能承载的上限。控制并发的参数是--max-num-seqs 256它限制同时处理的最大序列数。默认值通常够用但在显存紧张时把它调小能让服务更稳定。我一般会结合--max-model-len一起调上下文短就可以多放一些并发上下文长就得减少并发。4.4 一个真实的调优案例说个具体的。有次我要在一张 24GB 的卡上跑一个 7B 的模型要求支持 8K 上下文。第一次启动直接 OOM。我的排查顺序是这样的第一步把 max-model-len 从默认值降到 8192还是 OOM。第二步把 gpu-memory-utilization 从 0.9 降到 0.85勉强能起但并发一高就崩。第三步换成 AWQ 量化权重显存占用直接降到一半左右这时候再把 max-model-len 调回 8192gpu-memory-utilization 设 0.9稳定运行并发也上去了。整个过程的核心逻辑就是先定位瓶颈是权重还是 KV Cache再决定是量化还是缩上下文。如果权重就占了大头量化最有效如果权重不大但 KV Cache 爆了那就缩上下文或降并发。5. 那些文档里不会写、但一定会遇到的坑5.1 启动卡住不动也不报错这个现象很常见尤其是第一次加载模型时。vLLM 要从磁盘读权重、初始化 CUDA、编译一些算子这个过程可能持续几分钟。如果你看到日志停在某一行不动先别急着 CtrlC等一等。判断方法是看 GPU 利用率用nvidia-smi观察如果显存占用在涨说明它在干活。真正卡死的情况通常是模型路径写错、权重文件损坏、或者显存不够在反复重试。这时候日志里一般会有线索仔细翻一翻。5.2 端口被占用--port 8000如果被别的服务占了启动会失败。换个端口就行比如 8001。这个坑很小但第一次遇到会懵一下因为报错信息不一定直白。5.3 模型下载慢或下载失败如果你直接用 Hugging Face 的模型名vLLM 会去联网下载。国内网络下这一步经常超时。我的做法是提前用工具把权重下到本地然后--model指向本地目录。这样启动时不再依赖网络稳定得多。5.4 多卡启动时的常见问题用--tensor-parallel-size多卡时最容易出问题的是卡之间的可见性。要确保CUDA_VISIBLE_DEVICES设置正确而且所有卡型号一致。混插不同型号的卡vLLM 很可能起不来或者性能被最慢的那张拖累。注意多卡并行不是简单地把卡数加上去就更快。通信开销会随卡数增加2 卡的加速比通常不是 2 倍可能只有 1.6 到 1.8 倍。卡越多边际收益越低。5.5 版本升级后的参数失效前面提过vLLM 迭代快。我有次升级版本后原来能用的参数报unrecognized arguments。解决办法就是查新版本的帮助python -m vllm.entrypoints.openai.api_server --help把帮助信息里对应的新参数名找出来替换。养成升级后先看 help 的习惯能省很多时间。6. 让服务真正可用接口对接与日常维护6.1 用 OpenAI 客户端直接对接服务起来后对接非常简单。以 Python 为例from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keyEMPTY ) resp client.chat.completions.create( modelqwen, messages[{role: user, content: 帮我写一段自我介绍}] ) print(resp.choices[0].message.content)注意api_key随便填一个非空字符串就行vLLM 默认不校验。model要和你启动时的--served-model-name一致否则会报找不到模型。6.2 日志与监控服务跑起来只是开始日常维护更重要。vLLM 会输出请求日志包括处理的 token 数、耗时等。我建议把这些日志收集起来重点关注两个指标首 token 延迟和吞吐量。首 token 延迟反映用户等待感吞吐量反映资源利用效率。这两个指标一旦恶化往往意味着显存或并发到了瓶颈。如果要做更细的监控可以接 PrometheusvLLM 暴露了 metrics 接口。不过对大多数个人和小团队场景看日志加nvidia-smi就够用了。6.3 优雅重启与版本管理生产环境里服务不能随便 kill。我一般会记录当前使用的启动命令和版本号写在一个脚本里。升级或调整参数时先起一个新实例在另一个端口验证没问题后再切换流量最后停掉旧实例。这样能做到几乎无感更新。7. 我踩过几次坑之后总结的几条经验第一条永远先跑最小配置。不要一上来就把参数拉满先用默认值把服务跑通再一项一项调。这样出问题时你能快速定位是哪个参数引起的。第二条显存利用率留 10% 到 15% 的余量。这不是保守是给突发长请求和系统开销留空间。我见过太多为了榨干显存而把服务搞得不稳定的例子。第三条量化权重优先于缩上下文。如果显存不够先考虑量化因为它对使用体验的影响最小。缩上下文会直接限制你能处理的任务类型。第四条把启动命令脚本化。参数一多手敲容易出错而且不利于复现。写成一个 shell 脚本注释清楚每个参数的作用下次调整时一目了然。第五条关注版本兼容性。CUDA、PyTorch、vLLM 三者的版本组合最好固定下来不要频繁升级。升级前先在测试环境验证确认没问题再上生产。最后分享一个小技巧如果你不确定某个显存参数该设多少可以先用一个很小的值启动然后逐步往上加同时观察日志里的 KV Cache block 数量。找到一个既能满足并发、又不会 OOM 的平衡点这个点往往比理论计算出来的更靠谱因为它是你实际硬件和负载下的真实结果。