ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

vLLM从0到1:安装部署与显存调优实战指南

vLLM从0到1:安装部署与显存调优实战指南 vLLM 这个名字2024 年下半年起只要是碰大模型推理的人多多少少都听说过。它解决的核心问题就一句话让大模型跑得更快、更省钱。同样是跑一个 70B 的模型用原生 HuggingFace Transformers 可能只能塞进一张 A100 且响应慢吞吞换成 vLLM 之后吞吐能翻几倍显存占用还能压下来一截。这不是魔法靠的是它的核心机制——PagedAttention分页注意力把 KV Cache 切成一块块小的物理块按需分配不像传统方案那样提前预留整个连续的显存空间。这篇文章不绕弯子直接按“从 0 到 1 上手”的顺序把 vLLM 的安装、模型启动、显存调优这些实操步骤拆开揉碎讲清楚。特别是显存这块我自己在 24GB 的 3090 和 80GB 的 A100 上都折腾过踩过不少坑比如 Qwen 系列模型默认配置下 OOM、并发一高就爆显存、批次大小调了半天反而变慢了……这些经验都会写出来。不管你是刚入门想拿自己显卡跑一个本地大模型还是做推理服务需要压性能这篇文章给你一套可以直接照着做的方案。下面直接进正题。1. 动手前的关键判断vLLM 适合什么场景不适合什么场景1.1 先搞清楚 vLLM 解决的真实痛点很多人一上来就装 vLLM结果发现自己的模型是小模型、或者自己只是要调个 API 玩完全感受不到它的优势。vLLM 的核心优化目标有两个第一提高吞吐量。它用 Continuous Batching连续批处理简单说就是不傻等一个请求的前向传播跑完再去接下一个而是让 GPU 满负荷干活时刻有数据在算。这个机制跟 CPU 的多线程很像——CPU 不是只跑一个进程而是不断切换让计算单元始终被喂饱。对大模型推理来说GPU 就是那个“CPU”如果一次只处理一个请求GPU 的利用率能低到惨不忍睹。vLLM 在这一点上能把单位时间内处理的请求数翻好几倍。第二降低显存浪费。传统推理框架会为每个请求预留一块完整的 KV Cache 空间但这个空间到底要多大完全是按最大序列长度算的实际用不用得完另一回事。vLLM 用 PagedAttention 按需分配就像操作系统的虚拟内存一样用到哪一块就映射哪一块。这对长上下文场景尤其重要输入一长KV Cache 的节省效果立竿见影。所以如果你只是跑跑小模型1B 以下、做一次性离线推理、或者不在乎 GPU 利用率vLLM 的优势体现不出来。但如果你是部署 Qwen2.5 7B/14B、DeepSeek R1 蒸馏版、或者开一个 OpenAI 兼容接口给多人调用vLLM 基本是首选方案。1.2 硬件环境要求和整体方案选型vLLM 对硬件有一个硬性前提必须有 NVIDIA GPU或者支持 CUDA 的 AMD GPU但配置麻烦很多并要求较新的驱动和 CUDA 版本。纯 CPU 环境跑 vLLM 不是不行但性能和官方支持都很有限真的只跑 CPU 的话更推荐直接用 llama.cpp 或者其他 CPU 推理方案。这里我以 NVIDIA GPU 为例给你一个直观的选型参考显卡型号显存大小适合尝试的模型规模RTX 3060 / 406012GB量化后的 7B 模型Qwen2.5-7B 可用 INT4 运行RTX 3090 / 409024GB7B/14B 模型正常精度FP16/BF167B 量化后可留更多并发余量A100 / A80040GB / 80GB32B/70B 模型量化或正常精度跑小批量L40S / H10048GB / 80GB70B 模型优先选择主要用于商用部署我自己最常用的是 24GB 显存的卡跑 Qwen2.5-7B-Instruct更新一点的 DeepSeek-R1-Distill-Qwen-7B 也能跑得很顺畅。这里有个很关键的认知显存决定了你模型的“上限”而 vLLM 决定的是同样模型和显存下你能榨出多少性能。如果你手头只有 8GB 显存比如 4060 笔记本版建议先用 GPTQ 或 AWQ 量化模型再上 vLLM不然玩起来会很痛苦。在方案层面安装 vLLM 有两条路pip 安装编译好的 wheel 包和Docker 镜像。两者没有绝对优劣取决于你的使用场景。如果是个人电脑直接 pip 最方便因为 Docker 在 Windows 上跑 GPU 需要 WSL2 加持网络和磁盘配置也可能绕一些路。如果是服务器环境或要交付一套标准服务那 Docker 更省心——依赖冲突、CUDA 版本问题一次打包解决我推荐你用镜像方式尤其是 vLLM 官方推出的vllm/vllm-openai镜像里面已经预装好了 OpenAI 兼容服务拉下来就能跑。2. 安装避坑实录从 pip 到 Docker 的完整正确姿势2.1 环境准备CUDA、Python 和 PyTorch 的三角关系很多人在 vLLM 安装这一步就被反复折磨最典型的症状是pip install vllm报错说什么编译不过或者好不容易装上一导入就提示 CUDA 版本不匹配。这些问题的源头往往不是 vLLM 本身而是PyTorch 的 CUDA 版本和 vLLM 的 CUDA 版本“打架”了。先说版本对应关系。vLLM 发布时会针对特定 CUDA 版本编译比如v0.6.x系列通常适配 CUDA 12.1你机器上nvcc --version显示的 CUDA Toolkit 版本可以略高但 PyTorch 的预编译包必须带匹配的 CUDA runtime。最稳妥的路径是先装好 NVIDIA 驱动再有 Python 3.10–3.12然后装 PyTorch 官方预编译版选择 cu121 或 cu118按 vLLM 文档要求来最后再 pip 装 vLLM。举个例子我的标准操作是# 1. 创建干净环境Python 版本建议 3.10 或 3.11实测最稳 conda create -n vllm python3.11 -y conda activate vllm # 2. 安装 PyTorch注意 index-url 指定 cu121 pip install torch2.5.1 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 # 3. 安装 vLLM pip install vllm这里有个容易踩的坑如果 PyTorch 装的是默认版CPU 版然后再 pip install vllm它给你装的是 pre-built binary导入时大概率直接报 CUDA unavailable 错误。还有人用 conda 装 pytorchvllm 的某些依赖比如flashinfer、xformers会和 conda 版产生 ABI 不兼容虽然不一定炸但排查起来很费时间。交互经验建议新手不要折腾源码编译安装 vLLM。源码编译需要 cmake、ninja、gcc还得拉一堆 Git submodule编译时间半小时起步中途一个环境变量不对就失败。除非你的 CUDA 版本特别新比如 CUDA 12.8导致官方 wheel 找不到对应版本否则不要走这条路。2.2 用 Docker 一次性搞定部署环境如果你是服务器操作强烈建议直接用 Docker。vLLM 官方维护了镜像仓库里面的每个 tag 都有明确的 CUDA 版本和 Python 版本标注拉下来就跑不用管宿主机上的 Python 环境。以现在用得比较多的版本为例docker pull vllm/vllm-openai:v0.6.3这个镜像自带vllm serve入口我把启动命令写在下面docker run --runtime nvidia --gpus all \ -v ~/models:/models \ -p 8000:8000 \ --ipchost \ vllm/vllm-openai:v0.6.3 \ --model /models/Qwen2.5-7B-Instruct \ --served-model-name qwen25 \ --port 8000要注意几个参数--ipchost是 vLLM 文档里明确建议加的因为它的 tokenizer 和模型加载要用共享内存默认 Docker 的 /dev/shm 只有 64MB跑大模型时数据共享很容易崩--gpus all是把所有 GPU 暴露给容器如果你有多卡但只想用其中一张就改成--gpus device0。很多人在 Windows 上用 Docker Desktop 跑 GPU 容器必须先开 WSL2 backend。如果没开你执行docker run会直接报could not select device driver with capabilities: [[gpu]]这基本就是 WSL2 没配置好或者驱动只装了 Windows 版但没装 WSL 版。Windows 下我其实更推荐直接用 WSL2 原生跑 conda性能损耗更小docker 的嵌套虚拟化还会进一步损耗性能。2.3 验证安装是否成功装完之后先跑一个最小验证python -c import vllm; print(vllm.__version__)能输出版本号说明基础环境没问题。再测一下能不能真正调用 GPU 跑推理python -c from vllm import LLM; llm LLM(modelQwen/Qwen2.5-0.5B-Instruct); print(llm.generate([Hello])[0].outputs[0].text)第一次跑这个命令会花一点时间下载模型并构建 CUDA graph之后就能看到输出。如果这中间有任何报错基本都是前面环境那一段的问题可以折回去重新检查。3. 启动服务的完整实操从命令行到调用测试3.1 vLLM 的两种启动方式CLI 直跑和 API ServervLLM 的启动分两种主流场景一种是脚本内直接调用LLM类做批量推理另一种是把它当作一个类似 OpenAI 的服务端跑起来用 HTTP 接口对外提供服务。先聊批量推理脚本。适合离线跑数据比如给一批文档做摘要或者批量生成文本。代码非常简单from vllm import LLM, SamplingParams llm LLM(model/models/Qwen2.5-7B-Instruct, tensor_parallel_size1) prompts [你好请介绍一下神经网络, 用一句话说明什么是注意力机制] sampling_params SamplingParams(temperature0.7, top_p0.9, max_tokens512) outputs llm.generate(prompts, sampling_params) for output in outputs: print(output.outputs[0].text)这种方式用来做数据处理很合适但生产环境一般不这么干——因为没有真正的并发管理API 层的限流、队列管理都得自己写。所以真正的部署场景还是用vllm serve或者直接跑 API Server。vllm serve是现在官方推荐的入口命令底层就是加载模型后启动一个 OpenAI 兼容的 HTTP 服务它长这样vllm serve /models/Qwen2.5-7B-Instruct \ --served-model-name qwen25 \ --port 8000 \ --host 0.0.0.0--served-model-name很重要。没有它的话默认的模型名会是路径最后一段比如Qwen2.5-7B-Instruct。如果你设了qwen25那么在调用接口时用的model字段必须写qwen25不然会返回 404。这一点翻车频率很高尤其是跟 FastChat、One-API 这类网关对接的时候模型名对不上报错还特别隐晦。启动成功后日志里会打印类似下面的信息INFO 06-30 12:00:00 api_server.py:345] Starting vLLM API server on http://0.0.0.0:8000 INFO 06-30 12:00:00 api_server.py:346] Available routes are: ...看到Available routes就意味着服务已经可以接收请求了。这时用 curl 测一发curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d {model:qwen25,messages:[{role:user,content:你好}],max_tokens:64}正常响应会返回一个包含choices和usage字段的 JSON跟 OpenAI 的格式几乎一致。这一步通了就能直接接入 LangChain、Dify、FastGPT 这类工具或者自研应用不需要任何适配层。3.2 加载模型时的关键参数别裸奔先配好这些启动服务看着简单一敲命令就行但其实默认参数只在“能跑通”的层面安全性能远不是最优。我建议第一次启动就加上这几个参数vllm serve /models/Qwen2.5-7B-Instruct \ --served-model-name qwen25 \ --max-model-len 8192 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 32逐个解释下--max-model-len 8192这是允许的最大序列长度输入的 prompt 长度加上生成的输出长度。vLLM 默认会读模型的max_position_embeddings但有些模型这个值设得很大比如 32K、128K如果不手动限制它就会为超长序列预留 KV Cache直接把显存拉爆。这个参数是显存不够用时的第一道闸门。--gpu-memory-utilization 0.9允许 vLLM 最多用多少比例的显存默认是 0.9。如果你的服务是独占显卡可以把 0.9 推到 0.95但如果显卡上还要跑别的东西比如同时又要做数据预处理、又要跑 Stable Diffusion这个值就要往下调否则会互相抢显存导致 OOM。--max-num-seqs 32一次最多同时处理的序列数决定并发上限。这个值太小吞吐上不去太大会吃掉更多显存而且每个请求排队时间变长。要根据平均请求长度来调这个跟显存调优直接相关下一章展开讲。另外一个很实用的参数是--enable-prefix-caching或--enable-prefix-caching具体看版本命名它会缓存 prompt 相同前缀的 KV Cache。如果你做的场景是 RAG 或者客服问答——用户问题前面总是带着系统提示语和大段背景资料——这个功能几乎必开。开一次之后相同前缀的请求推理速度能肉眼可见地提升。3.3 多卡推理怎么启动如果你的模型一台卡放不下比如 70B 模型40GB 单卡肯定不够海光、A100 40G 都勉强这时要用多卡张量并行。vLLM 的做法是vllm serve /models/Qwen2.5-32B-Instruct \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.9--tensor-parallel-size 2表示把模型切到两张卡上并行跑显存和算力都翻倍。注意一个前提多张卡之间要用 NVLink/Sense 等高速互联如果只有 PCIe 互联性能会因为通信开销打折但也能用主要是延迟高一些。多卡另一个常见坑是 GPU 没对齐端。比如有两张卡一张是 24GB 的 3090一张是 12GB 的 3080tensor_parallel_size2会强行启用两张卡但因为显存不一致较小的卡会先被打满导致 OOM。多卡并行要求卡型号和显存基本一致混插情况下建议用CUDA_VISIBLE_DEVICES0强制只选大的卡或者干脆单卡跑量化版模型。4. 显存调优从 OOM 到性能榨出的实战路径4.1 理解显存花在哪里模型权重和 KV Cache显存调优的前提是先搞清楚显存里装了什么。主要两块模型权重和KV Cache。模型权重相对固定。以 Qwen2.5-7B 为例参数总量约 7.6B默认 FP16/BF16每个参数占 2 字节那模型权重就要吃掉大约 15.2GB。如果你再用--quantization awq启用 4bit 量化权重降到 4GB 左右但推理精度会有轻微损失。模型权重的显存是跑不掉的先算清楚这块剩下的空间才是你可以拿来调节 KV Cache 的余量。KV Cache 的计算公式网上有很多版本我给你一个最直观的经验式KV Cache 显存 ≈ 2KV 的两部分× layers层数× hidden_size隐藏层大小× max_tokens总序列长度× 精度字节数换算下来对 7B 模型、max_tokens 设为 8192、BF16 精度KV Cache 大概需要 7GB 到 10GB 的空间。这还不算激活值等临时空间。所以 24GB 显卡跑 7B 模型如果不对 max_model_len 做限制默认 32K 上下文会导致 KV Cache 直接要到 40GB 以上必炸。那调优的本质就清晰了要么降低模型权重体积量化要么限制序列长度和并发数缩小 KV Cache 冗余要么提升显存利用率把能用的都用上。4.2 gpu-memory-utilization 和 max-model-len 怎么配合实操层面我最常调的一对组合就是--gpu-memory-utilization和--max-model-len。拿 24GB 显存跑 Qwen2.5-7B-Instruct 为例一步步给你算固定权重占用模型权重约 15.2GB。预留激活值等临时空间大约 1.5GB 到 2GB。剩余可用给 KV Cache24 × 0.9 - 15.2 - 1.8 ≈ 4.6GB。在这个空间内如果你的 max_model_len 设为 4096KV Cache 约 4.8GB勉强合适但如果你把 max_model_len 拉到 8192KV Cache 直接翻倍到 9GB 左右超出剩余空间启动时就会报Not enough memory。看到这个问题解决方案有两条路把--max-model-len降为 4096保证 KV Cache 足够。用 AWQ/GPTQ 量化把权重压到 4.5GB 左右省下 10GB 给 KV Cache就可以支持 8192 甚至 16384 的上下文。所以我的建议是先想清楚你这服务的业务场景需要多长上下文。客服和 RAG 场景输入资料长输出短max_model_len 可以设大一点对话场景用户问题短输出一段话4096 都够了。盲目拉长 max-model-len只会白白吃掉显存降低并发能力。4.3 生产环境实测一张 24GB 卡能支撑多少并发这是很多同学最关心的问题我的 24GB 卡跑 7B 模型到底能支撑多少人同时聊天结论先放前面不是看你注册了多少在线用户而是看你的峰值并发请求有多高。我把一次实测的压测数据列出来供你参考我用的是 Qwen2.5-7B-InstructBF16约 15GB 权重max_model_len4096gpu_memory_utilization0.92max_num_seqs64enable_prefix_cachingTrue。用 locust 模拟 50 个并发用户每个用户发一个流式请求平均输出长度约 300 tokens。实测下来qps每秒完成请求数稳定在 4 到 6 之间平均每个请求的首 token 延迟TTFT约 300ms。如果把max_num_seqs调回 8qps 掉到 1 到 2单请求的生成速度会快一些但整体吞吐明显下降——因为GPU 没吃饱每个请求之间有大量空闲时间。这就是最核心的调优逻辑吞吐和首 token 延迟是一对矛盾。vLLM 默认注重连续批处理倾向于把批塞满来最大化吞吐但塞得越满排队中的请求等待时间就越长。需要低延迟场景比如在线对话max_num_seqs不要设太大需要高吞吐场景比如离线批量生成可以调高它。还有一个隐性因素是max_tokens。如果每个请求的输出都限制到 128 tokens那并发能力完全不是一个量级。做 RAG 的回复模型和做长文生成的模型同一张卡实际能支撑的并发差别能到 3 到 4 倍。有个重要决策点max_num_seqs 和 max_model_len 不是独立参数两者同时影响显存占用。vLLM 会在 kvcache 空间和 max_num_seqs 之间综合权衡一次并发多每个序列分到的 KV 空间就少导致长请求失效或者排队。所以增大 max_num_seqs 的时候要注意监控输入输出长度是否接近 KV Cache 上限。4.4 量化是显存问题的第二答案除了调参数另一个立竿见影的手段是量化。vLLM 支持 AWQ、GPTQ、FP8 等量化格式。对于个人玩家我强烈推荐用现有量化模型比如在 HuggingFace 上搜Qwen2.5-7B-Instruct-AWQ很多都已经量化好了直接用vllm serve /models/Qwen2.5-7B-Instruct-AWQ \ --quantization awq \ --max-model-len 8192 \ --gpu-memory-utilization 0.95AWQ 权重通常只有原来的一半到三分之一以 7B 为例手动从 fp16 的 15GB 降到 4.5GB省下的显存全给 KV Cache。你在 24GB 卡上就能支持 8192 上下文的同时保持较高并发体验完全不同。这里要注意一点量化不是无损的AWQ 在常见评测集上一般只掉 0.5% 左右但个别生成质量敏感的任务比如翻译、数学题、代码生成还是能感觉到差异。所以我一般建议先跑 FP16确认能跑通、效果满足需求再试 AWQ 量化版如果评测结果差异可接受就用量化版上线性能收益真的很大。5. 常见问题排查与避坑技巧速查5.1 典型错误对照表从安装到启动再到调优我把最常遇到的问题整理成一张表都是实测中遇到过的错误现象根本原因解决方式ImportError: libcudnn.so.8: cannot open shared object file系统 CUDA/cuDNN 版本太老或没装升级驱动用 conda 环境装 cudnn或直接用官方 Docker 镜像Not enough memory. remaining kv cache: 0显存被权重占满KV Cache 没有空间减小 max-model-len量化权重或调低 gpu-memory-utilization 对比验证ValueError: The models max seq len (32768) is larger than maximum number of tokens...模型支持长序列但显存不够分配显式指定--max-model-len降低到 4096/8192服务能启动但请求很慢TTFT 几秒没有开 prefix caching或者 max_num_seqs 太小加上--enable-prefix-caching适当提高 max_num_seqs多卡启动时报CUDA_VISIBLE_DEVICES相关错误或卡找不到显卡驱动只认了部分卡或没有指定 GPU 序号用CUDA_VISIBLE_DEVICES0,1按实际卡号限定RuntimeError: NCCL error: unhandled cuda error多卡通信异常常见原因是驱动或容器网络不行重启容器检查驱动确认 nvidia-smi 输出正常V100 等老卡跑不了最新版 vLLM新版本对 compute capability 8.0 以下显卡的支持弱了用 vLLM 0.4.x 或 0.5.x 老版本或者换 RTX 3090 及以上这张表不是让你背下来而是给你排查思路碰到问题先看它发生在哪个阶段——是导入库、加载权重、还是跑第一个 token不同阶段对应的问题方向完全不同。5.2 排查方法论三步定位法我自己处理 vLLM 报错时有一套“三步定位法”基本不会乱第一步看 nvidia-smi 确认显存和进程状态。如果显存占用奇高但没有推理请求在跑说明权重加载有问题或者上一个进程没退出僵尸进程占显存。用kill -9处理残留进程之前先nvidia-smi检查 PID。第二步看 vLLM 的启动日志里的 WARNING 和 ERROR。vLLM 的日志写得很详细启动时会清楚告诉你每个参数决定的显存分配情况。比如这句INFO 06-30 12:00:00 model_runner.py:543] Starting vLLM using 15.2 GB for model weights and 5.1 GB for KV cache.这就明明白白告诉你了权重吃 15.2GBKV Cache 留了 5.1GB剩余自由空间一目了然。很多时候你看一眼日志就能定位问题。第三步用极小配置复现。把 max-model-len 调到 512gpu-memory-utilization 调到 0.4如果这样就能跑通说明问题出在配置太高而不是代码。之后逐步放大参数找到临界值。这是最有效的定位方法。5.3 Windows 社区版和 WSL 场景的经验热词里提到了 “vllm windows 社区版”也确实是很多 Windows 用户想尝试的方向。vLLM 官方本身没有 Windows 支持但社区有基于 WSL2 的跑法。我在 Windows 11 上亲测过注意了下面这段讨论的是如何在 Windows 上通过 WSL2 正常使用 vLLM不涉及任何特殊网络手段。只要 WSL2 是标准开启状态然后安装 CUDA 驱动时勾选 WSL 支持就能在 WSL 里用 pip 安装 vLLM。但说实话如果你只是想在 Windows 上体验一下还有个更轻的选择Ollama 或 LM Studio。它们底层也能调用 GPU部署速度更快但为了最大吞吐和并发能力还是建议——前提是 WSL2 已经稳定开启了才行。实测下来 WSL2 vLLM 的性能损耗大概在 5% 到 10% 之间相比 Docker Desktop 的损耗还是更推荐前者。但如果你要长期做推理服务我会强烈建议直接用 Linux 服务器。6. 不只是启动还要学会调度与监控最后补一个实用话题既然你已经把 vLLM 跑起来了就顺手把 GPU 和服务的监测也做了不然显存调优就成了瞎调。最基本的工具是nvidia-smi每隔几秒输出一次显存和使用率watch -n 1 nvidia-smi这条命令能让你实时看到 GPU 利用率、显存占用和显存温度。当 vLLM 跑请求时GPU-Util 应该在高位波动最好超过 80%如果 GPU-Util 一直趴在 30% 以下说明没跑满通常是 max_num_seqs 太小、模型量化后计算太轻导致数据搬运占了主导或者请求太短计算密集度太低。再进阶一点vLLM 的--enable-metrics会暴露/metrics端点里面有很多 Prometheus 指标比如vllm:num_requests_running当前正在跑的请求数、vllm:gpu_cache_usage_percKV Cache 使用比例。接上 Grafana 就能做服务看板。不过在单机开发阶段先用nvidia-smi 日志足够了。还有一点值得提醒进程挂了不要急着重启先free -h看内存、nvidia-smi看显存确认是资源耗尽还是代码问题。如果反复 OOM我最大的体会是——别死磕参数了先换个量化模型试试。AWQ 模型对显存压力几乎是降维打击很多时候参数调了半天不如直接上一个量化版一劳永逸。我在实际使用中发现一个很有意思的细节生产环境里真正限制 QPS 的往往不是 GPU 算力而是显存里 KV Cache 分块策略和 max_num_seqs 的配置。同样的推理延迟下vLLM 对“快速问答”这类短请求的处理能力几乎是连续推理框架的两倍但对长文本生成场景优势就没那么夸张。所以如果你的业务是长文档总结别指望 vLLM 解决所有性能问题该并发排队还是排队。这几年的实践让我逐渐意识到弄懂 vLLM 的核心不是背命令而是理解显存分配的整个过程和推理调度的逻辑。参数记住了会忘但把“权重固定、KV Cache 动态、量化按需、并发权衡”这四件事想明白换任何模型你都能很快找到自己的配置。希望这篇里的经验和坑能帮你少走我走过的弯路。
返回列表