简介:针对Docker环境下的vLLM大模型部署需求,这份源码包面向需要落地QwQ-32B不同量化方案的AI开发者,覆盖AWQ、GPTQ-Int4与GPTQ-Int8三种量化方式的部署与测试流程。压缩包共3个文件,以HTML说明页为核心,辅以inscode配置与gitignore辅助文件,整体仅6KB,适合快速查阅或作为部署笔记复用。资源内详细梳理了首次在容器中安装vLLM、从已有镜像加载以及部署多模态大模型的步骤,并给出显存占用、GPU利用率、最大请求数等关键指标的实测结果;同时提供curl命令行测试和本地图片测试的具体方法,便于开发者验证模型服务是否正常。目前已有144人学习,对于正在选型量化方式或搭建推理服务环境的开发者具有直接的参考价值。 最近被问得最多的问题就是:vLLM部署到底难不难?如果是裸机部署,老实说坑不少,CUDA版本、PyTorch版本、GCC版本、Python版本,哪个对不上都能折腾你半天。但如果你走Docker这条路,很多环境问题其实在镜像层就已经解决了。这篇文章我就用一套完整可复现的流程,把vLLM跑起来,顺便讲讲官方镜像里到底装了什么、生产环境怎么配docker-compose,以及我实际踩过的几个坑。
这篇文章适合这几类人:想在自己的服务器上用Docker跑起大模型推理服务的开发者、正在研究vLLM源码但卡在环境搭建上的朋友、以及需要把推理服务做成标准交付物给团队或客户用的工程师。我会尽量把每一步的原理也说清楚,这样你不仅能把服务跑起来,出了问题也知道去哪里排查。
1. 裸机部署踩过的坑,让我最终转向Docker
先说说我为什么最终选了Docker这条路。vLLM本身是个挺挑剔的项目,它对Python版本有要求,对CUDA版本也敏感,还依赖特定版本的PyTorch。我第一次裸机部署的时候,光是把CUDA toolkit从11.8切到12.1就折腾了一下午,结果torch编译又报了奇怪的链接错误。后来干脆把服务器重装了系统,才把环境理干净。
用Docker之后,这些事全都不需要操心了。官方镜像vllm/vllm-openai自带了一套经过验证的CUDA、PyTorch和vLLM组合,你只需要保证宿主机有显卡驱动和NVIDIA Container Toolkit,剩下的全在容器里各玩各的。这个隔离性在生产环境特别重要——同一台机器上,一个容器跑vLLM,一个容器跑别的推理框架,互不干扰,依赖冲突这个问题几乎不存在了。
不过也要说明,Docker方案不适合所有场景。如果你要改vLLM源码做二次开发,或者在容器里频繁调试CUDA kernel,裸机或者dev容器反而更方便,因为改代码后的热重载、编译缓存都在本地。但如果你是“把服务跑起来、稳定提供服务”这个目标,Docker就是最优解。我现在的做法是:日常调试用裸机或开发容器改代码,对外提供稳定服务一律用Docker做镜像交付。
2. 官方vllm-openai镜像里到底打包了什么
很多人把vllm/vllm-openai当成一个黑盒来用,拉下来就跑。但如果你要基于它做源码级修改,或者想弄明白为什么镜像体积这么大,就得看看它到底装了什么。
2.1 镜像的核心分层结构
vLLM官方镜像的基础是nvidia/cuda的runtime镜像,比如12.4.1-runtime-ubuntu22.04。注意它用的是runtime版本而不是devel版本,这意味着镜像里没有nvcc编译器,只有运行CUDA程序必需的运行时库。这也是vLLM镜像体积虽然大,但没大到离谱的原因之一。
镜像里预装的内容包括:Python 3.10(不同版本镜像略有差异)、对应版本的PyTorch、vLLM主程序、OpenAI兼容的API服务器代码,以及一些推理依赖库如transformers、tokenizers、safetensors等。等于说,官方已经把“编译+安装vLLM”这一步做完了,你拿到的就是一个开箱即用的推理服务。
2.2 为什么pip install vllm偶尔会触发源码编译
如果你不在容器里跑,而是在自己的环境里pip install vllm,有时候会遇到“Building wheel for vllm...”这个过程,这其实是pip在从源码编译。vLLM包含大量CUDA kernel和C++扩展,PyPI上虽然有预编译wheel,但只覆盖一部分常见的CUDA版本和Python版本组合。一旦你的环境不在覆盖范围内,就会回退到源码编译,那个时间通常以十分钟甚至小时计算。
这也是我推荐用官方镜像的另一个原因——镜像里已经是编译好的产物,不需要你在部署环境里经历漫长的编译过程。如果你要改源码再跑,也可以基于官方镜像做增量构建,把源码拷贝进去替换掉原有版本,这样比从零构建快很多。
2.3 源码目录里值得关注的文件
虽然我们聊的是Docker部署,但标题里带源码两个字,我就顺带说下vLLM源码里几个关键位置。克隆vllm仓库后,你最先应该看的是vllm/entrypoints/openai目录,那是OpenAI兼容API服务的入口,包括api_server.py和serving_engine.py。如果你要改推理服务的HTTP层行为,改的就是这些文件。
真正核心的推理引擎在vllm/engine目录,包括异步引擎和LLM类。而vllm/model_executor目录下是各种模型的具体实现,比如你要看Qwen3怎么加载的,就去models目录下面找对应的文件。搞清楚这几个目录,就等于拿到了vLLM源码的地图,后续不管是排查问题还是做二次开发,都有的放矢。
3. 从拉镜像到跑通OpenAI接口的完整流程
下面进入正题,我把从零开始用Docker部署vLLM的完整流程走一遍。我这边的环境是Ubuntu 22.04,显卡是NVIDIA的,驱动版本550+,假设你已经装好了Docker。
3.1 第一步:确认宿主机GPU环境可用
在拉镜像之前,先确认两件事:显卡驱动是否正常、Docker能否访问GPU。
# 确认GPU驱动和显卡型号 nvidia-smi # 确认Docker已安装 docker --version如果nvidia-smi输出正常,看到显卡型号和驱动版本,说明驱动层面没问题。接下来要装NVIDIA Container Toolkit,这是Docker容器能使用GPU的关键组件。它的作用是把宿主机的GPU驱动和CUDA运行时映射进容器里。
# 添加NVIDIA Container Toolkit的apt源 curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update sudo apt-get install -y nvidia-container-toolkit # 重启Docker使配置生效 sudo systemctl restart docker装完以后可以用一个小镜像验证GPU透传是否正常:
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi如果容器里能正常输出nvidia-smi的信息,说明GPU透传已经通了,这一步是后续所有操作的基础。
3.2 第二步:拉取vLLM官方镜像
官方镜像在Docker Hub上叫vllm/vllm-openai,tag对应版本号。我建议拉最新的稳定版本,比如:
docker pull vllm/vllm-openai:latest这一步会下载几个GB的数据,取决于你的网络环境。如果你在国内,Docker Hub的下载速度可能不太理想,可以在/etc/docker/daemon.json里配置镜像加速器,然后重启Docker。这一步能让你省下大量的等待时间,后面踩坑部分我会再细说。
3.3 第三步:启动容器跑起Qwen3-8B
镜像拉下来之后,用docker run启动一个推理服务。这里我用Qwen3-8B作为示例,因为它是目前社区里用得很多的模型,8B参数量在单张24G显存的卡上也能跑起来。
docker run --gpus all \ --ipc=host \ --shm-size=2g \ -p 8000:8000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ vllm/vllm-openai:latest \ --model Qwen/Qwen3-8B \ --served-model-name qwen3-8b \ --max-model-len 32768 \ --gpu-memory-utilization 0.9第一次启动的时候,vLLM会从Hugging Face下载模型权重,中间那行-v ~/.cache/huggingface:/root/.cache/huggingface就是把宿主机上的模型缓存目录挂载进容器,这样模型下载一次之后,后续删了容器重建也不需要重新下载。
启动成功的话,终端会输出类似这样的日志:
INFO: Started server process [1] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://0.0.0.0:8000看到这四行,说明服务已经起来了,接下来验证一下OpenAI兼容接口是否正常工作。
3.4 第四步:用curl验证接口
vLLM启动后默认跑的是OpenAI兼容的API,这意味着你之前用OpenAI SDK写的代码,只需要改一下base_url就能切换到本地模型。
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b", "messages": [{"role": "user", "content": "介绍一下vLLM是什么?"}], "max_tokens": 512, "temperature": 0.7 }'正常情况下你会收到一个JSON响应,里面包含模型生成的文本、token用量等字段。到这一步,一个最基础的vLLM推理服务就算跑通了。
4. 显存估算与模型参数之间的适配关系
很多人在跑通之后,会想换一个更大的模型,或者想调整输入长度上限,结果一改参数就OOM。这背后的核心是显存管理。vLLM的显存主要花在两个地方:模型权重本身和KV Cache(键值缓存)。模型权重好算,参数量乘以每个参数的字节数就差不多了,但KV Cache是一个动态变化的部分,它跟max-model-len、并发数都有关系。
4.1 快速估算一个模型需要多少显存
以Qwen3-8B为例,如果用FP16精度加载,每个参数占2个字节,那么模型权重大约占16GB显存。如果你用INT8量化,权重占用降到8GB左右,INT4则降到4GB左右。当然这只是权重的部分,KV Cache还需要额外的显存空间,所以实际要求会比权重本身大不少。
这也是为什么官方建议Qwen3-8B至少需要24GB显存的卡——模型权重16GB,再加上KV Cache和推理过程中的临时缓冲区,24GB刚好是底线。
4.2 --gpu-memory-utilization参数怎么设
这个参数控制vLLM最多占用显卡显存的比例,默认是0.9。意思是如果有一张24GB的卡,vLLM会尝试占用约21.6GB,剩下的留给其他进程和显示输出。
我个人的经验是:
- 如果卡上只跑这一个服务,可以设到0.9甚至0.95。
- 如果还要跑别的任务或者共享GPU,设到0.6~0.7比较稳妥。
- 如果设太高导致OOM,先降下来看看日志里实际占用了多少,再逐步调优。
4.3 max-model-len影响的不只是输入长度上限
--max-model-len这个参数很多人理解成“输入输出的最大长度”,对,但它的真正意义是划定KV Cache的空间预留。这个值设得越大,KV Cache预留的显存就越多,能支撑的并发请求数也越多。但问题也随之而来——如果设得太大,可能导致模型加载完就没剩下多少显存给KV Cache了,服务能支持的并发反而变低。
一个比较稳妥的做法是:先查模型的config.json里max_position_embeddings字段,看看模型本身支持的最大长度是多少,然后设置一个不超过这个值的max-model-len。比如Qwen3-8B支持128K的上下文,但如果你实际用不到那么长,设32K或者64K就足够了,能省下不少显存给并发请求用。
4.4 多卡部署的tensor-parallel-size
单张卡跑不动大模型的时候,可以用多张卡跑张量并行。做法是在启动参数里加:
--tensor-parallel-size 2意思是把模型权重切分到2张卡上。这个参数设置的前提是你的机器有多张显卡,且显存大小一致,否则会报错或者性能受限于最小显存的卡。我实测下来,8B的模型在2张24G卡上跑张量并行,单卡显存占用大约在8~9GB,比单卡跑16GB轻松很多,而且因为算力增加,推理速度也会提升。
需要注意的是,--tensor-parallel-size的取值会直接影响模型的分布式加载方式,改了之后需要重启服务,同时重新观察显存占用情况。如果显存仍然吃紧,建议在卡数允许的范围内逐步调大数值。
5. 面向生产环境的docker-compose配置
把服务跑通只是第一步,真正要稳定地提供服务,还得用docker-compose把这套配置固化下来。这样不仅方便团队协作,也方便后续运维和重建。下面是我实际使用的一份docker-compose配置,你们可以直接参考。
5.1 完整示例配置
version: "3.8" services: vllm: image: vllm/vllm-openai:latest container_name: vllm-qwen3 restart: always ipc: host shm_size: "2g" ports: - "8000:8000" volumes: - ~/.cache/huggingface:/root/.cache/huggingface - /etc/localtime:/etc/localtime:ro environment: - HF_TOKEN=${HF_TOKEN} - CUDA_VISIBLE_DEVICES=0,1 command: - --model - Qwen/Qwen3-8B - --served-model-name - qwen3-8b - --tensor-parallel-size - "2" - --gpu-memory-utilization - "0.9" - --max-model-len - "32768" deploy: resources: reservations: devices: - driver: nvidia count: 2 capabilities: [gpu] logging: driver: json-file options: max-size: "50m" max-file: "5"这里有几个细节值得注意。第一,ipc: host和shm_size: "2g"是为了解决容器内共享内存不足的问题,vLLM的tokenizer和数据处理会在共享内存里暂存数据,默认的64MB肯定不够。第二,restart: always让服务在容器异常退出后自动重启,这在生产环境里几乎是必须的。第三,日志限制成50MB一个文件、最多5个文件,防止日志把磁盘撑满。
5.2 模型下载和缓存的持久化方案
~/.cache/huggingface:/root/.cache/huggingface这段挂载是我推荐的核心。模型权重下载之后,如果你重新创建容器,只要挂载在同一个目录,vLLM就会直接复用本地的缓存,不用再上网下载一次。这个在带宽有限的环境下尤其重要,省下来的时间非常可观。
另外,如果你是先在别的服务器上把模型下好了,想拷贝到目标机器上,直接把整个huggingface缓存目录拷贝过去即可。vLLM的缓存识别通过目录结构和文件名判断,不用额外的初始化命令。
5.3 环境变量与密钥管理
在compose文件里,我用了${HF_TOKEN}来引用宿主机环境变量,这是一个推荐做法。因为有些模型在Hugging Face上是gated model,需要登录token才能下载。你可以在宿主机上准备一个.env文件,里面写入token,compose启动时会自动读取。这样token不会硬编码到配置里,避免代码仓库里泄露密钥。
# .env文件 HF_TOKEN=hf_your_token_here启动的时候用docker compose up -d,它会自动读取当前目录下的.env文件。之后如果要更新模型版本或者调整参数,改compose文件再执行docker compose up -d即可。
6. 我在部署过程中踩过的几个典型坑
这部分是我个人实战中遇到并解决过的问题,写出来供大家参考,希望能帮你省去一些排查时间。
6.1 拉镜像和下载模型时网络太慢
Docker Hub拉取vLLM镜像,在网络不好的时候确实痛苦。我遇到过拉取几个GB的镜像卡了一个小时的情况。解决办法是在/etc/docker/daemon.json里配置镜像加速器:
{ "registry-mirrors": ["https://docker.m.daocloud.io"] }配置完重启Docker,拉取速度会有明显提升。模型权重从Hugging Face下载慢的话,可以考虑在环境变量里设置HF_ENDPOINT指向镜像站点,这样下载速度会快不少。注意,如果用的是内网代理或者特定网络环境,需要在VLLM容器启动时带上HTTP_PROXY和HTTPS_PROXY环境变量。
6.2 容器内提示CUDA driver版本不兼容
这个问题的典型表现是启动时日志报错:
CUDA error: the provided PTX was compiled with an unsupported toolchain常见原因有两种:一是宿主机显卡驱动版本太老,容器内的CUDA版本要求更高的驱动;二是Docker没有正确传递GPU设备,导致容器里拿不到宿主机的CUDA驱动。排查思路是先看宿主机nvidia-smi的驱动版本是否满足vLLM镜像中CUDA版本的要求,再看启动时有没有加--gpus all。
如果驱动版本偏老,最简单的办法是升级NVIDIA驱动;如果不想升级驱动,就选择对应旧CUDA版本的vLLM镜像。vLLM官方镜像的tag里会标明CUDA版本,比如vllm/vllm-openai:cuda-12.1就是用CUDA 12.1的版本,对驱动要求相对低一些。
6.3 启动时OOM或者请求时OOM
OOM问题分成两类。一类是模型加载阶段就OOM,原因是模型权重加预留显存超过了显卡总容量,这时候优先减小--gpu-memory-utilization、调低--max-model-len,或者开量化。另一类是请求处理过程中OOM,比如并发请求同时到达,KV Cache被占满,可以降低并发数、减小max-model-len,或者做请求排队。
有一次我遇到了一个比较隐蔽的情况:Qwen3-8B配--max-model-len 131072,模型占完显存后KV Cache几乎没空间了,导致处理一个很短的请求都报显存不足。后来把--max-model-len降到32768,问题立刻解决。所以这个参数务必根据实际场景来设置,不是越大越好。
6.4 GPU利用率跑不满,推理速度上不去
这个问题分多种原因,常见的是模型体积太小、并发请求太少导致GPU喂不饱;或者是max-model-len设定太短,导致单次请求能处理的数据量偏小,GPU算力空转;还有一种情况是CPU成为瓶颈,比如tokenizer在大并发下占满CPU,导致GPU等待数据。
我的排查思路是:先用docker stats看容器CPU和内存占用,再用nvidia-smi看GPU利用率和显存占用,最后看vLLM日志里的推理耗时指标。如果是并发不够,就适当提高并发请求数;如果是CPU瓶颈,就保证容器有足够的CPU配额,或者把tokenizer放到单独的CPU核心上运行。
6.5 修改容器内的配置不生效
很多人在跑vLLM的时候,想在容器里改环境变量或者改某些配置,比如调整日志级别。改了之后发现服务没有变化,原因往往是容器里的进程已经启动了,配置文件被加载到内存里了,要重启容器才能生效。
建议在改动环境变量或挂载文件后,用docker compose down和docker compose up -d完整重建容器,而不是只执行docker restart。因为每次restart会重新读取配置,但有些状态还是会被缓存,最简单可靠的方式始终是down掉再up。
7. 我个人实践下来的一些体会
最后分享几个我认为值得留意的点。第一个是模型版本锁定。vLLM和模型文件的兼容性并不是绝对的,大版本升级时经常会出现某个模型结构适配不上的情况。所以生产环境一定要锁定vLLM镜像的tag,不要用latest随缘更新。每次升级之前,先在测试环境跑一轮兼容性验证,确认无误再上生产。
第二个是预制模型目录很值得做。我的做法是,在跳板机上下载好常用模型,然后通过内网传输到GPU服务器,或者直接把缓存目录做成镜像的一部分。这样新的GPU服务器加入集群时,不用在现场拉好几个小时的模型,部署时间从天级别缩短到分钟级别。
第三个是不要忽视API的并发能力和限流配置。vLLM本身支持较高的并发,但如果没有在API网关层做限流,突发流量会把GPU显存占满,导致请求排队时间急剧上升。我当时遇到过线上服务突然卡死的情况,排查下来就是并发请求数超过KV Cache容量造成的。后来加了一层简单的请求排队和超时熔断,服务稳定性明显提升。
好了,Docker部署vLLM这条路我算是走通并稳定跑了一段时间了。希望这篇文章能帮你少走一些弯路。如果你在部署过程中遇到其他奇怪的报错,欢迎在评论区交流,我看到了会尽力解答。
本文还有配套的精品资源,点击获取