最近一直在折腾本地大模型的部署,前阵子在一个 Windows 工作站上把 vLLM 跑起来,加载 Qwen3-8B-FP8 这个模型,整个过程踩了不少坑。今天把这套流程完整记下来,给想在 Windows 上体验 vLLM 的朋友做个参考。这套方案的核心就是用 Docker Desktop 配合 WSL2 后端,绕开 vLLM 官方对 Linux 的依赖,再配合 ModelScope 拉权重,把模型服务跑起来。文章会覆盖环境准备、显存评估、启动参数、接口测试、性能验证和问题排查,尽量让你照着做也能跑通。
1. 为什么要在 Windows 上折腾 vLLM,这套方案又适合谁
1.1 这套组合解决了什么实际问题
很多做算法、做后端、做 AI 应用集成的朋友,日常工作主力机就是 Windows。但是手头任务又需要跑一个 OpenAI 兼容的推理服务,要能支撑并发请求、要做流式输出、要能快速切模型做对比实验。这时候第一反应是找个 Linux 服务器,可有时候资源就在手边,一台带 NVIDIA 显卡的 Windows 工作站就摆在那,为什么不用起来。
Qwen3-8B-FP8 是个很合适的落地选择。8B 这个规模,单卡能跑,效果比小模型强一个档次;FP8 量化之后权重占用比 BF16 版本小一半左右,对显存和带宽的要求更友好。配合 vLLM 的 PagedAttention、Continuous Batching 这些机制,即便是消费级显卡也能获得不错的吞吐量。
所以这套组合解决的实际问题就是:在不需要额外 Linux 服务器的情况下,用 Windows 本机显卡,把一个生产可用的 LLM 推理服务跑起来,并提供标准 OpenAI 接口,供本地应用、脚本、甚至局域网内其他设备调用。
1.2 vLLM 在 Windows 上的“官方空白”怎么绕
vLLM 官方文档对 Windows 的支持态度一直很暧昧。核心组件大量依赖 Linux 特有的机制,比如 NCCL、共享内存 IPC、CUDA Graph 申请等。直接 pip install vllm 跑在 Windows 原生 Python 里,大概率会在编译 xformers 或者 flash-attention 的时候翻车,即便过了编译,运行时的共享内存和调度逻辑也可能不正常。
绕行方案主要有三条路线。第一条是用 WSL2 直接装 Linux 环境,再把 vLLM 装在 WSL2 内部;第二条是用 Docker Desktop,让它使用 WSL2 后端,在容器里跑 vLLM;第三条是在 Windows 上装 Linux 虚拟机,配置 GPU 直通。三条路我都试过,最省心的还是 Docker Desktop 方案。原因很直接:镜像里 vLLM 和 CUDA 的版本搭配是维护好的,不需要自己在 WSL2 里折腾 CUDA toolkit、编译依赖、Python 环境。你要做的只是把 NVIDIA 驱动装好,把 Docker Desktop 配成 WSL2 模式,然后一行命令拉镜像跑服务。
| 方案 | 难度 | 踩坑概率 | 适合人群 |
|---|---|---|---|
| Windows 原生 pip 安装 | 高 | 极高 | 不建议,除非你有大把时间处理编译问题 |
| WSL2 直接安装 vLLM | 中高 | 中 | 喜欢原生 Linux 环境、愿意自己管理 Python 环境的人 |
| Docker Desktop + WSL2 后端 | 中低 | 低 | 想快速跑通服务、不想折腾依赖的人,也是本文主推 |
| 独立 Linux 虚拟机 GPU 直通 | 高 | 高 | 特殊隔离场景,一般用不上 |
我个人建议,如果只是要跑服务、做接口联调、验证效果,直接走 Docker Desktop。如果是深度改 vLLM 源码做二次开发,那还是老实装个 Linux 环境,容器里改代码不方便。
2. 动手之前,先把显存和基础环境摸清楚
2.1 显存需求不是猜的,算一笔账就明白
很多人在部署时翻车,翻在显存不够。Qwen3-8B-FP8 的权重是 FP8 精度,每个参数占 1 字节,所以权重本身大概 8GB 多一点。但这只是模型权重,推理时还需要额外的显存来放 KV Cache、CUDA 上下文、激活值、临时计算缓冲区。如果你用 vLLM,默认会尽量利用空闲显存来缓存历史 KV,所以 max_model_len 给得越大,KV Cache 占用越多。
一个粗略的估算公式是:总显存需求约等于权重大小 + KV Cache + 2GB 左右的固定开销。比如你给 vLLM 设置--max-model-len 8192,一般会再吃掉 2-4GB 显存。这样总需求就在 13GB 到 16GB 之间。也就是说,16GB 显存的显卡能跑,但比较紧张,建议把显存利用率参数调低一些,并发调小一些;24GB 显存就很宽裕了,可以放开一点限制。
我实际测试时用的是一张 24GB 显存的卡,设置--gpu-memory-utilization 0.92,--max-model-len 8192,跑起来剩余显存还有富余。如果你的卡只有 16GB,建议把--max-model-len降到 4096,--gpu-memory-utilization设为 0.9,然后并发设小一点,这样也能稳定运行。
2.2 Docker Desktop 安装的几个细节
Docker Desktop 在 Windows 上的安装本身不复杂,但有几个前提要满足。系统最好是 Windows 10/11 专业版或企业版,因为这些版本支持 Hyper-V 和 WSL2。家庭版虽然也能跑,但会费更多周折。安装前建议先把 WSL2 的内核升级一下,用管理员权限打开 PowerShell,执行wsl --update,避免之后 WSL2 和 Docker 对接时出现内核版本过旧的问题。
安装 Docker Desktop 时,安装向导会让你选择使用 Windows 容器还是 Linux 容器,这里必须选 Linux 容器。装完进入设置,在 General 里勾选 “Use the WSL 2 based engine”,然后在 Resources → WSL Integration 里,把你要用的那个 WSL 发行版开关打开。这一步很关键,如果不打开集成,Docker 命令在 WSL 里会无法调用 Windows 侧的 Docker daemon。
还有一个小细节:Docker Desktop 启动后,默认会占用比较大的内存。你可以在 Resources → Advanced 里把内存调到 12GB 或更高,否则后面跑大模型的时候,WSL2 内存不足会直接 OOM。
2.3 NVIDIA 驱动和 WSL2 的 CUDA 对接
Windows 侧的 NVIDIA 显卡驱动是基础,WSL2 里面不需要再单独装显卡驱动,它直接复用 Windows 的驱动。但这里有个前提:驱动版本必须足够新。vLLM 镜像里默认的 CUDA 版本通常比较高,老驱动会跑不起来。我建议把 NVIDIA 驱动更新到最新稳定版,反正现在 Game Ready 驱动和 Studio 驱动在 WSL2 下都能用,选一个装了就行。
装完之后,在 WSL2 里打开终端输入nvidia-smi,如果能看到显卡信息和驱动版本,说明 CUDA 对接正常。如果提示找不到命令,别慌,可能是 WSL2 里没有安装 CUDA toolkit 的工具链,但 nvidia-smi 一般都会自动映射过来。实在不行,去 NVIDIA 官网下载对应 WSL2 的 CUDA toolkit 安装一遍就好了。
验证完显卡,再验证 Docker 能不能用 GPU。启动 Docker Desktop 后,在 WSL2 终端里执行:
docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi如果能看到显卡信息,说明 Docker 侧 GPU 透传没问题。这一步建议务必测一下,很多人后面容器里报“找不到 CUDA driver”,问题就出在这。
3. 模型权重怎么选、怎么拉
3.1 为什么非选 FP8 这个量化版本
Qwen3 系列发布时提供了多种精度版本,从 BF16 到 FP8 都有。FP8 是 8 位浮点,存储占用是 BF16 的一半,但精度损失相比 INT8、INT4 这类整数量化要小很多。对大模型推理来说,FP8 是一个性能和效果平衡得比较好的点。
更关键的是,vLLM 对 FP8 权重支持比较原生。你不需要提前做量化转换,直接把 Qwen3-8B-FP8 权重目录丢给 vLLM,它会自动识别权重里的量化参数并加载。如果你拿的是 BF16 版本,不想用 FP8,那也可以,但显存占用直接翻倍,速度也会受带宽影响。我用同样的测试集对比过,FP8 版本和 BF16 版本在生成质量上差距很小,但显存占用少了将近一半,这使得 16GB 显存跑 8B 模型成为可能。
所以如果你主要目的是部署服务而不是做量化研究,直接选官方推出的 FP8 版本最省事。
3.2 用 ModelScope 拉权重的实操过程
国内网络环境下,从 Hugging Face 拉大文件经常超时、断流。更省心的是用 ModelScope,它本身就是模型托管平台,下载速度快,而且支持类似 Hugging Face 的命令行工具。Qwen3-8B-FP8 在 ModelScope 上有官方仓库。
先装一下下载工具:
pip install modelscope然后找个目录,执行:
modelscope download --model Qwen/Qwen3-8B-FP8 --local_dir D:/models/Qwen3-8B-FP8--local_dir指定模型保存的本地路径。这里我强烈建议,如果这台机器上还装了 WSL2 和 Docker,不要直接下载到 Windows 的 D 盘,而是把模型放到 WSL2 内部的 Linux 文件系统里,比如~/models/Qwen3-8B-FP8。原因是 Docker 容器挂载 Windows 盘符下的路径时,I/O 性能损耗明显,而且经常出现权限问题。放到 WSL2 内,读取速度快,而且不需要处理复杂的路径映射。
下载完成后,确认一下目录里有几个关键文件:config.json、model.safetensors.index.json、若干分片的.safetensors文件,以及 tokenizer 相关文件。只要这些文件齐全,模型就能被 vLLM 正常加载。
3.3 模型目录和 Docker 挂载方式
模型文件准备好了,接下来要想清楚怎么让容器访问到它。如果模型放在 WSL2 的~/models下,那么在 Docker 启动命令里可以直接挂载 Linux 路径,因为容器本来跑在 WSL2 后端上。比如:
docker run --gpus all -v ~/models:/models -p 8000:8000 ...如果模型在 Windows 的 D 盘,就需要把路径转换成 Docker 能识别的格式:
docker run --gpus all -v D:/models:/models -p 8000:8000 ...实测下来,Windows 路径挂载偶尔会出现权限错误,尤其是文件属主不一致时。所以我的建议始终是:模型放 WSL2 内部,挂载 Linux 路径。另外,模型权重的读取频率很高,放在 WSL2 的 ext4 文件系统上,比放在 NTFS 挂载点里明显更快,模型加载时间能缩短不少。
4. 启动 vLLM 服务,关键参数逐个拆解
4.1 一行命令跑起来
镜像选择上,用官方维护的vllm/vllm-openai。带 OpenAI API server 支持,启动即服务。选一个稳定的 tag,我用的版本对应 vLLM 0.6 以上的分支,功能比较全。
完整的启动命令如下:
docker run --gpus all \ --ipc=host \ -p 8000:8000 \ -v ~/models:/models \ vllm/vllm-openai:latest \ --model /models/Qwen3-8B-FP8 \ --served-model-name qwen3-8b-fp8 \ --max-model-len 8192 \ --gpu-memory-utilization 0.92 \ --enforce-eager这里面的参数,我逐个说一下为什么这么设。--ipc=host是 vLLM 官方推荐的,因为 vLLM 运行时会在进程之间共享内存,默认容器 IPC 限制太小可能导致奇怪的错误,直接放开最省事。
--served-model-name是给 API 调用时用的模型名。这个名字可以随意改,但必须和后续 curl 请求里的model字段保持一致。很多人忽略这个参数,用默认的模型路径名,结果请求时报 model not found,其实改一下就通了。
--max-model-len控制模型最长上下文长度。8192 是比较保守的选择,既能满足大部分对话需求,又不会让 KV Cache 占太多显存。如果你的显存确实紧张,可以降到 4096,能明显看到显存占用下降。
--gpu-memory-utilization 0.92表示允许 vLLM 使用最多 92% 的显存。留一点余量给 CUDA context 和其他开销。如果显存很紧张,设成 0.85 更稳。
--enforce-eager这个参数很有用,它让 vLLM 跳过 CUDA Graph 的捕获,虽然会损失一点性能,但能显著降低启动时的显存峰值,也避免某些显卡在 CUDA Graph 捕获阶段直接报错。首次跑服务,建议先加上这个参数,确认能跑通后再去掉测试性能。
4.2 启动日志应该看什么
执行启动命令后,会有几秒到几十秒的加载时间。第一次启动时模型权重要从磁盘读进显存,标志性日志是Loading weights took xx seconds。如果这步卡住,大概率是模型文件损坏或者路径不对。
接着会看到 CUDA graph 相关的日志,以及显存分配信息。日志末尾会出现类似Starting vLLM API server on http://0.0.0.0:8000的描述,这时候服务就起来了。如果显存不够,日志里会明确提示 GPU memory 不足,需要调整 max-model-len 或者 gpu-memory-utilization。
还有一个常见情况:终端里正在打印一堆和 cache 相关的日志,看起来像卡住了。其实是在做 block 分配和 prefix cache 初始化,只要没报错,耐心等一等就好。
4.3 OpenAI 兼容接口的验证
服务起来后,先用最简单的请求确认它活着:
curl http://localhost:8000/v1/models返回的 JSON 里应该包含你设置的模型名qwen3-8b-fp8。这一步如果通了,说明服务正常、鉴权没开(vLLM 默认不开鉴权)、端口映射也没问题。接下来发一个聊天补全请求:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b-fp8", "messages": [{"role": "user", "content": "你好,介绍一下你自己"}], "max_tokens": 512, "temperature": 0.7 }'正常情况下会返回一段带 generated text 的 JSON。如果返回 404,大概率是模型名没对上;如果返回 400,通常是请求格式有问题,检查 messages 结构是否合规。
5. 验证性能,并搞清楚 vLLM 和其他工具的区别
5.1 用 Python 脚本测并发和流式输出
纯 curl 只能验证功能。想测并发吞吐,建议写个简单的 Python 脚本,用openai库发请求。安装依赖:
pip install openai然后写脚本:
from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", ) response = client.chat.completions.create( model="qwen3-8b-fp8", messages=[{"role": "user", "content": "用一句话解释什么是大语言模型"}], max_tokens=256, stream=True, ) for chunk in response: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True)stream=True 是流式输出,OpenAI 兼容接口原生支持,这也是 vLLM 的优势之一,特别适合做对话类应用。实测流式模式下首 token 返回速度很快,体感很接近商用 API。
如果想压测并发吞吐,用 vLLM 自带的 benchmark 脚本最标准。进入容器:
docker exec -it <container_id> /bin/bash然后在容器内部执行:
python3 -m vllm.bench.benchmark_serving \ --model /models/Qwen3-8B-FP8 \ --served-model-name qwen3-8b-fp8 \ --num-prompts 100 \ --request-rate 10这个命令会模拟 100 个请求、每秒 10 个并发提交,最后输出吞吐量、TTFT(首 token 延迟)、TPOT(每个 token 生成延迟)等指标。这些数据比肉眼体感靠谱得多,如果要写对比报告,用这个数据就行。顺便说一句,sglang 也是个不错的推理框架,和 vLLM 功能重叠很多,但部署复杂度和 Windows 友好程度上,vLLM 更胜一筹,除非你要做很复杂的图结构推理,否则没必要换。
5.2 和 LM Studio 这类图形化工具比怎么样
不少人在 Windows 上跑大模型,第一时间想到的是 LM Studio 这类工具。它确实方便,图形界面点一点就能下载模型、启动聊天。但如果你要做的是对外提供 API 服务、处理并发请求、接入自己的应用,LM Studio 就有明显的短板:并发能力弱、可配置参数少、吞吐量上不去。
| 对比项 | vLLM | LM Studio |
|---|---|---|
| 部署方式 | Docker 或 Linux,命令行为主 | Windows 原生 GUI |
| 并发吞吐 | 高,PagedAttention + Continuous Batching | 低,主要面向单用户聊天 |
| OpenAI 兼容 API | 原生支持,接口规范 | 支持,但功能裁剪较明显 |
| 高级参数 | 丰富,可精细控制显存、调度、量化 | 有限 |
| 上手难度 | 中等,需要命令行 | 低,安装即用 |
我的结论很明确:如果你只是在电脑上自己聊天、玩一玩,LM Studio 很好用;如果你要做应用集成、服务化部署、并发压测,vLLM 才是正解。而且 vLLM 一旦跑通了,后面换模型、调参、接监控都很顺手,属于“一次投入长期受益”的部署方式。
6. 常见问题与排查技巧实录
6.1 问题速查表
实际部署过程中,我踩过的坑不少,整理成一张速查表,方便你遇到问题时对照着查。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 容器启动报 CUDA driver not found | Windows 驱动版本过旧,或 Docker 未启用 GPU 支持 | 升级 NVIDIA 驱动,确认docker run --gpus all可用 |
| WSL2 内存不足导致 OOM | Docker Desktop 默认内存分配太小 | 在 Docker Desktop 设置里调大内存,建议 12GB 以上 |
| 模型加载卡住或报错 | 模型文件不完整,或路径挂载错误 | 用 ModelScope 重新下载,确认文件齐全;模型尽量放 WSL2 内 |
| 端口 8000 被占用 | 本地已有服务占用 | 改成-p 8001:8000,请求时访问对应端口 |
| 请求返回 model not found | --served-model-name没设置或不对 | 设置该参数,并在请求体 model 字段保持一致 |
| 启动时显存瞬间打满 | CUDA Graph 捕获阶段显存峰值过高 | 加--enforce-eager,降低--gpu-memory-utilization |
| 生成速度很慢 | 模型放在 NTFS 挂载路径,或未启用 GPU 透传 | 把模型移到 WSL2 内,确认 GPU 透传正常 |
| Docker 命令在 WSL2 里不生效 | WSL Integration 没打开 | Docker Desktop → Resources → WSL Integration 勾选对应发行版 |
这张表是我觉得最实用的部分。很多问题看起来神秘,实际就是几个固定环节出错。
6.2 几个值得记住的独家操作技巧
第一个技巧:启动前先确认 GPU 透传。不管别的,先跑一遍docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi。这个测试能过滤掉一半的后续问题。
第二个技巧:模型下载地址选择上,优先 ModelScope。别在 Hugging Face 上死磕,尤其是大文件多的情况。ModelScope 对国内网络友好,下载速度稳定,偶尔断流也能断点续传。
第三个技巧:Windows 上重启服务时,建议在 WSL2 里先执行docker ps -a看看容器状态。有时容器挂在异常退出状态,重新 run 之前先停掉旧容器,不然端口会冲突。批量清理的时候可以用docker rm -f $(docker ps -aq),简单粗暴。
第四个技巧:如果你要同时跑多个模型,建议每个模型分不同端口启动,比如 8000、8001、8002。这样切换模型时不用重启现有服务,客户端只需要改 base_url。实测下来,单卡跑一个 8B FP8 模型比较合适,如果同时跑两个 7B 级别模型,显存会非常紧张,不建议这么干。
第五个技巧:启动参数记得把--served-model-name设置成简短的名字,比如qwen3-8b-fp8,别用一长串路径。API 调用方不会关心你的模型是从哪个目录加载的,他们只关心名字是否好记、好写。这个细节一旦忽略,对接时很烦。
第六个技巧:如果你觉得 vLLM 默认调度策略不够用,可以在启动时加--enable-prefix-caching。这个参数在处理多轮对话、长文档问答时能明显降低重复 prefill 的算力消耗,实测对特定场景有提升。不过要注意,它会额外增加一些显存开销,显存紧张时先不开。
7. 一些个人体会
最后说点实在的。我在 Windows 上部署 vLLM 这条路上来回折腾过几回,最深的体会是:别在“原生安装”上死磕。Windows 不是 vLLM 的官方战场,硬要在原生 Python 环境里编译,等于跟整个生态做对抗。Docker Desktop 加 WSL2 这套路,表面看是绕了一圈,实际上是最省力的路径。
还有一点,模型权重的存放位置真的会影响体验。我最初图方便把模型放在 Windows 的 D 盘,结果启动加载时间远超预期,后来挪到 WSL2 内部,加载速度快了不少。这种细节没人写在官方文档里,只有自己踩过才知道。
这套部署方案跑通之后,你就拥有一个本地的高性能 OpenAI 兼容推理服务。后面无论是接 Dify、FastGPT 这类应用框架,还是写脚本批量处理文本,都能直接用,不用再依赖外部 API,数据也完全在自己手里。如果你现在正卡在 Windows 部署这一步,照着上面的流程走一遍,应该能少走不少弯路。