先说句实在话:vLLM 在 Windows 上不能直接装、不能直接跑,想在一台 Windows 机器上把 Qwen3-8B-FP8 推理服务拉起来,绕不开 WSL2。我这次从完全空白的 Ubuntu 子系统开始,一步步装驱动、配 CUDA、拉模型、起服务,最后成功用 OpenAI 兼容接口跑通了对话和压测。整个过程踩了不少坑,这篇就当作一份可以照着抄的实战记录。适合手里有 NVIDIA 显卡(显存建议 12GB 以上)、想在本地体验大模型部署、或者后续准备迁移到 Linux 服务器的同学参考,WSL2 里的操作和原生 Ubuntu 几乎一模一样,学会这套后面换服务器也通用。
1. 方案选型:为什么 Windows 上跑 vLLM 首选 WSL2
1.1 vLLM 在 Windows 原生环境的天花板
vLLM 从设计之初就没打算兼容 Windows。它的核心依赖 PagedAttention、CUDA Graph、以及大量针对 Linux 编译的 CUDA 扩展算子,这些组件和 Linux 内核、glibc、CUDA runtime 的耦合非常深。Windows 上虽然有 CUDA 支持,但 vLLM 官方根本不提供 Windows 分支,社区也没有维护完整的移植版本。真有人尝试在 Windows 下硬编译,最终基本都会卡在算子注册、链接库缺失、甚至是文件路径分隔符这类莫名其妙的问题上。
所以大部分“Windows 跑 vLLM”的教程都会让你装 WSL2,这其实不是绕路,而是最省力的正路。WSL2 本质是个轻量虚拟机,里面跑完整 Linux 内核,微软通过 GPU-Paravirtualization 技术把 NVIDIA 显卡直接透传给 Linux 子系统。vLLM 在 WSL2 里跑,跟在原生 Ubuntu 服务器上跑,底层环境几乎一致,性能损耗也很小,我实测推理吞吐大概比裸 Linux 低 3% 到 5%,日常使用根本感知不到差别。
1.2 WSL2、Docker、原生安装三条路线怎么选
我把市面上的方案分成三类,分别说下适用场景。
第一种是 WSL2 内 pip 安装 vLLM。这条路环境干净、链路透明,vLLM 版本、PyTorch 版本、Python 版本都能自己控制,出问题也方便排查,适合想深入理解部署原理、后续要改代码或者调试源码的人。缺点是步骤多,要手动装 Miniconda、配环境、处理依赖冲突。
第二种是 Docker Desktop + vLLM 官方镜像。官方在 Docker Hub 上维护了vllm/vllm-openai镜像,拉下来docker run --gpus all就能跑,CUDA 和 PyTorch 都不用自己装,最适合快速验证“这模型到底能不能跑”。缺点是镜像体积大,动辄几个 GB;Docker Desktop 本身还吃内存;如果要改 vLLM 内部参数或二次开发,容器里的环境反而碍手碍脚。
第三种是 Windows 原生安装。结论很明确,不推荐,纯属浪费时间。
我的建议是:第一次接触 vLLM、想快速看效果,用 Docker;准备长期使用、或者后面要接别的 Python 项目,用 WSL2 手动装。这篇教程默认走 WSL2 手动安装路线,因为步骤最完整,能帮你把每个环节都搞清楚。
1.3 硬件门槛与显存预算
先确认你的硬件够不够。显卡必须是 NVIDIA,这是硬性要求;AMD 在 vLLM 上只有实验性的 ROCm 路径,一线使用基本没有维护,别碰。显存方面,Qwen3-8B-FP8 的 FP8 权重大约占 8GB,再加上 CUDA context、KV cache、运行时激活,12GB 显存属于勉强能跑,需要把上下文长度压到很低;16GB 比较舒适,24GB(如 RTX 3090/4090)基本随便造。
Windows 系统版本建议 Windows 10 21H2 以上或 Windows 11,老版本 WSL2 的 GPU 透传支持不完整,容易出现“装好了但检测不到显卡”的诡异情况。如果不确定自己系统版本,Win+R 输winver看一眼就行。
2. 环境搭建:WSL2 + GPU 透传 + Python 全流程
2.1 五分钟装好 WSL2 与 Ubuntu
安装 WSL2 现在非常简单,管理员身份打开 PowerShell 或 CMD,执行一条命令:
wsl --install这条命令会自动启用 WSL2 功能、安装虚拟化平台、下载并安装 Ubuntu 发行版。装完按提示重启电脑,重启后 Ubuntu 会自动弹出初始化窗口,设置 Linux 用户名和密码。注意这个账号密码只在 WSL 内部生效,和 Windows 登录账号无关,但一定要记好,后面sudo安装软件都要用。
如果你的机器以前装过旧版 WSL,先手动升级到最新再继续:
wsl --update wsl --set-default-version 2装完验证一下版本:
wsl -l -v看到 Ubuntu 的 VERSION 列是 2,说明 WSL2 正常。如果显示 1,执行wsl --set-version Ubuntu 2手动转换。首次转换可能要几分钟,耐心等。
2.2 让 WSL2 识别你的 NVIDIA 显卡
这一步很多人栽跟头。WSL2 里不需要单独装 NVIDIA 驱动,驱动是通过 Windows 侧透传进去的,但前提是 Windows 侧的驱动版本足够新。Windows Update 自动推送的驱动经常偏旧,WSL 的 GPU 透传功能对驱动版本有要求,旧驱动会导致 Ubuntu 里根本看不到显卡。
建议直接去 NVIDIA 官网下载最新的 Game Ready 或 Studio 驱动,手动装完重启。然后进入 Ubuntu 终端:
nvidia-smi如果你能看到类似这样的输出:
+-----------------------------------------------------------------------------+ | NVIDIA-SMI 560.94 Driver Version: 560.94 CUDA Version: 12.6 | +-----------------------------------------------------------------------------+ | GPU Name Persistence-Mode | Bus-Id Display-Active | | 0 NVIDIA GeForce RTX 4090 On | 00000000:01:00.0 On | +-----------------------------------------------------------------------------+说明 GPU 透传成功。如果提示command not found,先确认驱动是不是太旧;如果提示No devices were found,多半是 Windows 驱动版本不够,去升级一次再试。
这里有个小坑:
nvidia-smi里显示的 CUDA Version 是驱动支持的最高 CUDA 版本,不是系统里实际安装的 CUDA toolkit 版本。vLLM 的 PyTorch 轮子会自带 CUDA runtime,所以其实不一定要手动装 CUDA toolkit,但这一步能确认 GPU 可见性,非常关键。
2.3 Python 环境隔离与文件系统避坑
WSL2 的 Ubuntu 自带 Python 3,但我不建议直接用系统 Python,包管理太混乱。装 Miniconda 是最省心的方式:
cd ~ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh安装过程中一路 yes,装完重开终端让 conda 生效。然后创建一个干净的 Python 环境:
conda create -n vllm python=3.11 -y conda activate vllmPython 3.11 是我测下来和 vLLM 0.8.x 兼容性最好的版本,3.12 也能用,但某些依赖包在 3.12 上的轮子更新不及时,遇到问题排查成本高,没必要冒险。
文件系统这块必须提醒:模型权重一定要放在 WSL2 的 Linux 文件系统里,也就是~/目录下,不要放到/mnt/c/开头的 Windows 盘路径。WSL2 访问/mnt/c/是跨文件系统操作,走的是 9P 协议,IO 性能比原生 ext4 慢一个数量级。模型文件动辄 10GB 以上,加载时慢到你怀疑人生。我一开始图省事把模型放在 D 盘,加载直接卡了二十多分钟;后来挪到~/models/下,一分钟不到就加载完了。
3. 安装 vLLM 与获取 Qwen3-8B-FP8 权重
3.1 锁版本安装 vLLM,验证 GPU 状态
激活 conda 环境后,安装 vLLM:
pip install vllm这条命令会自动拉取 vLLM 主体以及 PyTorch、transformers、tokenizers 等依赖。我的建议是安装时固定一个经过验证的版本,比如:
pip install "vllm==0.8.5"vLLM 版本迭代非常快,几乎每个月都有大版本更新,偶尔会引入破坏性变更。锁定版本后,遇到问题查文档、搜 issue 都方便定位。以后想升级再主动改版本号。
安装完做两个验证。先确认 vLLM 本身:
python -c "import vllm; print(vllm.__version__)"再确认 PyTorch 能不能调用 GPU:
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"输出True NVIDIA GeForce RTX 4090这类信息,说明 PyTorch 已经正确识别显卡,整个 GPU 链路彻底打通。
注意区分“显卡驱动可用”和“PyTorch 可用”。
nvidia-smi正常只能说明驱动层没问题,而 PyTorch 能否调用 GPU 取决于 CUDA 运行库是否匹配,所以这两个验证都要做。
3.2 下载 FP8 权重:Hugging Face 与 ModelScope 双通道
模型权重的获取,国内用户建议优先用 ModelScope,下载速度快,不需要额外配置。先安装 CLI:
pip install modelscope然后下载模型,这里模型 ID 用Qwen/Qwen3-8B-FP8举例,实际下载时以你在 ModelScope 或 Hugging Face 上搜到的仓库名为准:
modelscope download --model Qwen/Qwen3-8B-FP8 --local_dir ~/models/Qwen3-8B-FP8Hugging Face 命令类似:
pip install huggingface_hub huggingface-cli download Qwen/Qwen3-8B-FP8 --local-dir ~/models/Qwen3-8B-FP8无论走哪个通道,下载完成后都检查一下目录结构:
ls -lh ~/models/Qwen3-8B-FP8正常情况下你会看到config.json、tokenizer.json、model.safetensors.index.json,以及若干分片后的model-00001-of-0000X.safetensors文件。
还有个验证技巧:看总文件大小。8B 参数用 FP8 存储,权重总量应该在 8GB 到 9GB 之间。如果模型文件加起来接近 16GB,那这个仓库很可能是 BF16 原版,只是名字里带了“FP8”,启动参数就要相应调整。
3.3 FP8 格式细节与显存占用实测
FP8 是 8 位浮点数格式,相比 BF16(16 位浮点)权重占用直接减半。Qwen3-8B 的 BF16 权重约 16GB,FP8 权重约 8GB。显存省下来的同时,由于数据传输量减少,推理速度通常也能提升 20% 到 40%,精度损失对对话任务来说几乎感知不到。
但这里有个容易踩的坑:FP8 量化有几种不同实现格式,常见的是 FP8 和 AutoFP8。vLLM 启动时加载 FP8 权重,--quantization参数有时要写fp8,有时要写auto_fp8,具体看模型仓库是用什么工具量化的。最稳妥的办法是下载后打开config.json,看有没有quantization_config字段:
"quantization_config": { "quant_method": "fp8", "activation_scheme": "static" }quant_method是fp8就对应--quantization fp8,如果是auto_fp8就写--quantization auto_fp8。有些新版本 vLLM 也能自动识别,但显式指定最保险,避免启动时报格式不匹配。
显存预算方面,我用 24GB 显存的 RTX 4090 实测,Qwen3-8B-FP8 各部分占用大致如下:
| 项目 | 占用估算 | 说明 |
|---|---|---|
| 模型权重 | 约 8GB | FP8 量化后 |
| CUDA context | 0.5~1GB | 加载即占用 |
| KV cache | 2~8GB | 随 max-model-len 增大而增大 |
| 推理激活 | 0.5~2GB | 动态变化 |
如果你的显卡只有 12GB,优先压缩--max-model-len。比如把 32768 降到 8192,能一下省出五六 GB 显存。对普通对话场景,8192 上下文长度已经完全够用。
4. 启动服务与参数调优:把 Qwen3-8B-FP8 跑出最佳性能
4.1 最小可用配置:一行命令跑通服务
环境就绪后,启动 vLLM 服务:
vllm serve ~/models/Qwen3-8B-FP8 \ --quantization fp8 \ --dtype auto \ --max-model-len 8192 \ --gpu-memory-utilization 0.90 \ --served-model-name qwen3-8b \ --host 0.0.0.0 \ --port 8000看到Application startup complete.或Uvicorn running on http://0.0.0.0:8000就是启动成功了。第一次启动时 vLLM 会做 CUDA graph 捕获和 W4A16 / FP8 算子预热,耗时一到三分钟,期间 GPU 占用率飙到 100% 是正常现象,别以为卡死就关了。
有个容易被误判的日志:启动过程中会打印一行[pynccl.py:113] vllm is using nccl==2.30.7。这不是报错,只是提示当前 NCCL 版本。很多新手看到带error字样的日志就慌,其实要看日志级别,ERROR才是真问题,INFO和WARNING大多可以忽略。
4.2 核心参数逐项拆解与调优建议
上面那串命令里每个参数都不是随便写的,逐个说下作用:
| 参数 | 作用 | 建议 |
|---|---|---|
--quantization fp8 | 指定 FP8 加载格式 | 按 config.json 的 quant_method 调整 |
--dtype auto | 权重精度自动匹配 | 固定用 auto 即可 |
--max-model-len 8192 | 最大上下文长度 | 显存不够就先降这个 |
--gpu-memory-utilization 0.90 | 允许使用的显存比例 | 同时跑别的任务就降到 0.7 |
--served-model-name qwen3-8b | API 里显示的模型名 | 客户端调用时 model 字段要用这个名字 |
--host 0.0.0.0 | 监听所有网卡 | 允许局域网访问,注意安全 |
--port 8000 | 服务端口 | 冲突时换一个 |
还有几个进阶参数我建议加上:
--enable-prefix-caching \ --kv-cache-dtype fp8--enable-prefix-caching开启前缀缓存。如果你做 RAG 或者多轮对话,每次请求都会带上同样一大段系统提示词,开启后 vLLM 会复用这些 token 的 KV cache,首 token 延迟能降 30% 以上。
--kv-cache-dtype fp8是把 KV cache 也用 FP8 存储,显存占用还能再省一截。这个参数对显存紧张的用户非常友好,缺点是极端长上下文下精度可能轻微下降,对话场景基本无感。
多卡用户会用到--tensor-parallel-size,比如两张卡就设 2。但这是进阶玩法,单卡不要动它,设成 1 就行。
4.3 API 联调与吞吐压测实录
服务起来后,先用 curl 验证最基础的通路:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b", "messages": [{"role": "user", "content": "你好,请简单介绍一下你自己"}], "max_tokens": 256, "temperature": 0.7 }'能返回 JSON 格式的回复就说明推理链路完全跑通了。日常开发我更推荐用 Python 的 openai 包,因为 vLLM 服务完全兼容 OpenAI API 格式,所有基于 OpenAI SDK 的代码都可以零改动切换过来:
from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", # vLLM 不校验 key,随便填 ) resp = client.chat.completions.create( model="qwen3-8b", messages=[{"role": "user", "content": "用一句话解释 FP8 量化"}], max_tokens=256, ) print(resp.choices[0].message.content)跑通了聊天接口,再测一下真实性能。vLLM 自带了压测工具,新版叫vllm bench serve:
vllm bench serve --model qwen3-8b --tokenizer ~/models/Qwen3-8B-FP8也可以自己写个简单脚本统计首 token 延迟和生成速度。我在 RTX 4090 上的实测参考值:单请求、256 tokens 输出,首 token 大约 60ms,生成速度稳定在 90~110 tokens/s;并发 8 路请求时,总吞吐能到 400 tokens/s 左右。这个性能跑个人项目、小团队内部工具绰绰有余。
5. 常见问题速查与避坑经验
5.1 最容易踩的 5 个高频坑
我把这次部署遇到的坑和排查思路整理成一张速查表:
| 现象 | 原因 | 解决方法 |
|---|---|---|
WSL2 里nvidia-smi找不到 GPU | Windows 驱动过旧 | 去 NVIDIA 官网装最新驱动后重启 |
| 启动时报 CUDA out of memory | 显存不够 | 降低--max-model-len和--gpu-memory-utilization |
| 加载模型报量化格式不匹配 | FP8 格式和参数不对应 | 看 config.json 的 quant_method,改用auto_fp8 |
| API 请求超时无响应 | 端口被防火墙拦截或模型加载未完成 | 检查日志是否已经Application startup complete |
| 模型加载极慢,CPU 占用 100% | 模型放在/mnt/c/跨盘读取 | 移到~/models/下 |
第一个坑最隐蔽。你装了最新驱动,重启完 Windows 后直接进 WSL2 执行nvidia-smi,可能仍然是找不到设备。这时候先确认一下 Windows 侧驱动确实加载了,然后在 PowerShell 里执行wsl --shutdown彻底关闭 WSL 再重新进去,很多时候 GPU 透传是在 WSL 启动时才建立的,不重启 WSL 加载不了新驱动。
第二个坑也很常见。有些模型仓库的 config.json 里默认的max_position_embeddings是 32768,如果你不手动传--max-model-len,vLLM 会按这个上限预留 KV cache,显存小的卡直接 OOM。遇到 OOM 优先降这个参数,不要一味调低--gpu-memory-utilization,后者是全局限流,连权重加载都可能失败。
5.2 推理变慢时的排查路径
服务能跑但慢,是另一类高频问题。我一般按下面顺序排查:
先确认模型是不是真的以 FP8 加载了。启动日志里找Loading model weights took后面会带实际加载的数据类型,如果显示torch.float8_e4m3fn就是 FP8 生效;如果显示torch.bfloat16,说明--quantization参数写错了或者模型本身就不是 FP8,权重会多占一倍显存,速度自然上不去。
再检查 CUDA graph 是否捕获成功。vLLM 启动完成后,日志里会有类似Capturing CUDA graph的记录。如果这一步失败,vLLM 会退回到 eager mode,推理速度可能掉 50% 以上。CUDA graph 捕获失败多半是因为显存不够,给模型留点余量,别把--gpu-memory-utilization开到 0.98。
最后看看是不是并发设置的问题。默认--max-num-seqs 256意味着最多可以有 256 个序列共享 GPU,序列太多时每请求分到的算力会被稀释,单请求延迟变高。如果是交互式应用,可以调低到 32 或 64,换来单请求更低的延迟。
5.3 WSL2 内存、交换分区与持久化配置
WSL2 默认会占用 Windows 端最多 50% 的内存,这在跑大模型时可能不够用。你可以通过C:\Users\你的用户名\.wslconfig文件手动限制:
[wsl2] memory=16GB processors=8 swap=8GB改完记得在 PowerShell 执行wsl --shutdown重启 WSL 生效。memory是 WSL 可以使用的最大内存,processors是 CPU 核心数,swap是交换分区大小。如果你机器内存只有 16GB,建议给 WSL 分 12GB 左右,留 4GB 给 Windows 本体,否则整机容易卡死。
还有一个容易忽略的点:WSL2 的虚拟机不会自动回收已分配的内存。跑完服务后 WSL 进程可能仍然占着大量内存,这时候在 PowerShell 里执行wsl --shutdown再重新进入即可释放。
至于持久化,WSL2 里conda创建的环境和~/models/目录都会持久化保存,重启 Windows 也不会丢失。但如果你的 Windows 系统做重大版本更新,偶尔会出现 WSL 实例失联的情况,建议定期备份~/models/下的模型文件和配置文件,省得重新下载。
我个人的习惯是:模型权重这类大文件放在 WSL2 的 Linux 文件系统里跑服务,长期不用的模型再压缩备份到 Windows 磁盘。这样兼顾了性能和容量,也是这次实战下来觉得最合理的使用方式。最后再说一句,如果你本地显存实在不够,用 Docker 镜像跑同样配置能省去很多环境问题,但原理和参数调优思路和 WSL2 完全一致,先把这篇的细节吃透,换到任何环境都能快速上手。