简介:这是一份围绕大模型部署实战的开源资料包,聚焦如何基于vLLM框架部署通义千问Qwen语言模型,适合有Python基础、希望快速上手推理服务搭建的AI开发者和运维人员。包内共9个文件,包括6个Python脚本、2张示意图和1份README说明,其中vllm_server、vllm_client、vllm_wrapper、vllm_offline等脚本分别覆盖服务启动、客户端调用、封装对接与离线推理,配合gradio_webui可快速搭建可视化交互界面。压缩包仅433KB,结构紧凑,流程教程从环境依赖、模型加载到接口测试与性能调优均有涉及。资料已有1488人学习,内容经过筛选验证,适合作为从源码到服务的完整参考模板,帮助读者少走弯路。
1. 用vLLM部署通义千问Qwen大语言模型,先把账算清楚再动手
基于vLLM部署通义千问Qwen大语言模型这件事,听起来是一行命令的事,实际落地时卡人的全是显存、上下文长度和并发调度这些细账。vLLM选对了,Qwen从0.5B到72B能在一台或几台机器上跑成OpenAI兼容服务,给RAG、Agent、内部工具链当底座;选错了,服务起不来、吞吐上不去、长对话崩,全得靠日志猜。这篇文章面向两类人:手里有卡想本地部署大模型并交付给团队调用的工程师,以及正在比较vLLM和ollama、llama.cpp、SGLang这几个框架的选型者。我会把从选型到压测的全流程拆开写,每步都能照做,坑也一并标出来。
2. 部署前先选型:Qwen型号、vLLM版本和CUDA环境的三角关系
部署大语言模型最忌讳一上来就pip install vllm,然后随便拉一个模型开始跑。vLLM的调度策略、KVCache管理和量化支持都很吃环境和模型格式的匹配,提前把这层关系理顺,后面所有命令都是顺水推舟。选型就三件事:选哪个Qwen、装哪个vLLM、从哪下模型。
2.1 Qwen型号怎么选:0.5B到72B的显存账本
Qwen 2.5系列从0.5B一直排到72B,选型的第一原则是先看手里显存,再定模型。FP16精度下模型权重约占参数量的2倍字节:7B模型权重大约15GB,加上KVCache和中间激活,24G单卡能跑但余量不大;14B权重约28GB,基本要40G卡起步;32B权重约64GB,单卡只有靠量化,否则得上两张80G做张量并行。72B就别想单卡了,至少四张A100/H100,或者直接上AWQ量化把权重压到4bit。下表是我按FP16权重估算的选型参考,不含KVCache和激活开销:
| 模型 | 权重估算 | 推荐显存 | 常见场景 |
|---|---|---|---|
| Qwen2.5-0.5B-Instruct | 约1GB | 4~8G | 边缘设备、CPU兜底 |
| Qwen2.5-1.5B-Instruct | 约3GB | 8G | 简单对话、文本分类 |
| Qwen2.5-3B-Instruct | 约6GB | 12G | 轻量Agent、函数调用 |
| Qwen2.5-7B-Instruct | 约15GB | 24G | 本地部署的主流甜点 |
| Qwen2.5-14B-Instruct | 约28GB | 40G/A30 | 需要更强推理质量 |
| Qwen2.5-32B-Instruct | 约64GB | 多卡或量化后40G | 高质量私有底座 |
| Qwen2.5-72B-Instruct | 约144GB | 多卡80G | 企业级大规模服务 |
注意这里推荐的是Instruct版本,它自带对话模板,vLLM加载后会按模板组织prompt。Base版本是纯语言模型,接对话服务还要自己拼模板和停词,新手直接用Instruct最省事。至于上下文长度,Qwen 2.5系列官方标称128K,但本地部署能不能开满,取决于KVCache预分配和显存余量,这个在第3章参数部分细说。
2.2 vLLM与CUDA版本:版本差一档,服务起不来
vLLM是编译型依赖,它对PyTorch和CUDA运行时的版本很敏感。最省事的做法是直接用官方Docker镜像,镜像里CUDA、torch、vLLM三者已经匹配好;如果选择pip安装,先确认nvidia-smi显示的驱动能支撑目标CUDA版本,再建虚拟环境装。比如CUDA 12.4要求驱动版本不低于525,驱动太老,torch能import但一加载模型就报CUDA error。
# 第一步:确认驱动和CUDA版本,记录下nvidia-smi里的Driver Version nvidia-smi # 第二步:新建Python虚拟环境,避免和系统环境互相污染 python -m venv .venv source .venv/bin/activate # 第三步:安装vLLM,它会连带安装匹配的torch pip install vllm # 第四步:验证vLLM能正常import,并打印版本号 python -c "import vllm; print(vllm.__version__)"这四步里最容易翻车的是第三步:pip为了满足依赖可能会把torch升级到和你驱动不匹配的版本。所以我的习惯是装完后立刻跑第四步,如果import报CUDA相关错误,先回退torch版本,而不是去动驱动。驱动升级代价大、影响面广,尽量通过固定vLLM版本和torch版本来匹配现有驱动,这是生产环境更稳妥的做法。另外,vLLM官方文档标注了每个版本对应的CUDA和Python版本范围,安装前花两分钟对照一下,能省掉后面一整天的排错。
2.3 模型从哪下、下什么格式:ModelScope与HuggingFace的取舍
模型下载渠道直接决定你部署的顺畅程度。HuggingFace是模型最全的地方,但国内网络环境下载大模型经常断流;ModelScope上有Qwen官方账号同步的全部版本,国内下载速度快很多,而且命令行工具支持断点续传,适合几GB到上百GB的模型。实际操作中我的默认选择是ModelScope,除非需要某个只在HuggingFace上发布的第三方微调版本,才考虑从HuggingFace拉。
# 先安装ModelScope客户端 pip install modelscope # 下载Qwen2.5-7B-Instruct到当前目录 # --model指定仓库名,--local_dir指定本地目录名 modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir ./Qwen2.5-7B-Instruct参数说明:--local_dir如果不写,模型会落到ModelScope的默认缓存目录,部署时还要去翻缓存路径,建议每次都显式指定。下载完成后检查目录里的config.json和safetensors文件是否齐全,如果中断过,重新执行同一条命令,断点续传会自动补齐缺失分片。另外格式上要特别注意:vLLM原生支持HuggingFace的safetensors格式和AWQ/GPTQ量化格式;GGUF是ollama和llama.cpp用的格式,不能直接喂给vLLM。如果你以前用ollama本地部署大模型比较多,习惯了GGUF的方便,换到vLLM时模型源要从头重新下,这是很多人卡住的地方。
3. 最小部署命令:一行命令把Qwen部署成大模型服务
选型做完,下一步就是把模型跑起来。所谓项目源码和流程教程,核心其实就是这一章的启动命令、参数配置和请求脚本,把它们按顺序存下来,就是一份完整可复用的部署方案。我会用Qwen2.5-7B-Instruct配合单张24G显卡演示最小可运行配置,然后再把三个最容易改错的参数单独拆开讲。
3.1 启动OpenAI兼容服务的最小命令
vLLM提供了OpenAI兼容的API Server,这意味着你原来写给GPT的代码只需要改base_url就能切到本地Qwen。启动命令如下:
# 最小启动命令,在项目目录下执行 python -m vllm.entrypoints.openai.api_server \ --model ./Qwen2.5-7B-Instruct \ --served-model-name qwen2.5-7b \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 32768 \ --gpu-memory-utilization 0.9这段命令的逻辑:--model指定模型目录,本地路径和HuggingFace仓库名都可以;--served-model-name是对外暴露的模型名,客户端请求时model字段要和它一致;--host 0.0.0.0让服务监听所有网卡,这样局域网内其他机器也能访问;--max-model-len控制最大上下文长度,我设了32K而不是128K,因为7B模型开满128K的KVCache会直接撑爆24G显存;--gpu-memory-utilization设置显存利用率上限。
--host 0.0.0.0是部署服务的常用做法,但要注意安全,如果机器有公网IP,建议配合防火墙或内网环境使用。启动成功的标志是日志里出现Uvicorn running on http://0.0.0.0:8000,同时会打印模型名称和GPU显存分配信息。新版vLLM也支持vllm serve短命令,效果和上面一样,但传参语法略有差异,用vllm serve --help先看一眼再写。
3.2 三个必调参数的边界:max-model-len、gpu-memory-utilization和tensor-parallel-size
这三个参数是vLLM部署大模型时最容易改出问题的,我把它们的行为边界列成表,对照着调会快很多。
| 参数 | 作用 | 常见值 | 调大后的代价 |
|---|---|---|---|
max-model-len | 最大上下文字符数 | 8192~32768 | KVCache按此预分配显存,设太大直接OOM |
gpu-memory-utilization | vLLM可用的显存上限 | 0.85~0.92 | 接近1时挤占框架预留空间,推理报错 |
tensor-parallel-size | 张量并行卡数 | 2/4/8 | 卡间通信开销剧增,小模型反而变慢 |
max-model-len的原理是vLLM启动时按这个长度预分配KVCache,不是按实际对话长度动态增长。所以你设64K但实际只用2K,显存照样被预占掉。这也是为什么很多人部署Qwen时不敢开满128K,因为7B模型在24G卡上开满128K,权重加KVCache直接超了。实用做法是:先在24G卡上从16384开始,能稳定跑再往上加。
gpu-memory-utilization设到0.95以上风险很大,vLLM除了模型和KVCache,还需要给CUDA context和框架本身留余量。低于0.8又浪费显存,我一般默认0.9,遇到多并发报显存错误再往0.85降。注意这个参数控制的是vLLM进程的显存水位,如果同一张卡上还有别的任务,要按比例调低。
tensor-parallel-size只在多卡时用,原理是把模型参数切到多张卡上并行计算。但7B这种规模的模型,单卡能放下时加TP只会增加NCCL通信开销,吞吐反而下降。这个参数的正确用法是:模型单卡放不下时才开启,比如32B模型在两张40G卡上设2。另外tensor-parallel-size必须整除实际卡数,设错会在启动时报GPU vLLM tensor parallel size相关错误。
3.3 用OpenAI兼容接口验收部署:curl探活与Python请求
服务起来后,先别急着接业务,用OpenAI客户端协议验一遍接口通不通。第一条命令探活,看模型是否注册成功:
# 查看服务端注册的模型列表 curl http://localhost:8000/v1/models/v1/models会返回JSON列表,里面包含你设置的qwen2.5-7b。如果返回404或空列表,多半是--served-model-name拼写问题,或者服务还没完全就绪。日志出现ready字样后再探活比较稳。接着发一个chat补全请求:
# 发送一条对话补全请求,验证生成链路 curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen2.5-7b", "messages": [{"role": "user", "content": "用一句话介绍vLLM"}], "max_tokens": 256, "temperature": 0.7 }'这个请求走的是OpenAI的chat协议,vLLM内部会把messages按Qwen的chat模板拼装后送进模型。max_tokens限制生成长度,temperature控制随机性,这两个字段和OpenAI的含义完全一致,你的业务代码几乎零改动就能切过来。如果返回一个带choices[0].message.content的JSON,说明链路通了。接下来用Python脚本化验证,方便后面集成到自动化测试里:
from openai import OpenAI # base_url指向vLLM服务,api_key随意填,vLLM默认不做鉴权 client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", ) resp = client.chat.completions.create( model="qwen2.5-7b", messages=[{"role": "user", "content": "用一句话介绍vLLM"}], max_tokens=256, temperature=0.7, ) print(resp.choices[0].message.content)这段代码里api_key="EMPTY"是vLLM的默认行为,它不校验key内容,但字段必须带上,否则openai客户端会报401。如果你后续要给服务加鉴权,vLLM也支持通过--api-key参数开启简单校验,生产环境建议开启,不然内网里谁都能白嫖你的算力。部署验证到这里就算完成,接下来要回答一个更关键的问题:这个服务到底能扛多大并发、吞吐达不达标。
4. 部署完了怎么验收:吞吐、首Token延迟与并发压测三条线
很多教程到服务启动成功就结束了,但作为交付项目,你还需要给团队一份性能数据,否则别人不敢把流量切过来。这一章讲三条验收线:单请求硬吞吐、并发表现、运行时监控。这三条线分别对应模型本身跑多快、服务扛不扛得住、以及瓶颈在哪。
4.1 用vLLM自带的benchmark脚本打一遍硬吞吐
vLLM安装包里带了吞吐基准脚本,它绕过HTTP层直接驱动引擎,测的是模型和框架的纯生成能力,适合评估单机上限:
# 测硬吞吐:100条prompt,输入1024 token,输出512 token python -m vllm.benchmark.benchmark_throughput \ --model ./Qwen2.5-7B-Instruct \ --input-len 1024 \ --output-len 512 \ --num-prompts 100 \ --trust-remote-code参数逻辑:--input-len和--output-len模拟真实对话的长度分布,--num-prompts控制并发prompt数量。脚本会在结束前打印Throughput: xx tokens/s,这就是单机硬吞吐。比如Qwen2.5-7B-Instruct在单张24G卡上通常能跑到每秒几千token,这个数字可以作为后续调优的基准线。如果脚本报No module named vllm.benchmark,说明你的vLLM版本改了路径,跑python -m vllm.benchmark --help看可用子命令,或者直接升级到新版本。
注意benchmark脚本和你实际服务的数字会有差距,因为脚本不经过网络栈和OpenAI协议转换,也不受max-num-seqs等调度参数限制。它的作用是快速验证硬件和框架本身有没有问题,比如两张卡对比时,一张卡吞吐异常低,那肯定是散热或驱动问题,而不是代码问题。
4.2 自己写并发压测:看调度排队和超时表现
硬吞吐测完,还要测服务在真实HTTP并发下的行为。核心指标是首Token延迟TTFT和每个请求的端到端耗时。下面这段脚本用AsyncOpenAI并发发20个请求,统计总耗时和P50延迟:
import asyncio import time from openai import AsyncOpenAI # 异步客户端,配合asyncio实现并发请求 client = AsyncOpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", ) async def one_request(i: int): t0 = time.time() resp = await client.chat.completions.create( model="qwen2.5-7b", messages=[{"role": "user", "content": f"第{i}个请求,请只回复'收到'"}], max_tokens=64, temperature=0.3, ) return time.time() - t0 async def main(): start = time.time() times = await asyncio.gather(*[one_request(i) for i in range(20)]) total = time.time() - start times.sort() print(f"总耗时: {total:.2f}s") print(f"P50延迟: {times[len(times)//2]:.2f}s") print(f"最慢请求: {times[-1]:.2f}s") asyncio.run(main())这段脚本的关节在于asyncio.gather会把20个请求同时打到vLLM,vLLM的调度器收到后会按continuous batching策略把请求塞进同一个batch。判断部署质量的标准是:总耗时接近单请求耗时的1.5到2倍算健康,如果20个并发让总耗时变成单请求的10倍以上,说明调度或显存已经到瓶颈。想要更细的首Token延迟数据,可以把stream=True打开,用流式响应掐表算首包到达时间,那才是用户真实感知的响应快慢。
4.3 从/metrics读KVCache利用率和排队数
vLLM提供了Prometheus格式的metrics端点,一条curl就能拉到运行时的核心状态:
# 拉取运行时指标,配合grep过滤关键项 curl -s http://localhost:8000/metrics | grep -E "gpu_cache_usage_perc|num_requests_waiting|num_requests_running"三个指标的含义:gpu_cache_usage_perc是KVCache使用率,长期接近100%说明并发已经顶到显存上限,再增加请求只能排队;num_requests_waiting是等待中的请求数,这个值持续大于0说明prefill或decode处理不过来;num_requests_running是正在跑的请求数,正常应接近--max-num-seqs的设定值。这几个指标也对应eenginecore与scheduler、executor交互流程的外部表现:scheduler决定谁能进batch,executor真正跑CUDA kernel,metrics反映的就是排队策略是否健康。压测时把这几个数和第4.1节的硬吞吐对照看,就能区分瓶颈在调度还是显存还是模型本身。
5. vLLM部署Qwen避坑指南:五个常见的翻车点与解法
这一章写的是我在vLLM部署Qwen过程中真实遇到过的踩坑记录,按“现象→原因→解决”的格式整理。你大概率会踩中其中一两个,先看目录再对号入座,能省半天排错时间。
5.1 显存明明够,服务却报No available memory
现象:24G卡部署Qwen2.5-7B-Instruct,按网上推荐的参数设--gpu-memory-utilization 0.95 --max-model-len 32768,启动时报No available memory for the cache blocks,然后进程退出。
原因:KVCache预分配不是按模型权重算的,而是按max_model_len × 最大batch数 × 层数 × 头数计算的,vLLM默认附带--max-num-seqs和预填充token数相关的并发余量。0.95的显存利用率看似留了5%,但加上CUDA context和PyTorch预留,预分配计算时已经超额。
解决:把--gpu-memory-utilization降到0.85,--max-model-len从32768降到16384,启动后再慢慢往上调。这是vLLM部署大模型最经典的玄学现场,遇到OOM先降这两个值,不要想着去改PyTorch的显存池配置,那是改不动的。
5.2 多卡部署后吞吐不升反降
现象:拿两张24G卡部署Qwen2.5-7B-Instruct,--tensor-parallel-size 2启动成功,但跑第4.1节的benchmark,吞吐比单卡还低20%。
原因:张量并行要把层参数切到多卡,每次forward都要跨卡同步,7B模型本身单卡能装下时,通信开销大于并行收益。vLLM社区里这个现象很常见,模型规模越小越明显。
解决:7B模型用单卡跑,不要把显存浪费在并行上。张量并行只留给32B以上或单卡放不下的模型。如果你的目标是提升Qwen-7B并发能力,正确做法是部署两个独立vLLM实例在前端加负载均衡,而不是开TP。多卡唯一靠谱的场景是单卡装不下同一个模型,此时TP才划算。
5.3 长对话到一半报input too long或输出乱码
现象:对话变长后请求返回带input too long的错误,或者生成到一半内容变成重复的乱码,看起来像模型“傻了”。
原因:--max-model-len设成了8192,而messages拼接后的token数超过这个值,vLLM直接拒绝请求。乱码则是另一种情况:上下文长度正好卡在边界,模型在截断位置继续生成,注意力分布被切坏的KVCache干扰。
解决:把--max-model-len调到模型支持的上限内。Qwen2.5-7B官方支持128K,但24G卡开不到那么长,可行方案是降并发或换AWQ量化版本省出显存给KVCache。一句话原则:实际服务里的最大prompt长度建议不超过--max-model-len的80%,留出生成空间,否则输出阶段随时可能撞墙。
5.4 换版本、换镜像后性能下降或直接起不来
现象:升级vLLM一个小版本后,同样的Qwen模型、同样的参数,benchmark吞吐掉了10%到15%;或者把docker镜像从固定版本换成latest后,加载模型时报算子不匹配错误。
原因:vLLM版本迭代时,attention backend和调度逻辑经常调整,flash-attention版本也不断更换。“vllm新版本性能下降”在社区是高频话题,新特性引入回归并不罕见。生产环境追latest等于让版本漂移帮你挑风险。
解决:锁版本号。用docker镜像时固定tag,比如vllm/vllm-openai:0.6.2,不要用latest;pip安装时在requirements里写好vllm==对应版本。升级前先看release notes里有没有涉及scheduler逻辑和attention实现的变更,再决定要不要升。社区里也经常看到“GLM用什么版本的vLLM镜像”这类问题,答案永远是:去看模型卡上标注的测试版本,而不是自己猜新版更兼容。另外docker启动后秒退,日志只留一行错误,多半是挂载路径问题:
# 用固定版本镜像和显式挂载路径,避免相对路径坑 docker run -d --gpus all \ -v ~/models:/model \ -p 8000:8000 \ vllm/vllm-openai:0.6.2 \ --model /model/Qwen2.5-7B-Instruct \ --served-model-name qwen2.5-7b这个例子把模型目录显式挂载到容器内的/model,容器内路径必须和--model参数完全一致,相对路径在容器里经常解析到不存在的目录,导致“秒退”假象。日志里如果出现Error loading model,第一反应查挂载,而不是查模型。
5.5 并发一高首Token延迟失控:prefill在挤占decode
现象:单请求首Token延迟只有300ms,并发从1升到10,首Token延迟变成3秒以上,体验明显卡顿。
原因:vLLM虽然是continuous batching,但prefill阶段计算量大,长prompt的prefill会阻塞batch里其他请求的decode步骤。scheduler在分配批次时如果不对prefill做切分,一个大prompt进来就像高速公路上并入了辆卡车,整个batch的节奏被打乱。
解决:给scheduler加约束。新版vLLM支持--max-num-prefill-tokens限制单次prefill的token总量,设置后用带chunked prefill的机制把大prompt切成小块混进decode的空隙里。只设--max-num-seqs限制最大batch数也有帮助,相当于从源头控制卡车数量。这类问题盯metrics就行,num_requests_waiting飙升而GPU算力没满,十有八九是prefill没切分。
6. 进阶部署技巧:量化、LoRA和Docker镜像三条路怎么选
基础服务稳定后,下一个问题通常是:显存不够用怎么办、微调模型怎么接、多人协作环境怎么统一。这一章讲三条可落地的进阶路线,按推荐顺序排。
6.1 AWQ量化部署:显存砍半,代价可控
如果你的Qwen-7B在24G卡上开32K上下文总在OOM边缘,首选方案是换AWQ量化版本。AWQ把权重压到4bit,显存占用大约降一半,推理速度损失在个位数百分比,是性价比最高的降本手段。启动命令只需改模型路径和指定量化方式:
python -m vllm.entrypoints.openai.api_server \ --model ./Qwen2.5-7B-Instruct-AWQ \ --served-model-name qwen2.5-7b-awq \ --max-model-len 32768 \ --quantization awq \ --gpu-memory-utilization 0.9ModelScope上官方维护了AWQ版本,文件名带AWQ后缀,下载后--quantization awq告诉vLLM用AWQ算子解码。切换后原代码零改动,--served-model-name变了,客户端记得同步。量化后模型质量会有轻微损失,如果下游是强逻辑任务,先在离线测试集上对比几个关键case再切。
6.2 LoRA微调模型接入vLLM:关键在 --enable-lora
如果你做的是Qwen2.5-7B微调行业大模型,训练完拿到的是LoRA adapter,不是完整模型文件。vLLM原生支持部署base模型叠加LoRA,启动时加两个参数:
python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --enable-lora \ --lora-modules my-lora=/path/to/adapter \ --served-model-name qwen2.5-7b-lora--enable-lora打开适配器加载能力,--lora-modules指定别名和adapter路径,请求时model字段传my-lora就能走微调后的行为。注意显存账要同时算base模型和adapter,adapter本身不大,几十到几百MB,但base模型该占的显存一分不少。如果微调时改了词表或使用了自定义chat模板,部署前确认这些配置和base模型兼容,否则推理结果会在模板拼接阶段变形。
6.3 生产环境为什么我最终还是回到Docker镜像
我是从pip安装开始用vLLM的,踩过几次版本和驱动匹配问题的坑后,生产环境最终还是固定成Docker镜像方式部署。原因很朴素:镜像把CUDA、torch、vLLM、flash-attention的匹配关系固化好了,换机器部署时不用重新排错一遍;版本号写在镜像tag里,回滚就是重启一个旧tag容器。团队协作时,一个docker run命令就是全部的环境文档,新手照着跑不会在第一步就因为驱动版本起不来。
我现在的固定流程是:ModelScope下载模型,Docker镜像启动服务,metrics探活,benchmark留底。每次部署前把vLLM版本、模型路径、三个关键参数表格贴到项目说明里,翻车时先对照参数再查日志,这个习惯帮我省了太多无头绪的调试时间。部署框架这东西,选定一条路径走到黑,比反复横跳有效率得多。希望帮到你。
本文还有配套的精品资源,点击获取