首页 / AI教程 / 正文
AI教程

vLLM教程:从安装部署到性能调优,快速搭建高吞吐大模型推理服务

chuanbook chuanbook
发布于 2026 年 10 月 06 日
阅读 约28分钟
浏览 2
评论 0

我刚开始折腾大模型推理服务时,总在显存和吞吐之间左右为难。一个请求处理完再处理下一个,GPU 大量时间在等待。后来朋友推荐了 vLLM,说它专治这种浪费。我花了几周时间从安装到调参,慢慢摸清了它的脾气。这份 vLLM 教程想把我踩过的坑和总结的经验分享出来,帮你少走弯路。

1.1 vLLM是什么:PagedAttention与连续批处理机制

vLLM 是一个开源的大模型推理与服务引擎。我习惯把它看作“大模型推理的加速器”。它的目标是让同样的显卡跑出更高的吞吐,同时把延迟控制住。核心秘密藏在两个机制里:PagedAttention 和连续批处理。我第一次读论文时觉得概念不难,真正部署后才发现效果确实明显。

PagedAttention 的思路很像操作系统的虚拟内存分页。传统推理把每个请求的 KV Cache 放在一整块连续显存里。请求长短不一,显存碎片就多,浪费严重。vLLM 把 KV Cache 切成固定大小的块,按需分配,像拼图一样灵活。这样做的好处是显存利用率上去了,能同时服务的请求数量也多了。我从监控里看到,开启 PagedAttention 后,同样一张 A100 能承载的并发数几乎翻倍。

连续批处理机制解决的是“等”的问题。以前的静态批处理要等一批请求全部结束,才能开始下一批。vLLM 允许新请求随时加入正在运行的批次,已经完成的请求立刻退出。GPU 很少空转,吞吐自然更高。我印象很深的一次测试,用连续批处理跑混合长度请求,吞吐比 HuggingFace Transformers 高出十几倍。这个差距对线上服务来说,直接关系到显卡成本和响应速度。

1.2 vLLM与其他大模型推理框架对比

我试过不少推理框架,每个都有自己的性格。HuggingFace Transformers 适合实验和微调,推理性能偏弱。TensorRT-LLM 性能很强,编译和调优门槛高,我折腾半天才跑通一个模型。TGI 由 HuggingFace 推出,生态集成好,部署也算方便。Ollama 适合本地个人玩,并发一大就吃力。LMDeploy 在国产模型上优化不错,社区规模相对小一些。

vLLM 吸引我的地方是平衡。它提供 OpenAI 兼容 API,客户端代码几乎不用改。PagedAttention 和连续批处理带来实打实的吞吐提升。社区更新快,新模型支持通常很及时。我拿它做过客服机器人后端,也做过离线数据标注,表现都稳。对比下来,如果你要自建高并发 API 服务,vLLM 常常是优先选项。

TensorRT-LLM 在极致延迟场景仍有优势,代价是工程复杂度。TGI 的监控和部署体验也值得称赞。我的建议是别只看 benchmark 数字。团队熟悉 Python 和 PyTorch,vLLM 上手更快。团队有专门推理优化工程师,TensorRT-LLM 可以深挖。我见过有人盲目追新,结果卡在编译环节,项目延期。选框架和选鞋一样,合脚最重要。

1.3 vLLM典型应用场景与选型建议

vLLM 最常见的场景是在线大模型 API 服务。我帮朋友搭过一个内部知识问答接口,用 vLLM 启动服务,前端直接调 OpenAI SDK。并发上来后,连续批处理把显卡吃得很满,响应时间还能接受。离线批量推理也很合适。跑数据集生成、内容审核、摘要提取,vLLM 可以批量提交请求,吞吐比逐条跑高很多。

多模型服务和 LoRA 适配器是另一个亮点。我试过在同一个 vLLM 实例里加载基础模型和多个 LoRA,按请求切换。这对 SaaS 产品很友好,不用为每个客户单独起服务。RAG 应用也常把 vLLM 当生成后端,配合向量数据库和重排模型。Agent 场景里,vLLM 的低延迟让多轮工具调用更流畅。

选型时我会问几个问题。并发量多大?延迟要求是几百毫秒还是几秒?显卡是 A100、H100 还是消费级 4090?模型是 Llama、Qwen 还是多模态?团队更熟悉 Docker 还是源码编译?这些问题定下来,答案通常就清晰了。vLLM 在吞吐和易用性之间拿捏得不错。极端低延迟或特定硬件,可以再评估其他方案。

1.4 vLLM版本选择与学习路线规划

vLLM 版本迭代很快,我建议优先选稳定版。看 GitHub Releases 里的说明,确认支持的 CUDA 和 PyTorch 版本。新版本可能带来性能提升和新模型支持,也可能引入兼容问题。生产环境别追最新,等社区反馈一轮再升级。我在测试环境会同时装两个版本,方便对比。CUDA 驱动版本也要对好,否则装完跑不起来。

学习路线我习惯分成四段。入门阶段跑通 pip 安装,用示例模型启动 OpenAI 兼容服务,发几个请求。基础阶段写离线批量推理脚本,调采样参数,理解停止条件。优化阶段研究连续批处理参数、KV Cache 显存、张量并行和量化。高级阶段碰分布式、多模态、生态集成和生产运维。每段都有动手任务,光看文档容易忘。

我自己的习惯是边学边记。每跑通一个功能,就写一段笔记,记录命令、报错和解决办法。vLLM 教程网上很多,质量参差不齐。官方文档和源码 examples 最可靠。遇到问题先查 issues,再跑最小复现。学完第 1 章,你应该对 vLLM 的定位和路线有数了。后面的章节我会带你一步步安装、推理、调优和上生产。

学完第1章的原理,我心里痒痒,想立刻把 vLLM 跑起来。真到动手那天,才发现环境准备比想象中琐碎。显卡驱动、Python 版本、CUDA 版本,哪个对不上都报错。我前后装了三台机器,踩了不少坑。这一章我把安装部署的完整流程拆开,从硬件检查到服务验证,一步步带你走通。

2.1 硬件、系统与CUDA驱动要求

vLLM 对硬件不算挑剔,但也不是随便一张显卡就能跑。它需要 NVIDIA GPU,计算能力 7.0 以上。我试过用老款 P100,能跑但性能一般。显存方面,7B 模型建议 16GB 起步,13B 要 24GB,70B 得上多卡或量化。消费级 4090 跑 7B 很轻松,跑 13B 就有点紧张。我手头一张 A100 40GB,跑 7B 和 13B 都稳。你要是用云服务器,记得选带 NVIDIA 驱动的镜像,省去装驱动的麻烦。

操作系统我推荐 Ubuntu 20.04 或 22.04。CentOS 也能用,但社区资料少一些。Windows 不是不能跑,WSL2 里可以,生产环境还是 Linux 省心。CUDA 驱动版本要和 vLLM 要求的 PyTorch 版本匹配。我习惯先跑 nvidia-smi 看驱动版本,再查 vLLM 官方文档的兼容表。有一次我驱动太旧,装完 vLLM 导入就报错,升级驱动才解决。CUDA Toolkit 不一定非要单独装,pip 安装的 PyTorch 会自带 CUDA 运行时。

检查命令很简单。nvidia-smi 看显卡和驱动,nvcc --version 看 CUDA 编译器版本。我还会跑一个 python -c "import torch; print(torch.cuda.is_available())" 确认 PyTorch 能识别显卡。这几步花不了几分钟,能避免后面很多玄学问题。我朋友跳过检查,结果装完发现显卡太老,白忙一场。硬件这关过了,后面的路就顺了。

2.2 Python虚拟环境与依赖安装

Python 版本我建议用 3.9 到 3.11。3.12 刚出来时有些包不兼容,我吃过亏。虚拟环境用 conda 或 venv 都行。我习惯 conda,创建环境命令是 conda create -n vllm python=3.10。venv 更轻量,python -m venv vllm-env 也可以。环境建好,激活它,后面所有操作都在这个环境里。别在系统 Python 里直接装,依赖冲突会让你怀疑人生。

安装 PyTorch 是第一步。去 PyTorch 官网选对应 CUDA 版本的命令。我常用 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121。CUDA 12.1 兼容性不错。装完验证一下 torch.cuda.is_available() 返回 True。接着装 vLLM,pip install vllm 就行。它会自动拉取依赖,包括 transformers、fastapi 等。我遇到过网络慢导致超时,换成国内镜像源会快很多。

依赖版本冲突是常事。我有次先装了旧版 transformers,再装 vLLM 就报错。解决办法是先建干净环境,直接 pip install vllm,让它自己决定依赖版本。如果需要特定版本,用 pip install vllm==0.4.2 这样的命令。装完跑 python -c "import vllm; print(vllm.__version__)" 确认。我还会跑官方示例脚本,确保基本功能正常。环境这步稳了,后面启动服务才不容易翻车。

2.3 pip、源码编译与Docker部署方式

pip 安装最省事,适合大多数场景。一条命令搞定,升级也方便。我日常测试都用 pip,几分钟就能跑起来。缺点是受限于预编译 wheel,有些最新特性或特定硬件优化可能没有。如果你只是用现成模型做推理,pip 完全够用。我推荐新手从这个方式开始,先把服务跑通,再考虑其他。

源码编译适合想改代码或追最新 commit 的人。从 GitHub clone 仓库,pip install -e . 安装。编译过程可能遇到 CUDA 架构不匹配、gcc 版本问题。我编译过一次,花了半小时,中间报错好几次。好处是能拿到最新功能和性能改进。生产环境追新有风险,我一般只在测试环境编译。团队里有人专门维护编译脚本,每次升级都跑一遍回归测试。

Docker 部署是我最喜欢的方式,尤其在生产环境。官方提供 vllm/vllm-openai 镜像,拉下来就能用。docker run --gpus all -p 8000:8000 vllm/vllm-openai --model facebook/opt-125m 这样启动。Docker 把环境隔离得干干净净,换机器不用重新配。我帮客户部署时都优先用 Docker,版本回滚也容易。镜像体积有点大,网络好就无所谓。三种方式各有适用场景,我建议你至少掌握 pip 和 Docker 两种。

2.4 模型下载、本地缓存与权限配置

模型下载是部署里最耗时的环节。HuggingFace 上的模型动辄几十 GB,网络不好能下一整天。我习惯用 huggingface-cli download 命令,支持断点续传。先 pip install huggingface_hub,再 huggingface-cli login 输入 token。有些模型需要授权,比如 Llama 系列,得先在页面上申请。下载命令是 huggingface-cli download meta-llama/Llama-2-7b-hf --local-dir ./models/llama-2-7b。

缓存目录默认在 ~/.cache/huggingface。磁盘空间不够时,可以设置环境变量 HF_HOME 指向大容量盘。我有一台机器系统盘小,就把缓存放到数据盘,export HF_HOME=/data/hf_cache。vLLM 启动时会自动从缓存加载,不用重复下载。权限问题也常遇到。Docker 里跑 vLLM,挂载的模型目录要有读权限。我遇到过 Permission denied,最后用 chmod -R 755 解决。多用户环境要小心,别把敏感模型暴露给所有人。

国内下载慢是个痛点。我试过用镜像站,export HF_ENDPOINT=https://hf-mirror.com,速度能快不少。也可以手动下载模型文件,再放到缓存目录对应位置。vLLM 支持从本地路径加载模型,--model /path/to/model 这样写。我有时会先把模型下载到本地,再传到内网服务器,避免反复下载。模型文件大,传输用 rsync 带压缩,比 scp 快。缓存管理好了,启动服务时几乎秒加载。

2.5 启动OpenAI兼容API服务与验证

启动服务命令不复杂。pip 安装的话,vllm serve facebook/opt-125m 就能跑。默认端口 8000,OpenAI 兼容 API 自动开启。我常用参数有 --host 0.0.0.0 让外部能访问,--port 8080 换端口,--tensor-parallel-size 2 多卡并行。模型越大,启动加载时间越长。70B 模型可能要几分钟,耐心等日志出现 Uvicorn running on 才算好。我第一次启动时以为卡死了,其实是在加载权重。

验证服务用 curl 最直接。curl http://localhost:8000/v1/completions -H "Content-Type: application/json" -d '{"model": "facebook/opt-125m", "prompt": "Hello", "max_tokens": 10}'。返回 JSON 里有生成的文本,说明服务正常。Chat 接口也类似,/v1/chat/completions。我还会用 Python OpenAI SDK 测试,client = OpenAI(base_url="http://localhost:8000/v1", api_key="token"),然后调 client.chat.completions.create。这样更接近真实使用场景。OpenAI SDK 的 api_key 随便填,vLLM 默认不校验。

服务跑起来后,我会看日志确认没有报错。常见问题有显存不足、模型路径错误、端口占用。显存不足就换小模型或加量化参数。端口占用换个端口。模型路径错误检查 --model 参数。我习惯先跑小模型验证流程,再换大模型。服务稳定后,可以用 nohup 或 systemd 让它后台运行。生产环境还要配反向代理和鉴权,这些后面章节会讲。走到这一步,你已经能用 vLLM 提供 API 服务了。接下来就是怎么调好、用好的问题。 from vllm import LLM, SamplingParams

llm = LLM(model="Qwen/Qwen2.5-7B-Instruct") sampling_params = SamplingParams(temperature=0.7, max_tokens=512) prompts = ["介绍一下杭州", "写一首关于冬天的诗", "解释什么是注意力机制"] outputs = llm.generate(prompts, sampling_params)

for output in outputs:

print(output.outputs[0].text)

服务能跑起来只是起点。真正上线之后,老板关心的是每秒能处理多少请求,用户抱怨的是回答怎么这么慢,运维盯着的是显存怎么又爆了。这三件事互相拉扯,调优的过程就是在吞吐、延迟、显存之间找平衡点。我花了大概两个月时间,踩了不少坑,慢慢摸出了一些门道。

4.1 连续批处理与调度参数优化

连续批处理是 vLLM 吞吐量的核心来源。传统批处理要等一个批次里所有请求都生成完才能开始下一批,短请求被长请求拖着等。vLLM 的做法是每个解码步都重新组批,已经结束的请求立刻让出位置给新来的。我第一次看到日志里 Running: 32 reqs, Waiting: 128 reqs 这种滚动输出时,才真正理解这个机制在干什么。

调度参数直接决定批处理的效果。--max-num-seqs 控制单批次最大请求数,默认 256。我一开始设得太大,发现延迟飙升,因为每个请求分到的计算资源变少了。后来压到 128,吞吐没掉多少,P99 延迟降了三成。--max-num-batched-tokens 限制一个批次里所有请求的 token 总数,这个值比单纯的请求数更影响显存和计算效率。我一般设在 4096 到 8192 之间,具体看模型大小和显卡能力。

还有一组参数容易被忽略。--scheduler-policy 可以选 FCFS 或优先队列,默认先来先服务。我们有个场景是 VIP 用户的请求要优先响应,切换策略后配合 priority 字段传参,确实能压低高优请求的等待时间。--enable-chunked-prefill 这个开关我建议打开,长 prompt 的预填充会被切成小块,不会阻塞正在解码的请求。打开之后我们的混合负载场景下,P95 延迟从 4.2 秒降到了 2.8 秒。

调这批参数的时候,我的做法是先固定其他变量,一次只动一两个。比如先把 max-num-seqs 从 64 扫到 512,记录每个值下的吞吐和延迟,画成曲线找拐点。拐点之后吞吐涨得很慢,延迟却涨得很快。那个拐点就是你当前硬件和负载下的甜点区。不同模型、不同显卡,拐点位置不一样,抄别人的配置往往不灵。

4.2 PagedAttention与KV Cache显存管理

KV Cache 是推理时显存占用的大头。模型权重占一部分,剩下的大多给了 KV Cache。vLLM 用 PagedAttention 把 KV Cache 切成固定大小的块来管理,块之间可以不连续。这个设计借鉴了操作系统的虚拟内存分页,好处是碎片少、利用率高。我做过对比,同样硬件下 vLLM 能同时服务的请求数比朴素实现多三到四倍,主要功劳就在这儿。

--gpu-memory-utilization 决定了 vLLM 能占用多少显存,默认 0.9。剩下的留给 CUDA 上下文和临时张量。我遇到过设成 0.95 之后跑着跑着就 OOM 的情况,因为解码过程中会有一些临时分配。后来统一用 0.85 到 0.88 之间,稳得多。--block-size 默认 16,一般不用改。我试过调到 32,吞吐略升,但短请求的显存浪费变多。调到 8 则块管理开销变大,得不偿失。

--swap-space 是 CPU 内存交换区大小,默认 4GB。当显存里的块不够用时,vLLM 会把一些 KV Cache 换到 CPU 内存。这在请求突发的时候能救急,但换入换出有开销。我们线上配了 16GB swap,只作为缓冲,正常运行时基本用不到。如果发现 swap 频繁触发,说明显存真的不够了,该考虑加卡或者量化。

监控 KV Cache 使用率很有必要。vLLM 日志里会打印 GPU KV cache usage 百分比。我写了个脚本定期抓这个值,超过 90% 就告警。长期跑在 95% 以上,延迟会明显抖动。还有 --num-gpu-blocks-override 这个参数可以手动指定块数量,一般用来自动探测失败时的兜底。我基本没手动设过,默认逻辑够用。

显存管理还有个实践心得:启动时模型加载完那一刻的显存占用要盯一下。如果加载完就用了 85%,留给 KV Cache 的只剩一点点,并发根本上不去。这时候要么换更小的模型,要么上量化,要么加卡。我见过有人拿 7B 模型在 24GB 卡上硬跑,并发只能到个位数,体验很差。

4.3 张量并行、流水线并行与多卡配置

单卡放不下模型时就得动用多卡。vLLM 支持张量并行和流水线并行,前者把每一层的矩阵切到多张卡上,后者把不同层分到不同卡上。张量并行通信量大,适合卡间带宽高的场景,比如同一台机器里的 NVLink。流水线并行通信量小,适合跨机部署,但会有气泡。

--tensor-parallel-size 是最常用的多卡参数,设成 2 就是把模型切到两张卡上。我用两台 A100 跑 70B 模型时设的 4,单卡显存占用明显下降。要注意的是张量并行度最好是 2 的幂次,而且不能超过注意力头数。有次我设了个 3,服务直接起不来。设完之后每张卡的显存占用大致是原来的 1/N,但不会精确等于,因为有些层不好切。

流水线并行用 --pipeline-parallel-size 控制。我试过一次 2 卡流水线跑 13B 模型,发现吞吐不如张量并行的 2 卡配置。原因在于流水线的气泡时间,batch size 小的时候特别明显。后来我们把 batch 调大了,流水线的效率才上来。现在我的经验是:能张量并行就张量并行,跨机或者卡数很多时才考虑流水线。

多卡配置还有个细节是 --distributed-executor-backend。默认用 ray,单机多卡也可以用 mp。我用 mp 的时候启动快一些,ray 在多机场景更稳。有个坑是 ray 集群没配好的话,vLLM 会卡在初始化阶段,日志停在 Waiting for workers。这时候去查 ray 的状态,大概率是某个节点没连上或者版本不匹配。

多卡调优的衡量指标是扩展效率。理想情况下 2 卡吞吐是 1 卡的两倍,实际能做到 1.7 到 1.9 倍就算不错。通信开销、负载不均衡都会吃掉一部分。我会在加卡前后各跑一次基准,算一下实际加速比。如果低于 1.5,就要看看是不是卡间通信拖了后腿,或者是不是 batch size 太小没喂饱。

4.4 量化推理:AWQ、GPTQ、FP8与精度权衡

量化是省显存、提吞吐的利器。FP16 的 7B 模型权重占 14GB 左右,量化到 INT4 只剩 4GB 不到,留给 KV Cache 的空间大了很多。vLLM 支持 AWQ、GPTQ、FP8 这几种主流量化格式。我三种都用过,各有各的脾气。

AWQ 是我用得最多的。它的特点是激活感知,对权重里重要的通道保留更高精度。我拿 Qwen2.5-7B 的 AWQ 版本跑过评测,效果和 FP16 版本差距在 1% 以内,显存少了近 60%。加载方式是加 --quantization awq,模型路径指向 AWQ 权重。AWQ 对硬件有一定要求,计算能力 7.5 以上的卡支持比较好。老卡上跑可能会 fallback 到慢路径。

GPTQ 出现得更早,生态里的量化模型也多。--quantization gptq 就能加载。它的量化粒度更细,group size 可以选 32、64、128。group size 越小精度越高,显存占用也越大。我试过 group size 32 和 128 的对比,前者在代码生成任务上确实好一些,但吞吐低了一点。选哪个看你的任务对精度有多敏感。

FP8 是 H100 之后比较热的格式。原生支持 FP8 的卡上,vLLM 可以开 --quantization fp8,权重和激活都用 8 位浮点。精度损失比 INT4 小,显存节省不如 INT4 多。我拿 H100 跑过 FP8 的 Llama3-70B,吞吐比 FP16 高了大概 40%,输出质量几乎没差别。FP8 的好处是配合 FP8 KV Cache 一起用,显存节省能叠加。

精度权衡这事不能一刀切。做客服问答这种容错高的场景,INT4 问题不大。做医疗、法律这种对准确性敏感的,我建议至少上 FP8 或者干脆不量化。我做过一次测试:同一个数学题,FP16 模型答对,INT4 版本算错了一位小数。这种错误在业务里可能是致命的。量化之前一定在自己的评测集上跑一遍,别只看通用榜单。

4.5 性能基准测试、吞吐延迟分析与瓶颈定位

调优没有基准测试就是盲人摸象。我刚开始凭感觉调,改完参数觉得快了,实际一压测发现没差别。后来养成了习惯:每次改动前后都跑一遍 benchmark。vLLM 自带了 benchmarks/benchmark_throughput.py 和 benchmark_serving.py,前者测离线吞吐,后者测在线服务延迟。

压测的负载要尽量贴合真实场景。我用 shareGPT 数据集模拟对话,输入长度从几十到几千 token 都有。只测单一长度没意义,长短混合才能暴露调度问题。压测的时候并发数要一档一档加上去,从 1 加到系统撑不住为止。记录每一档的吞吐、TTFT(首 token 延迟)、TPOT(每 token 输出延迟)和 P99。

看结果的时候有两个关键曲线。吞吐随并发上升,到某个点开始变平,说明计算资源饱和了。延迟随并发上升,到某个点开始陡增,说明排队严重了。这两个点中间的区域就是最佳工作区间。我现在的服务就控在这个区间里,并发过高就限流,保证延迟可控。

瓶颈定位靠分层排查。先看 GPU 利用率,nvidia-smi 里如果利用率在 60% 以下,大概率是 CPU 或 IO 瓶颈,比如 tokenizer 慢或者网络传输拖后腿。利用率 90% 以上还在排队,说明是计算瓶颈,考虑量化或者加卡。中间地带要看具体是哪个 kernel 耗时,用 nsys 或者 pytorch profiler 抓一下。

我还遇到过一次诡异的情况:吞吐怎么调都上不去,GPU 利用率只有 40%。查了半天发现是日志级别设成了 DEBUG,每条请求都在刷大量日志,磁盘 IO 成了瓶颈。改成 WARNING 之后吞吐翻了一倍。这种问题看指标看不出来,得结合系统层面的监控。现在我压测的时候会把 top、iostat、nvidia-smi 都开着,哪个指标异常一目了然。

总结下来,vLLM 的调优是个系统工程。调度参数管请求怎么进怎么出,PagedAttention 管显存怎么分,并行配置管多卡怎么配合,量化管精度和显存的取舍,基准测试管你怎么知道改对了没有。这几块环环相扣,改一个地方可能影响另一个。我的建议是先跑基准建立基线,再一个变量一个变量地调,每步都留有数据。慢慢你就会对自己的负载和硬件组合形成直觉,看到数字就知道哪里不对劲。

前面几章把单机部署和调优摸透了。实际生产里,需求往往更复杂:模型大到一张卡装不下,输入里带着图片,还要跟LangChain、LlamaIndex这些框架对接。这一章我把自己踩过的坑和摸索出来的做法整理一下。

5.1 多机多卡分布式推理部署

单机八卡跑70B模型时,如果显存还是紧张,就得考虑多机。vLLM靠Ray来组织跨机集群。我配过四台八卡A100的集群,每台机器先跑ray start --head --port=6379,其他节点用ray start --address=<head_ip>:6379加入。vLLM启动时指定--distributed-executor-backend ray,再设好--tensor-parallel-size和--pipeline-parallel-size,两者乘积等于总卡数。跨机张量并行对网络要求极高,最好有InfiniBand或者至少100Gbps RoCE。NCCL的NCCL_SOCKET_IFNAME要指定正确的网卡,不然会走错路,性能掉得厉害。

我遇到最头疼的问题是Ray版本不一致。有一台机器装的是2.9,其他是2.10,vLLM日志就停在Waiting for workers,也不报错。查了半天才发现是版本冲突。统一版本之后启动就顺了。还有一次是防火墙挡了Ray的端口,节点之间连不上。多机部署的延迟比单机多卡高不少,通信开销摆在那里。我的经验是:模型能在一台机器里放下,就优先单机多卡。跨机主要留给超大规模模型,或者单机卡数不够的情况。

流水线并行跨机时要注意气泡。我试过两台机器各八卡,tp=8,pp=2,跑175B模型。batch size小的时候,流水线气泡让吞吐掉了三成。把--max-num-seqs调大之后才好转。衡量多机扩展效率很重要。加一倍机器,吞吐能到1.6倍就算合格,低于1.4倍就要查网络或者负载均衡。

5.2 多模态模型、嵌入模型与重排模型推理

vLLM早就不是纯文本推理框架了。多模态模型像LLaVA、Qwen-VL、Phi-3-vision都能跑。加载方式跟普通模型一样,指定模型路径就行。调用时通过OpenAI兼容接口传图片,image_url里可以放base64或者http地址。我跑Qwen2-VL的时候,显存比纯文本版本多占了大概30%,图像编码器吃掉了不少。--limit-mm-per-prompt可以限制每个prompt里图像的数量,防止有人塞一堆图把显存打爆。

嵌入模型和重排模型也能用vLLM加速。启动时加--task embedding,模型换成BAAI/bge-large-zh-v1.5这类,接口调/v1/embeddings。重排模型用--task score,输出的是查询和文档的相似度分数。不是所有嵌入模型都支持,最好先看vLLM的文档列表。我试过用vLLM跑bge和gte,吞吐比HuggingFace的pipeline高好几倍,延迟也稳。重排模型对长文档的处理要注意截断,vLLM不会自动帮你切。

多模态和嵌入模型混部在一个服务里比较麻烦。vLLM一个实例通常只加载一个模型。我的做法是起多个实例,前面用Nginx按路径分流。或者用--lora-modules挂载多个LoRA适配器,但多模态模型不太适合走LoRA。嵌入模型和重排模型可以单独起小实例,用--gpu-memory-utilization 0.3之类的低配,省出卡给大模型。

5.3 自定义模型、算子和插件扩展

遇到vLLM不支持的模型架构,得自己动手。核心是注册模型。vLLM提供了@register_model装饰器,你要实现模型的__init__和forward,还要处理权重加载。我做过一个自定义的奖励模型,直接抄了现有类似架构的实现,改改层名和配置。注册到ModelRegistry之后,启动时指定--model为自定义路径就能加载。注意跟PagedAttention的兼容性,KV Cache的形状和注意力计算要匹配,不然跑起来结果会错。

自定义算子情况少一些。有些模型用了特殊的激活函数或者归一化,vLLM没内置。可以用torch.library写个算子,或者直接写CUDA kernel。写CUDA的话要考虑跟CUDA graph的兼容。我试过一个自定义的rotary embedding,没注意graph capture,结果每次推理都重新编译,性能惨不忍睹。后来加了@support_torch_compile类似的标记才解决。插件扩展走vllm.plugins入口点,可以挂自定义的调度器、量化方法或者日志处理器。版本升级时接口经常变,我一般会把vLLM版本锁死,升级前先在测试环境验证插件。

自定义模型最容易翻车的地方是权重加载。不同模型的state dict key名字千奇百怪,vLLM的load_weights要自己写映射。我习惯先把HuggingFace的权重打印出来,跟vLLM期望的key逐个对照,写个转换函数。还有并行切分,张量并行时权重怎么切、怎么合并,要仔细处理。搞错的话单卡能跑,多卡结果就乱套。

5.4 与LangChain、LlamaIndex等生态集成

vLLM的OpenAI兼容API让它跟LangChain集成特别简单。用ChatOpenAI,base_url指向http://localhost:8000/v1,api_key随便填个EMPTY。我平时做RAG,LLM后端就挂vLLM,嵌入模型另起一个vLLM实例或者用OpenAI的。LangChain的流式输出要配合服务端的流式支持。新版本vLLM默认开启流式,旧版本要加--enable-streaming。我一般会在ChatOpenAI里设streaming=True,再调request_timeout=120,长文本生成不容易断。

LlamaIndex用OpenAILike类,配置方式差不多。ServiceContext里把llm和embed_model都指到vLLM。工具调用这块,vLLM支持--enable-auto-tool-choice和--tool-call-parser。我试过hermes解析器,跟LangChain的Agent配合能用。但工具调用的稳定性不如OpenAI原生,偶尔会解析失败。生产环境我一般只开简单的函数调用,复杂的Agent逻辑还是走OpenAI或者Claude。

集成时有个坑是超时和重试。vLLM服务如果排队严重,LangChain默认超时可能只有60秒,请求就挂了。我把max_retries设成2,request_timeout设成180。还有LangChain的token计数,它默认用tiktoken,跟vLLM的实际tokenizer可能不完全一致。对计费敏感的场景,最好在vLLM侧统计用量。LlamaIndex的CallbackManager可以接vLLM的日志,但需要自己写适配器。

5.5 鉴权、限流与安全合规配置

vLLM的API服务默认裸奔,谁都能调。生产环境必须加鉴权。简单做法是启动时加--api-key your-secret-key,客户端在Header里带Authorization: Bearer your-secret-key。这个只支持单个key,多用户场景不够用。我一般会在前面架一层Nginx或者API网关,做JWT验证和key管理。网关还能顺便做限流。vLLM本身没有精细的限流,只有--max-num-seqs这种粗粒度控制。用Nginx的limit_req模块,按IP或者API key限速,比较实用。

安全合规方面,日志脱敏很重要。vLLM默认会打印请求和响应的部分内容。生产环境我把日志级别调到WARNING,并且关掉--disable-log-requests。模型输出过滤可以接一个审核服务,比如用另一个小模型做敏感词检测。多租户场景下,不同租户用不同的API key,网关把key映射到不同的vLLM实例或者LoRA适配器。vLLM的--lora-modules可以挂多个LoRA,请求时指定model字段切换。但LoRA之间隔离不彻底,显存是共享的,高优租户可能被影响。

CORS配置也别忘了。如果前端直接调vLLM,要加--allowed-origins和--allowed-methods。我遇到过前端跨域被挡,查了半天才发现是CORS没开。还有HTTPS,vLLM本身不支持TLS,得靠Nginx做SSL终止。合规方面,如果处理个人数据,要确保日志不落盘或者加密存储。模型权重和KV Cache也可能包含敏感信息,多租户环境下最好物理隔离或者用可信执行环境。这些配置琐碎,但上线前必须过一遍。

这些高级特性让vLLM能覆盖更多场景,配置复杂度也上来了。我的建议是先把单机服务跑稳,再逐步引入分布式、多模态和生态集成。每加一个组件,都要有对应的监控和回滚方案。 scrape_configs: - job_name: 'vllm'

scrape_interval: 15s
static_configs:
  - targets: ['vllm-service:8000']
赞0
踩0
☆收藏0
版权声明
文章版权声明:除非注明,否则均为ZBLOG原创文章,转载或复制请以超链接形式并注明出处。
分享到
chuanbook

链接已复制到剪贴板