ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

vLLM模型启动实战:从环境配置到参数调优全解析

vLLM模型启动实战:从环境配置到参数调优全解析 1. 为什么模型启动都绕不开vLLM这两年做大模型推理部署手里只要有一两张像样的显卡基本都会接触到 vLLM。相比直接用 Transformers 的generate接口做推理vLLM 的启动方式表面上只是多了一行vllm serve但背后解决的问题完全是两个维度的事。你用 Transformers 跑一个 7B 模型单卡 A100 大概能吃到 30~40 tokens/s 的生成速度换到 vLLM 同卡同模型轻松翻倍甚至更多显存占用还能降下一截这就是为什么社区里谈部署 DeepSeek、Qwen 这类大模型时vLLM 几乎是默认选项。vLLM 的核心卖点是那套 PagedAttention 注意力机制它把 KV Cache 切成固定大小的块来管理有点类似操作系统里的虚拟内存分页。传统推理框架会把 KV Cache 预先分配一整块连续显存不管实际用到多少先占着vLLM 则是用到多少分配多少结束的序列立刻释放对应块新请求还能直接复用这些“内存页”。这一下就把显存利用率和吞吐都拉上来了。再加上它内置的连续批处理Continuous Batching请求不用等整个 batch 跑完才插进来而是每步解码之后动态调整 batch 成员服务端的资源利用效率自然比静态批处理高一个级别。所以这篇文章想做的事情很明确把 vLLM 模型启动这件事从环境准备、启动参数到常见坑位完整捋一遍。无论是刚入行想做本地 Demo还是在生产环境里部署服务化推理照着这篇文章的步骤和参数思路去配应该都能少走很多弯路。2. 环境准备CUDA、Python、安装方式一个都不能错启动 vLLM 之前卡住的多半不是模型问题而是环境没对齐。这个框架对 CUDA 版本、Python 版本和 PyTorch 版本的敏感程度比普通科学计算库要高得多。版本不匹配直接编译失败或者启动即崩溃排查起来极其消磨耐心。2.1 CUDA 版本与 GPU 驱动的关系先说最容易被忽略的问题nvidia-smi里显示的 CUDA 版本和 vLLM 运行时用到的 CUDA 不一定是同一个东西。nvidia-smi显示的是驱动所支持的最高 CUDA 版本而 vLLM 实际用的是 PyTorch 自带的 CUDA runtime。打个不太严谨但很好记的比方驱动是“操作系统”能向下兼容旧的 CUDAPyTorch 是“应用程序”它只认自己打包进去的 CUDA 版本。所以你在nvidia-smi里看到 CUDA 12.4但装了 CUDA 11.8 版本的 PyTorch程序照跑不误。真正决定 vLLM 能用哪个 CUDA 版本的是 PyTorch 的构建版本。目前社区里最稳的组合是CUDA 12.1 PyTorch 2.1或者直接上CUDA 12.4注意热词里有不少人提“cuda128 vllm”这是指 CUDA 12.8 环境属于比较新的组合主要适配 Blackwell 架构的 RTX 50 系显卡。如果你用的是 50 系卡官方 wheel 里已经有对应构建直接pip install vllm就行如果是 40 系及更老的卡装默认版本基本不会出问题。提示在命令行执行python -c import torch; print(torch.version.cuda)看到的是 PyTorch 实际使用的 CUDA 版本这个才是有意义的。如果它和你预期不一致请先重装对应 CUDA 版本的 PyTorch再装 vLLM。2.2 安装 vLLM 的两种主流方式安装 vLLM 无非两条路pip直接装预编译 wheel或者从源码编译。对绝大多数人来说直接 pip 安装是最省心的选择官方对主流 CUDA 版本都发布了对应的预编译包。# 创建虚拟环境Python 版本建议 3.10~3.12 python -m venv vllm_env source vllm_env/bin/activate pip install vllm安装完成后可以先用一个小模型验证环境是否健康不要一上来就拉几百 GB 的大模型。这里我习惯先跑一个Qwen2.5-0.5B-Instruct之类的迷你模型探探路vllm serve Qwen/Qwen2.5-0.5B-Instruct --max-model-len 2048如果这条命令能正常启动说明 CUDA 相关依赖都齐了。如果报错那就别急着继续先解决环境问题否则后面所有启动都会在同一个地方卡住。源码编译这条路线一般只在两种情况下才需要走一是你需要自定义算子二是你的 GPU 架构太新、而官方 wheel 的预编译版本还没来得及适配。编译本身的步骤不复杂但耗时很长7B 级别的模型跑一轮编译就是 20 分钟以上的事而且对显存和内存都有要求。没有特殊需求别碰源码编译。3. 模型启动实战从最小命令到生产级脚本环境就绪之后真正的重头戏是理解 vLLM 的启动参数。很多人在这一步栽跟头不是命令敲不对而是参数含义没吃透导致启动成功但推理性能很差或者频繁 OOM。3.1 最小启动命令用 vLLM 拉起一个开源模型的命令格式非常简洁vllm serve Qwen/Qwen2.5-7B-Instruct --host 0.0.0.0 --port 8000这条命令做了几件事从 Hugging Face 拉取模型权重到本地缓存加载模型并初始化推理引擎最后在8000端口启动一个 OpenAI 兼容的 HTTP 服务。启动成功后服务端的日志会打印当前模型的显存占用、KV Cache 分配情况以及监听地址。输入下面的命令做一个健康检查curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: Qwen/Qwen2.5-7B-Instruct, messages: [{role: user, content: 你好介绍一下你自己}], max_tokens: 128 }能正常返回带choices字段的 JSON就说明模型服务已经跑起来了。这里有个小细节请求体里的model字段默认必须和启动时的--model保持一致否则会报模型不存在的错误。3.2 关键启动参数逐个拆解了解最小启动命令之后我们来看生产环境中真正决定成败的几个参数。它们之间的关系没有想象中复杂但每个都值得搞清楚“为什么”。--gpu-memory-utilization和--max-model-len这两个参数是一对孪生兄弟共同决定 KV Cache 的可用空间。--gpu-memory-utilization表示 vLLM 最多能用多少比例的显存默认值是 0.90也就是给 CUDA context、模型权重以外的显存留 10% 余量。--max-model-len则限制模型最长能处理多少 token这直接影响 KV Cache 的总大小。实际配置时如果显存只有 24GB 而要跑 14B 模型直接把--max-model-len从默认的 8192 降到 4096 甚至 2048往往是能启动成功的决定性操作。因为 KV Cache 占用的显存和序列长度成正比序列长度砍半KV Cache 占用也砍半。反过来如果你显存充裕可以把--gpu-memory-utilization提到 0.95给更大并发留空间。--tensor-parallel-size与多卡协同当一张卡放不下模型时就需要用到张量并行。这个参数的含义是把模型的张量切分到多张 GPU 上并行计算和nn.DataParallel那种数据并行有本质区别。--tensor-parallel-size 2表示用两张卡来共同承载一个模型显存翻倍的同时推理速度也会有近似线性的提升。多卡启动时有一条公认的准则四卡以下优先用张量并行提升单请求吞吐四卡以上优先考虑数据并行跑多个副本提升整体并发。因为张量并行需要卡间通信每步 decode 都有一个 all-reduce 的开销卡太多通信成本会吞掉计算收益。--served-model-name这是启动参数里最容易被忽略、但调用端必须理解的一个。它定义了服务对外暴露的模型名。很多团队内部会把模型名统一成业务代号比如qwen2.5-7b这样调用方不需要关心底层模型细节换模型时也不用改客户端代码。vllm serve Qwen/Qwen2.5-7B-Instruct --served-model-name qwen2.5-7b curl http://localhost:8000/v1/models | python -m json.tool--max-num-seqs与并发控制这个参数控制最大并发序列数默认 256。它本质上是在“并发请求数”和“每个请求的显存配额”之间做平衡。并发数越高KV Cache 被切得越碎每个请求分到的显存越少极端情况下会限制单请求的max_tokens。如果你发现请求报ValueError: Requested max_tokens is larger than the maximum token number多半就是这个参数和--max-model-len打架了。合理做法是让max-model-len除于max-num-seqs得到的单序列 KV Cache 配额留出 30% 以上的余地。3.3 一个生产级启动脚本的完整示例把上面的参数合起来一个生产环境可用的启动脚本长这样#!/bin/bash export CUDA_VISIBLE_DEVICES0,1 export VLLM_CACHE/data/models_cache python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-14B-Instruct \ --served-model-name qwen2.5-14b \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.95 \ --max-model-len 8192 \ --max-num-seqs 16 \ --host 0.0.0.0 \ --port 8000 \ --enforce-eager--enforce-eager这个参数值得单独说它用来关闭 CUDA Graph 优化。默认情况下 vLLM 会用 CUDA Graph 捕获模型前向计算来降低 kernel launch 开销但代价是启动时要多预留一部分显存。如果你在 24GB 单卡上跑 7B 模型遇到启动即 OOM加上--enforce-eager往往能救回来代价是推理速度会下降 20%~30%。这个取舍在实际部署中很常见。注意VLLM_CACHE环境变量是 vLLM 寻找模型缓存的路径。不设它时模型下载默认冲锋到家目录的.cache/huggingface下一旦系统盘空间不足下载就会静默失败。我在生产服务器上一定会显式设置它到数据盘。4. 多卡并行与量化模型的启动细节单卡启动是入门多卡和量化才是生产环境的日常。这两部分各有各的坑实际操作时踩过的雷值得拿出来单独说。4.1 Tensor Parallel 启动第一步设备可见性多卡启动最容易出的问题就是没控住CUDA_VISIBLE_DEVICES。服务器上插了 8 张卡你只想用其中的 2 张如果不设置这个环境变量vLLM 默认会从 0 号卡开始取一旦 0 号卡被别的任务占着启动阶段就会报显存不足甚至直接把已有任务挤掉。正确做法是启动前先nvidia-smi确认空闲卡号再写入环境变量export CUDA_VISIBLE_DEVICES2,3 python -m vllm.entrypoints.openai.api_server --model Qwen/Qwen2.5-14B-Instruct --tensor-parallel-size 2 ...有个从经验里总结出来的细节CUDA_VISIBLE_DEVICES的值会影响卡号在程序内的映射。比如上面设了2,3在 vLLM 日志里看到的 GPU 编号就是 0 和 1而不是物理编号 2 和 3。排查问题时不要被日志里的编号骗了。4.2 量化模型的启动和参数选择热词里频繁出现 vLLM 部署 DeepSeek、运行 Qwen 系列这些大模型在消费级显卡上几乎离不开量化。vLLM 支持AWQ、GPTQ、FP8等主流量化格式启动时只要在--quantization参数里指定对应类型即可。vllm serve Qwen/Qwen2.5-7B-Instruct-AWQ --quantization awq这里需要特别提醒一点如果你下载的模型文件夹名称里带有AWQ、GPTQ后缀vLLM 有时能自动识别量化方式不传--quantization也能跑。但也有相当多的模型没有标准命名导致 vLLM 按默认的fp16方式加载结果要么显存翻倍放不下要么直接加载失败。最稳妥的办法是看模型的config.json里的quantization_config字段确认量化算法后再显式声明参数。FP8 是另一个被高频提及的方向尤其在 Blackwell 架构的卡上FP8 的计算吞吐比 FP16 高不少。启动 FP8 模型时要注意和 CUDA 版本的匹配前文提到的 CUDA 12.8 就是一个典型的 FP8 友好环境。如果启动时报算子不支持的错误多半是 PyTorch 的 CUDA 版本太老需要升级到 12.4 以上。5. 服务化接入OpenAI 兼容接口的对接细节既然热词里有 lm studio、ollama、vllm/sglang说明很多人其实是在同一台机器上对比不同的推理框架。考虑 vLLM 的部署方案时它提供的 OpenAI 兼容接口是最大的加分项你不需要为 vLLM 单独写客户端逻辑所有原来对接 OpenAI API 的业务代码只需要改一下base_url就能切换到本地模型。服务化对接时最容易犯的错是模型名不匹配。启动参数用了--served-model-name客户端却还拿 Hugging Face 的完整模型名去请求服务端会直接返回 404。建议把服务启动时打印的模型名记下来或者在curl http://localhost:8000/v1/models输出里确认一下再写代码。另外一个容易忽略的点是/v1/completions和/v1/chat/completions的区别。vLLM 两个接口都支持但非 Chat 模型只需用前者而 Chat 模型在两个接口下都能工作只是prompt的拼接方式不同。如果你把一个 Chat 模型通过 completions 接口调用服务端不会自动添加 Chat 模板输出往往是一段“嗯... 你好... ”这样没头没尾的文本仔细看日志会发现它根本没走对话格式。多客户端并发调用时需要注意超时设置。vLLM 一个典型的特征是模型如果排队的请求多了单个请求的响应时间会被明显拉长。有些模型在长序列生成时单个请求可能要几十秒才返回。客户端的 HTTP 超时时间给得太短比如默认的 30 秒就会出现“OpenAI 超时错误”但其实服务端还在正常处理的情况。生产上建议把timeout设成 300 秒以上同时用异步 HTTP 客户端配合合理的超时策略。6. 高频问题排查实录任何推理引擎在生产环境里跑久了都会遇到形形色色的问题。这里把我在实操过程中积累的排查经验整理成一个速查表再挑几个典型案例展开讲讲。6.1 高频问题速查表现象可能原因排查与解决办法启动即报 CUDA error: out of memorygpu-memory-utilization 设置过高或模型权重 KV Cache 超出显存降低 max-model-len或将 gpu-memory-utilization 降到 0.8或加 --enforce-eager报错提示缺少 Triton 相关算子vLLM 与 Triton 版本不匹配升级 vLLM 到最新版或重装匹配的 Triton优先用官方 wheel模型加载到一半卡住不动下载源网络问题设 HF_ENDPOINT 镜像或提前用huggingface-cli download下好权重请求报 model 不存在served-model-name 和请求体 model 字段不一致用 /v1/models 接口确认真实模型名生成速度突然变慢GPU 温度过高降频或并发请求过多相互竞争资源nvidia-smi 观察 GPU 利用率和温度必要时限制 max-num-seqsWindows 下 pip 装 vllm 失败官方对 Windows 的支持有限换 WSL2 环境或装 LM Studio社区版 vllm 兼容包具体可查官方 issue6.2 Windows 部署的特殊情况热词里提到“vllm windows 社区版”这个说法挺准确的。vLLM 官方对 Windows 的原生支持一直不太完善很多算子都不能直接在 MSVC 环境下编译。如果你非要在 Windows 上跑目前最靠谱的路线基本是两条用 WSL2 安装 Ubuntu 环境在 Linux 子系统里走常规 pip 安装流程。这样和原生 Linux 的差别最小CUDA 支持也完整。用 Docker Desktop 跑 NGC 容器这个方案适合不想折腾环境的用户。如果实在不想用 WSL2也可以关注 LM Studio 这类封装产物它把底层推理引擎封装成开箱即用的桌面应用不用手动处理 CUDA 环境适合本地体验模型但别指望它达到 vLLM 级别的吞吐优化。6.3 一个典型的 OOM 排查案例有一次我在一台 48GB 的 L40S 上部署 DeepSeek-R1-Distill-Qwen-14B启动参数是--gpu-memory-utilization 0.95 --max-model-len 8192。启动日志正常模型加载完毕但一发起请求就报 OOM。排查过程是这样的先用vllm serve不带请求时的日志确认 KV Cache 分配情况然后用nvidia-smi观察显存占用发现请求进来后显存直接顶满。进一步计算后发现14B 模型 FP16 权重本身就要占约 28GB加上 CUDA context 和激活值0.95的利用率下 KV Cache 能拿到的空间并不多而max-model-len 8192配max-num-seqs默认的 256意味着 KV Cache 理论上要给每个序列预留非常多的空间。最终把--max-num-seqs降到 32--max-model-len降到 4096问题解决。这里的教训是三个显存相关参数gpu-memory-utilization、max-model-len、max-num-seqs必须放在一起算总账不能只调一个就指望万事大吉。6.4 模型长时间启动无响应的处理还有一个高频问题vllm serve启动后卡在 “Loading model weights... ” 这一步很久不动看起来像是死机了。这通常不是真的死机而是大模型权重下载或从磁盘读取耗时较长。模型加载阶段日志输出较少很容易让人误判。正确做法是查看系统 IO 和网络带宽确认数据在持续读取中。如果是从 Hugging Face 下载最好先手动把模型文件下到本地再用本地路径启动这样既方便管理也能避免启动时网络抖动导致的失败。7. 框架选型vLLM、SGLang、Ollama、LM Studio 怎么选既然热词里同时出现了 vLLM、SGLang、Ollama、LM Studio这里就展开做个横向对比。选框架不是越强越好而是越匹配场景越好。框架核心定位适合场景劣势vLLM高性能生产级推理引擎服务化部署、高并发、多卡推理对 Windows 不友好配置相对复杂SGLang高性能推理 结构化生成控制需要复杂约束输出、Agent 场景生态不如 vLLM 成熟文档相对少Ollama开箱即用的本地模型工具个人电脑体验、快速跑模型性能调优空间有限并发能力弱LM Studio图形客户端封装推理Windows 用户本地体验模型不适合生产可定制性差简单说就是如果目标是把模型服务化给业务用vLLM 是首选如果做 Agent 场景、需要控制 JSON 输出结构SGLang 的约束解码能力比 vLLM 更精细如果只是个人电脑上体验一下大模型效果Ollama 或 LM Studio 的傻瓜式体验远比 vLLM 舒服。这里想重点说一个很多人忽略的点vLLM 和 SGLang 的区别不在于谁能跑得更快而在于社区生态。vLLM 的优势在于它是最早把 PagedAttention、Continuous Batching 这些技术完整落地并开源的项目周边工具、监控、K8s 集成方案都非常成熟SGLang 则在结构化生成和大规模多模态模型上做得很深入。两边的性能差距在大多数场景下其实是 10%~20% 的量级真正决定选型的往往是对相关生态的熟悉程度。8. 性能调优的几个方向启动阶段就该想清楚很多人以为性能调优是模型启动之后的事其实启动参数直接就决定了性能天花板。这里说三个可以在启动阶段就做好的事情。第一是算好显存账。启动前就把模型权重大小、激活值、KV Cache 三部分显存需求估算清楚。模型权重大小可以通过model.safetensors文件大小直接看到激活值和大 Batch Size 相关KV Cache 则主要被max-model-len和max-num-seqs控制。三者之和不要超过显卡总显存的 95%否则会遇到类似第 6 节的那种隐性 OOM 场景。第二是按需调整--max-model-len。这个参数不单是限制更是显存规划的一部分。如果你业务场景里用户输入平均只有 800 token输出平均 1000 token那max-model-len 4096完全够用没必要设置成 32768。设得越大KV Cache 需要预分配的空间就越多能支撑的并发就越少。第三是关注首 token 延迟和吞吐的权衡。vLLM 的 Continuous Batching 天然偏向高吞吐但在低并发场景下首 token 延迟可能不如专门的低延迟方案。如果你要做的服务对延迟敏感比如对话助手、实时翻译可以把--max-num-seqs调小一点避免太多请求挤在一起排队。你要是做离线批处理比如批量摘要、分类任务则可以相反把并发拉高、把吞吐打满。在显存充足的前提下还可以考虑增加--enable-prefix-caching参数。它的作用是缓存公共前缀的 KV Cache比如一个系统中所有请求都带有相同的 system prompt启用后系统 prompt 部分就不会重复计算。这个优化对多轮对话和路数固定的业务代码尤其有效能削掉不少不必要的计算开销。我在实际业务中开着它跑 chat 类模型整体吞吐提升了约 15%。9. 结合 Qwen 和 DeepSeek 的启动实践补充最后把两种当前曝光度最高的模型家族启动方式单独列一下因为它们的启动姿势确实有一些差异。9.1 Qwen 系列启动要点Qwen 系列在 vLLM 里的兼容性做得很好基本不用传额外参数。需要留意的是 Qwen2.5 系列中带Instruct后缀的模型是对话模型启动时不需要额外指定模板vLLM 会从tokenizer_config.json里自动识别而不带后缀的 base 模型则更适合做文本补全没有自动对话模板。如果你用 Qwen2.5-72B 这种大模型单卡基本放不下。前面说的多卡启动在这里就派上大用场vllm serve Qwen/Qwen2.5-72B-Instruct \ --tensor-parallel-size 4 \ --gpu-memory-utilization 0.90 \ --max-model-len 8192 \ --max-num-seqs 89.2 DeepSeek 系列启动要点DeepSeek 系列的情况稍微特殊一点。DeepSeek-V3 和 R1 系列模型体量巨大涉及 MoE 架构和特殊的模型分片方式。vLLM 对 MoE 模型的支持已经很成熟但启动时对显存的要求依然很高。以 DeepSeek-R1 的蒸馏版为例它跟 Qwen 的启动流程几乎一致直接传模型名即可。如果是完整的 DeepSeek-R1 模型那已经是另一套玩法了模型不是直接放显存的要考虑磁盘缓存、加载时间在内的全套部署方案。普通个人开发者或中小企业更建议先跑蒸馏版本对应的 vLLM 部署方案等流程完全跑通再考虑放大版本。这里也顺带提一下热词里那个略显古怪的“qwen3.8-flash-next”这多半是指某个针对 Qwen 的轻量化版本或社区魔改模型。不管模型名字看起来多花哨核心还是那几个启动参数--model指向本地路径或 Hugging Face 模型 ID其他参数照旧。9.3 启动脚本的沉淀与复用我个人的习惯是每个模型系列维护一份启动脚本放在固定目录并配上注释说明每台机器的显存规格和适用参数。这样换机器、换卡、换模型时不需要临时想参数改几个值就能应对。模型多了之后还会写一个简短的参数校验脚本启动前检查显存余量和模型路径是否存在避免临到启动才发现路径写错、磁盘不够的尴尬。最后再分享一个小技巧在vllm serve后面加上--verbose启动打印的日志会包含每个请求的排队时间和生成速度详情排障时非常有用。遇到模型生成为零等异常情况从这些日志里往往一眼就能看出瓶颈在哪。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进