第一次在8G显存的卡上跑vllm我的感受是原来开源大模型部署可以这么省事。以前用transformers自己写推理脚本一个请求进来基本独占整张卡第二个请求只能排队GPU利用率低到让人心疼。换成vllm之后同样一张显卡吞吐直接上了一个台阶多路并发请求也能扛住输出速度还快。这篇教程是给想快速把大模型服务跑起来的朋友准备的从环境准备、启动命令、指标判断到常见坑位我会把vllm部署这件事讲清楚目标是你花半天时间就能把自己的模型服务跑透。1. 先把话说清楚vllm到底替你解决了什么1.1 没有vllm的时候自建模型服务为什么又慢又贵很多人第一次接触vllm是在部署Qwen、InternLM这类开源模型的时候。为什么大家都推荐用它因为按照传统的transformers推理逻辑一次请求从进入到返回整条链路是串行的先做prefill预填充把用户的输入一次性算完再一步步做decode逐个token生成一个请求占着整张卡第二个请求只能排队等待。更浪费的是显存里给KV cache预留的空间通常按模型最大序列长度去分配。用户实际只输入几十个token你却给它留了几千个token的位置显存利用率自然上不去。vllm做的第一件事就是连续批处理continuous batching。大白话解释它不再等一个请求完全跑完才开始下一个请求而是在每一步推理时动态地把几个请求拼进同一个batch一起forward。就像以前出租车一车只拉一个乘客到站就空车回去现在改成拼车有人上下车车子始终在拉人。GPU是同一个GPU但有效计算密度提升了好几个量级。1.2 PagedAttention和显存分页为什么多出来的KS Cache能变成吞吐vllm另一个核心设计是PagedAttention。这个机制解决的是KV cache碎片化的问题。它把KV cache拆成固定大小的block像操作系统给进程分配内存页一样需要多少就分配多少不要求一整块连续的显存空间。这个思路直接吃掉了传统推理框架最吃亏的部分——大量显存被闲置浪费。有人会问KV cache省下来之后好处是什么答案是省下来的显存可以放大batch。batch越大GPU的算力利用率越高每秒钟能产出的token总量越大。这也是为什么同样一张卡用vllm部署能把吞吐翻好几倍的原因。快速上手阶段你不需要把PagedAttention的源码吃透但心里要有这个概念vllm的显存管理方式决定了它会优先吃满显存来放大batch所以后面提到的--gpu-memory-utilization参数会非常重要。顺带说一句如果你准备深入看vllm代码可以从LLMEngine、Scheduler、Worker这三层入手。Scheduler负责决定哪些请求进batchWorker负责真正的模型前向计算LLMEngine把整个调度循环串起来。快速上手不要求你懂这些但如果后面遇到性能问题知道堆栈在哪一层会帮你省很多事。2. 环境准备版本、依赖和模型权重一个都不能错2.1 先确认Python、CUDA和PyTorch的对应关系vllm对环境的要求不复杂但版本一旦错位启动时会有一堆莫名其妙的报错。我建议安装前先执行三条命令确认环境python --version nvcc --version nvidia-smi我的经验是Python用3.10到3.12之间CUDA用12.1及以上PyTorch用2.x版本这套组合在vllm上踩坑最少。nvidia-smi显示的是驱动支持的CUDA版本nvcc --version显示的是编译工具链的CUDA版本两个不一定一致vllm实际上更关心运行时能不能找到匹配的驱动和算子库。只要你驱动够新通常不会有问题。如果你机器上已经装了其他深度学习环境强烈建议用venv或者conda单独给vllm开一个环境不要直接在base环境里装。我以前图省事直接装结果跟另一个项目的torch版本互相打架最后重装了系统环境才恢复这个成本真的没必要。2.2 用pip装vllm还是自己编译一份大多数情况下一条pip命令就能搞定pip install vllmpip会自动拉取匹配的PyTorch、flash-attention等依赖。装完可以跑一下python -c import vllm; print(vllm.__version__)确认安装正常。那什么时候需要从源码编译一般是两种情况你要改vllm源码做二次开发或者你需要跑的模型架构在官方release版本里还没支持需要用到最新的主分支。源码编译会花不少时间我自己在一台16核机器上编译过一次大概用了四十多分钟如果机器核数少一个多小时也很正常。所以搜到vllm构建需要多长时间这样的问题我的回答是能pip就pip别折腾编译除非你确实有源码级的需求。2.3 模型权重用ModelScope下载更省心vllm本身不直接提供模型权重你需要先把模型下到本地。以通义千问Qwen3-8B为例用ModelScope的命令行工具下起来很方便pip install modelscope modelscope download --model Qwen/Qwen3-8B --local_dir ./models/Qwen3-8B--local_dir指定本地保存目录模型下载完会包含config.json、tokenizer文件、模型权重等完整内容。vllm在用--model指定路径时会去读取这个目录下的模型配置。我习惯下载完先看一眼目录结构确认权重文件是完整的一个整体而不是中间断掉只剩一堆临时文件。有些朋友习惯直接用Hugging Face上的模型路径比如Qwen/Qwen3-8Bvllm也支持但如果你的网络环境不太好还是先用ModelScope下到本地再加载能省掉很多等待时间。3. 第一次启动一条命令跑通Qwen3-8B3.1 推荐启动命令和各参数含义vllm提供了OpenAI兼容的HTTP服务。打开终端进到模型目录所在环境执行vllm serve ./models/Qwen3-8B \ --served-model-name qwen3-8b \ --host 0.0.0.0 \ --port 8000 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --max-num-seqs 32这里每个参数都不是随便填的我拆开解释--served-model-name是暴露给调用方的模型名称可以随便起但调用时用的model字段必须跟它一致。--host 0.0.0.0表示允许其他机器访问本地调试也可以用127.0.0.1。--gpu-memory-utilization 0.85表示允许vllm最多使用85%的显存。剩下15%留给模型权重、CUDA context和系统开销。如果你的显卡显存比较紧张可以调低到0.75如果显存富余可以调高到0.9以上。这个参数调不好后续很容易OOM。--max-model-len 8192是最大上下文长度。设得越大KV cache预留越激进能同时服务的请求数就越少。要根据实际业务需求来定不是越大越好。--max-num-seqs 32是单个batch最多容纳的请求数。调大能提升吞吐但会增加显存压力。启动后看到类似Application startup complete之类的日志并且没有报错就说明服务起来了。3.2 用OpenAI兼容接口做冒烟测试服务起来之后用curl直接发一条chat请求curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen3-8b, messages: [{role: user, content: 用一句话介绍你自己}], max_tokens: 128 }正常情况下会返回一个JSON里面有choices数组和usage字段。usage里的prompt_tokens和completion_tokens分别表示输入和输出的token数第一次调通时建议看一眼这两个数字能帮你对模型的实际token开销建立感觉。如果你习惯用Python可以用openai库来调from openai import OpenAI client OpenAI(base_urlhttp://localhost:8000/v1, api_keyEMPTY) resp client.chat.completions.create( modelqwen3-8b, messages[{role: user, content: 讲个冷笑话}], max_tokens128, ) print(resp.choices[0].message.content)api_key随便填一个占位字符串就行vllm默认不做鉴权。生产环境要加鉴权的话可以在前面套一层网关或者用vllm支持的API key配置。3.3 启动失败时的快速排查第一次启动大概率会遇到点问题最常碰到的几个显存不足报CUDA OOM。这时把--gpu-memory-utilization调低或者把--max-model-len调小再或者换一个量化版模型。模型目录填错vllm会提示找不到config.json。确认--model指向的是模型权重所在目录而不是上一级目录。模型需要trust_remote_code部分模型架构不在vllm内置支持列表里需要加--trust-remote-code参数。极个别小众模型还要准备自定义代码。我一般建议优先用主流模型省心。4. 别凭感觉优化先盯住TTFT、TPOT和吞吐量4.1 三个核心指标先搞清楚服务跑通之后最重要的不是马上调整各种参数而是先搞清楚系统当前处于什么水平。大模型推理服务最常看三个指标TTFTTime To First Token从请求发出到收到第一个token的时间。这个值决定了用户感知的响应速度通常目标在几百毫秒到一两秒之间。TPOTTime Per Output Token生成每一个token的平均耗时。这个值决定了文字输出的流畅度如果一秒钟只能蹦出一个token用户会觉得卡。吞吐量每秒能生成的token总数。对于并发调用场景这个值决定了系统能同时服务多少用户。这三个指标互相制约。比如把--max-num-seqs调大吞吐通常会上升但如果batch太大每个请求分到的算力变少TTFT就可能变长。所以优化前一定要先明确你更在意首字延迟还是在意单位时间能处理的请求总量。4.2 用vllm bench serve自己压一把vllm自带了一个压力测试工具vllm bench serve可以在不写任何压测脚本的情况下直接对服务发起一批请求vllm bench serve \ --model ./models/Qwen3-8B \ --served-model-name qwen3-8b \ --num-prompts 100 \ --max-model-len 8192跑完之后会输出一组统计结果包括请求成功率、平均TTFT、平均生成速率等等。我第一次压的时候结果里TTFT的波动很大后来发现是因为首轮请求触发了显存和CUDA graph初始化后面就顺了。所以拿到压测数据后先剔除前几条预热请求再看整体分布不要只看平均值最好关注P50和P95。没有现成压测工具时也可以自己写循环用curl多发几次请求记录每个请求的耗时和输出长度手动算一个粗略的吞吐。虽然不够严谨但用来判断参数调整前后的趋势变化是够用的。4.3 用nvidia-smi观察GPU是否真的在卖力干活压测的时候并行开一个窗口跑nvidia-smi -l 1每秒刷新一次GPU利用率。如果GPU利用率始终在90%以上说明算力吃满了如果利用率不高但请求已经堆积说明瓶颈不在算力而在调度或CPU prefill环节这时要考虑调大--max-num-seqs或者检查CPU内存是否足够。还有一点容易被忽略vllm启动时会打印KV cache pool的相关信息大致内容是会估算KV cache占了多少显存、最大并发是多少。这些日志不是给你看着玩的当你想调大并发时先回看这段输出它会告诉你当前配置下理论上的并发上限省得你盲调。5. Windows、embedding和版本差异实测踩过的三个坑5.1 Windows上到底能不能跑vllm这个问题被问过很多次包括我最早也踩过。结论是原生Windows不是vllm的官方主战场部分算子编译和依赖在Windows上会遇到不少麻烦强行用不是不行但会让你把大量时间耗在环境折腾上。如果你只有Windows机器最省心的办法是装WSL2在WSL里跑一个Ubuntu环境然后把vllm装进去。NVIDIA驱动在Windows下装了之后WSL2里可以直接透传CUDA显卡能正常调用。模型权重可以在Windows侧用ModelScope下载摆在共享目录里WSL里直接加载这个路径就行。我在Windows上跑通过一次过程不能说痛苦但确实比Linux多花了不少时间。如果你是新手建议直接找一台Linux服务器或者用云GPU镜像先把vllm的主要流程跑通再考虑Windows的兼容问题。5.2 用vllm启动bge-m3这类embedding模型vllm不是只能部署对话模型它也能跑embedding模型。以BAAI/bge-m3为例vllm serve BAAI/bge-m3 --task embedding --port 8001注意这里的--task embedding不指定的话vllm会按默认的生成模型逻辑去处理很可能报错。模型起来后通过/v1/embeddings接口获取向量curl http://localhost:8001/v1/embeddings \ -H Content-Type: application/json \ -d {model: BAAI/bge-m3, input: 你好}碰到启动失败时先确认两件事一是模型架构是否在vllm的支持列表里像bge系列这类比较主流的没问题小众的embedding模型则不一定二是你用的vllm版本是否完善。如果版本太老对embedding任务的支持可能不完整升级一下版本往往能解决。5.3 版本升级带来的行为差异尤其要注意chunk_size这类隐藏参数vllm的版本更新速度很快但新版本不一定在所有场景下都更省心。有人反馈过chunk_size相关的行为在特定版本里表现异常比如使用chunked prefill时显存分配不符合预期或者在某些并发条件下性能明显回退。这类问题并不一定是bug也可能是默认值变了导致你的启动命令实际行为和之前不一样。我的建议是生产环境把vllm版本固定下来不要随手升级。新版本可以先在测试环境跑一遍压测对比关键指标确认没问题再切换。很多问题看似是模型问题查到最后其实是版本行为差异。6. 从跑通到跑好缓存命中、引擎选型和边缘设备6.1 prefix caching让重复前缀不再白算如果你观察过真实业务会发现很多请求是有公共前缀的比如多轮对话里前几轮历史几乎一样比如批量做评测时所有请求共享同一大段system prompt又比如代码补全时用户把前面几百行代码都塞进上下文。传统推理方式下每个请求都要重新计算这些前缀的KV cache浪费大量算力。vllm提供了Prefix Caching机制加了--enable-prefix-caching之后相同前缀的KV cache会被复用。如果某个请求的前缀跟之前算过的匹配这部分就不再重复计算。对多轮对话、Few-shot评测、文档问答这类场景收益非常明显。我在实测中遇到过一次缓存命中率很高但显存压力变大的情况原因是缓存也需要占额外显存。所以开启前要看好显存余量尤其当模型本身已经塞得很满时缓存带来的收益可能被显存紧张冲抵掉。6.2 vllm和sglang怎么选sglang和vllm经常被放在一起对比它们解决的是同一类问题。sglang在RadixAttention上做得比较激进对前缀复用的调度粒度更细在某些复杂Prompt和结构化生成场景下性能可以比vllm更极致。vllm的优势则在于生态成熟、接口标准、社区庞大遇到问题时更容易找到答案。我个人的选型经验是如果团队刚起步优先用vllm因为它能让你用最短时间把服务稳定跑起来如果后续发现延迟或吞吐确实不够再花时间对比一下sglang用压测数据说话。不要一开始就迷信哪个更快的论调多数场景下两者差距并没有传说中那么大。6.3 Jetson这类边缘设备上的vllm话题回到边缘设备。Jetson Thor这类设备上也能跑vllm官方提供了对应的容器镜像但边缘设备显存和算力都有限不适合直接上几十B的大模型。我见过比较常见的做法是在边缘设备上跑7B以下的小模型开低一点的并发配合量化把显存占用压下来效果还不错。边缘部署相比服务器端的难点不在vllm本身而在环境尺寸、功耗和可维护性所以快速上手阶段可以先不用深挖。7. 最后再说几句我的实际操作心得这篇教程写到这里核心链路已经完整了环境准备、模型下载、启动服务、压测验证、常见坑位。最后分享几个我实际跑项目时沉淀下来的习惯。第一先小后大。第一次接触vllm不要一上来就部署一个27B的大模型先用1.5B或者3B这种小模型把整个链路跑通确认接口、日志和压测方法都没问题再换大模型。这一步能帮你过滤掉非常多不必要的变量。第二启动日志值得仔细读。vllm启动时打印的显存估算、KV cache allocation、CUDA graph初始化信息不是无用输出。有一次我发现服务性能下降回头查启动日志发现--max-model-len被某次配置误改大KV cache余量明显变小问题一下就定位了。第三固定版本、固定参数、固定压测方法。版本升级前先跑一遍压测参数调整后记录前后指标压测时用同样的请求集。把这个习惯保持住你会发现自己排查问题的速度快很多。快速上手从来不是靠背诵命令而是靠建立一套自己的验证流程。