干过这行的都知道,vLLM 这东西官方文档默认只讲 Linux,Windows 上想跑起来,第一反应基本是“别想了”。但实际项目里,有时候你手上就一台 Windows 工作站,显卡是 RTX 4090,临时要起个 OpenAI 兼容的服务来测 Qwen3-8B-FP8,总不能为这个专门装个 Linux 双系统吧。我折腾过几次之后总结出一套还比较顺的流程:不用 Docker Desktop,不碰原生 Windows Python,直接通过 WSL2 来跑 vLLM,从零到把 Qwen3-8B-FP8 的接口调通,整个链路是完整且可复现的。这篇文章就把这套流程掰开揉碎讲清楚,包括每一步背后的原因、容易踩的坑、以及我实际跑模型时遇到的各种怪异问题。适合手里有 N 卡、想在 Windows 下快速验证模型效果的人,也适合刚接触 vLLM 想少走弯路的同学。
1. 为什么 Windows 跑 vLLM 要先绕道 WSL2
1.1 vLLM 的底层依赖决定了它不适合直接在 Windows 上跑
先从根源说。vLLM 不是一个普通的 Python 库,它为了提高推理吞吐,做了很多底层优化,比如 PagedAttention 管理 KV Cache、连续批处理、以及多卡场景下的 NCCL 通信。这些能力大量依赖 Linux 内核的特性,比如大页内存、设备文件映射、CUDA 驱动的用户态接口、NCCL 的 socket 和 shared memory 机制。Windows 虽然也能跑 CUDA 程序,但在这些细颗粒度的系统接口上,和 Linux 内核不是一回事。vLLM 官方在 Windows 上既没有提供正式的 wheel 包,也没有保证可用的运行时,强行在 Windows 的 Python 环境里pip install vllm,经常会在编译或者运行时挂掉,尤其是那些依赖 NCCL 的环节。
打个比方,vLLM 就像一台为 Linux 精心调校过的跑车,Windows 是条水泥路,不是说完全不能开,但轮子、悬挂、变速箱全都是按赛道设计的,硬上容易爆缸。所以想省心,就要先给它铺一条 Linux 兼容层,也就是 WSL2。
1.2 三条可行路径对比:原生、Docker、WSL2
我试过三种方式,先说结论:日常调试最推荐 WSL2,没有之一。
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 原生 Windows 安装 vLLM | 操作直观,不用装子系统 | 依赖 Python、CUDA 环境容易冲突;vLLM 官方不提供 Windows 支持,编译过程非常痛苦 | 不推荐 |
| Docker Desktop(Windows 容器) | 隔离干净,能复用现有镜像,团队协作方便 | 与 WSL2 相比多了一层虚拟化,磁盘占用大;GPU 直通需要额外配置,文件挂载路径容易混乱 | 已有 Docker 基础设施、需要统一部署环境 |
| WSL2 + Ubuntu | 轻量,启动快,与 Windows 共享 GPU 驱动,vLLM 兼容性好 | 首次安装有几步配置,网络是 NAT 模式,少数端口需要手动处理 | 本地开发、推理测试、脚本调试,我最常用 |
另外,Docker Desktop 在 Windows 上本身也有 WSL2 后端,等于套了两层,性能上会有额外开销。而直接使用 WSL2,相当于在 Windows 里跑了一个精简的 Linux 虚拟机,vLLM 的 CUDA 调用能直接穿透到宿主的显卡驱动上,损耗很小。
1.3 为什么推荐 WSL2 而不是 VM 虚拟机
如果你用过 VirtualBox 或 VMware 装 Ubuntu 跑 GPU 任务,会发现一个痛点:显卡直通要么需要额外配置,要么性能损失明显。WSL2 用的是 Windows 自己这套 Hyper-V 虚拟化底层,对 GPU 的支持经过了专门优化,NVIDIA 驱动在 Windows 里装好后,WSL2 内部直接就能用nvidia-smi,不需要再装一遍 Linux 驱动。这一点是省时省力的关键。
而且 WSL2 不只解决了 GPU 的问题,文件系统集成也很自然。你可以在 Windows 的D:\models目录下放模型文件,然后在 WSL2 里通过/mnt/d/models直接访问,虽然跨文件系统读写性能一般,但模型权重这种只读文件完全能接受。比开机切系统或者来回拷贝方便太多。这也是我最终选择 WSL2 的原因。
2. 基础环境准备:Windows + WSL2 + 显卡驱动
2.1 Windows 侧开启 WSL 功能
开始之前,先确认 Windows 版本。最好用 Windows 10 21H2 以上或者 Windows 11,老版本虽然也能装,但坑更多。打开 PowerShell(管理员权限),执行:
wsl --install这个命令会安装 WSL2 所需的全部组件,然后重启一次系统。如果你之前已经装过旧版 WSL,建议先升级到最新版本:
wsl --update重启之后,在 PowerShell 里查看版本:
wsl --status wsl --list --verbose正常情况下会显示默认版本是 2。如果还是 1,手动改成 2:
wsl --set-default-version 2这一步很关键,因为 vLLM 对 WSL1 的兼容性极差,很多 Linux 系统调用在 WSL1 上是模拟出来的,跑起来容易出莫名其妙的问题。
2.2 安装 Ubuntu 发行版
继续在 PowerShell 里执行:
wsl --install -d Ubuntu-22.04装完会自动弹出 Ubuntu 窗口,第一次启动会让你设置用户名和密码。这个密码不需要和 Windows 登录密码一致,记好就行。装完以后,后续用 Windows Terminal 进入非常方便。
我习惯用 Ubuntu 22.04,原因是它的 glibc 和系统库版本比较新,兼容 vLLM 预编译 wheel 的要求。Ubuntu 20.04 也见过能跑的,但遇到编译安装时容易因为 GCC 版本过老而失败,没必要给自己添堵。
进入 Ubuntu 以后,先做基础更新:
sudo apt update sudo apt upgrade -y2.3 显卡驱动和 WSL2 的 GPU 透传
这一步很多人会卡住,其实比你想象的简单。WSL2 不需要在 Linux 内部安装 NVIDIA 驱动,只需要确保 Windows 侧已经装好了 NVIDIA 显卡驱动,而且版本不能太老。
在 Ubuntu 终端里直接执行:
nvidia-smi如果能看到显卡信息,说明 GPU 透传正常。如果提示找不到命令,先检查 Windows 下nvidia-smi是否正常,再确认 WSL 已经更新到最新版。NVIDIA 官方驱动安装包一般自带 WSL 支持,我遇到的一次问题是驱动版本太旧,升级到最新驱动后就解决了。
验证 CUDA 版本时要注意,这里的 CUDA 版本显示的是驱动支持的版本,不一定要和你后面安装的 PyTorch 完全一致。vLLM 的预编译 wheel 通常自带 CUDA runtime,所以不需要在 WSL 里再装一套完整 CUDA Toolkit。这个知识点很多人搞混,以为必须先装 CUDA,其实没必要。
2.4 安装 Miniconda 并创建干净环境
为了不污染系统 Python,建议用 Miniconda 管理环境。在 Ubuntu 终端里执行:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh安装过程一路默认,最后记得勾选初始化 conda。如果当时忘了,安装完执行:
source ~/.bashrc然后创建独立的 Python 环境:
conda create -n vllm python=3.10 -y conda activate vllmPython 版本建议 3.10 或 3.11。vLLM 对 3.12 的支持这几年已经跟上来了,但 3.10 是最稳的选择,尤其是跑 Qwen3 这种模型时,很少因为 Python 版本踩坑。
如果你想让 WSL2 分到更多内存,可以在 Windows 用户目录下创建一个.wslconfig文件,内容类似:
[wsl2] memory=32GB processors=16 swap=8GB改完执行wsl --shutdown再重新进入,否则不会生效。这个文件对资源管理非常有用,特别是你要同时开 Windows 侧的浏览器、编辑器,再跑一个大模型推理,不限制内存的话 WSL2 可能把整个电脑吃满。
3. 安装 vLLM 的版本选择和依赖关系
3.1 vLLM 安装前必须理解的两件事
第一,vLLM 的安装包是依赖 PyTorch 的,而且它要求 PyTorch 的 CUDA 版本要匹配。不过现在 pip 安装vllm的时候会自动拉取合适的 PyTorch,一般不需要手动装。如果你之前手动装过 CPU 版本的 PyTorch,一定要先卸掉,不然 vLLM 会在运行时直接报 CUDA error。
第二,vLLM 的预编译 wheel 包只支持特定架构和特定 CUDA 版本。WSL2 的环境是 x86_64 Linux,市面上主流的 vLLM 版本都能找到匹配的 wheel,不需要从源码编译。网上有些教程让你先克隆仓库再构建,那是开发环境的玩法,正常使用pip install就够了,构建一次能吃掉你两三个小时。
3.2 执行安装并验证
在 conda 环境激活后:
pip install vllm如果你希望指定 CUDA 版本,或者想要最新的 nightly 版本,可以查一下官方索引。一般情况下直接装最新稳定版就好,Qwen3-8B-FP8 这种比较新的模型,需要相对较新的 vLLM 版本才能正确识别 FP8 量化。我装的时候 vLLM 已经发布到 0.6.x 之后了,很顺利。
安装完成后,先跑一个最低限度的验证:
python -c "import vllm; print(vllm.__version__)"再跑一行检查 GPU 是否被 vLLM 正确识别:
python -c "from vllm.utils import get_device; print(get_device())"不过最直接的验证还是启动一个小模型试跑。下面会讲 Qwen3-8B-FP8 的完整部署,你可以直接用它当“试金石”。
3.3 关于 FP8 模型和显卡架构的兼容问题
Qwen3-8B-FP8 是 8B 参数的 FP8 量化版本,权重文件大小比 BF16 版本小很多,推理时显存占用也更低。但有一个容易忽视的点:FP8 计算需要显卡支持相应的指令集,目前消费级显卡里,比较合适的是 Ada Lovelace 架构(RTX 40 系列)以及更新的 Blackwell 架构(RTX 50 系列)。如果你是 RTX 30 系列,虽然显存可能够,但 FP8 的核心算子不一定能用硬件加速,vLLM 可能会报不支持或者性能很差。
我实际测试时用了 RTX 4090,24GB 显存,跑 Qwen3-8B-FP8 非常舒服。如果你的卡是 RTX 3080 或者更低,建议不要硬上这个模型版本,直接换 Qwen3-8B 的 BF16 版本,或者降低并发和上下文长度再试试。
4. 下载 Qwen3-8B-FP8 并启动 vLLM 服务
4.1 下载模型文件
Qwen3-8B-FP8 的模型文件我直接用 ModelScope 下载。原因很简单,不用额外配置就能拉下来,断点续传也比较稳。先安装相关依赖:
pip install modelscope然后在 Python 里执行:
from modelscope import snapshot_download model_dir = snapshot_download('Qwen/Qwen3-8B-FP8', local_dir='/data/models/Qwen3-8B-FP8') print(model_dir)如果你希望放在 Windows 盘上共享,可以指定 local_dir 为/mnt/d/models/Qwen3-8B-FP8。不过我不建议这么做,因为跨文件系统读文件速度会有损失,最好还是放在 WSL2 自己的文件系统里,例如~/models/Qwen3-8B-FP8。
下载完成后,检查一下目录内容,里面应该包含config.json、多个*.safetensors文件、tokenizer.json等。注意看权重文件的后缀,FP8 版本一般是*fp8*.safetensors或者统一在model.safetensors.index.json里记录分片。
如果你更喜欢用 Hugging Face 的仓库,也可以直接:
pip install huggingface_hub huggingface-cli download Qwen/Qwen3-8B-FP8 --local-dir ~/models/Qwen3-8B-FP8只要能稳定下载,渠道无所谓的。
4.2 vLLM 启动推理服务
模型放在~/models/Qwen3-8B-FP8之后,启动命令非常简单:
vllm serve ~/models/Qwen3-8B-FP8 \ --served-model-name qwen3-8b \ --gpu-memory-utilization 0.85 \ --max-model-len 32768 \ --port 8000解释一下这几个参数:
--served-model-name:对外暴露的模型名称,这个名称要和后续调用接口时填的model字段一致。我这里写的是qwen3-8b,你也可以改成Qwen3-8B-FP8。--gpu-memory-utilization:允许 vLLM 使用的 GPU 显存比例。0.85 表示最多用 85%,剩下 15% 留给显卡驱动和其他应用,避免显存爆掉。如果你只有 16GB 显存,可以试着改成 0.75。--max-model-len:最大上下文长度。我设为 32768,也就是 32K tokens。Qwen3 本身支持更长的上下文,但越长占用的 KV Cache 显存越多。如果你的显存不够,要先把这里调低。--port:服务端口。默认是 8000,也可以改成其他端口。
启动后,日志里会显示模型加载进度、显存占用等信息。重点关注下面几行:
INFO: Shard 0: gpu_memory_usage = 18.2 GiB INFO: Graph capturing finished in 3 sec. INFO: Maximum concurrency for 32768 tokens per request: 8看到Graph capturing finished意味着模型已经编译好计算图,服务准备就绪。随后日志尾端会出现类似这样的信息:
Uvicorn running on http://0.0.0.0:8000这就说明服务已经起来了。
4.3 为什么启动时没有强制指定量化参数
很多第一次玩 FP8 模型的人会问,启动时要不要加--quantization fp8。大多数情况下不需要,vLLM 会从模型的config.json里自动读取量化配置。如果你强制指定,反而可能因为版本兼容问题报错。只有一种情况需要显式加参数:你下载的模型没有标准的quantization_config字段,或者 vLLM 版本较旧识别不了,此时再手动加:
vllm serve ~/models/Qwen3-8B-FP8 --quantization fp8如果这样还报错,先升级 vLLM,别浪费时间排查。
5. 调用 OpenAI 兼容接口
5.1 用 curl 快速验证
vLLM 启动后默认提供 OpenAI 风格的/v1/chat/completions接口。在 WSL2 里可以直接用 curl 测试:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b", "messages": [ {"role": "user", "content": "你好,请用一句话介绍你自己"} ], "max_tokens": 512 }'如果一切正常,返回的 JSON 里会包含choices数组,里面有模型生成的文本。注意这里的model字段必须和启动时的--served-model-name保持一致,否则会报model not found。
5.2 用 Python 封装调用
实际项目中不会用 curl,而是通过 requests 或 OpenAI SDK 来调用。看一个最小示例:
import requests url = "http://localhost:8000/v1/chat/completions" payload = { "model": "qwen3-8b", "messages": [ {"role": "system", "content": "你是一个有用的助手。"}, {"role": "user", "content": "解释一下什么是 KV Cache?"} ], "temperature": 0.7, "max_tokens": 1024 } resp = requests.post(url, json=payload) data = resp.json() print(data["choices"][0]["message"]["content"])如果使用 OpenAI SDK,只需要设置base_url指向 vLLM 服务:
from openai import OpenAI client = OpenAI( api_key="EMPTY", base_url="http://localhost:8000/v1" ) resp = client.chat.completions.create( model="qwen3-8b", messages=[{"role": "user", "content": "你好"}], max_tokens=512 ) print(resp.choices[0].message.content)5.3 Windows 侧访问 WSL2 里的服务
比较舒服的一点是,WSL2 里的服务默认通过localhost就能在 Windows 侧访问。也就是说,你可以在 Windows 浏览器里直接打开http://localhost:8000/docs查看接口文档,或者在 Python 脚本里直接调用,不需要额外端口转发。
不过偶尔会出现localhost不通的情况,尤其在 WSL 版本较老或者配置被改动过的时候。排查方法很简单,在 WSL2 里执行:
ip addr找到 eth0 的 IPv4 地址,然后在 Windows 浏览器里访问http://<WSL_IP>:8000。如果这样才能访问,说明 localhost 转发没有自动生效,可能需要重启 WSL 或重置端口转发规则。
6. 常见问题与性能调优实录
6.1 WSL2 内存不足导致 OOM
这是所有 vLLM 跑大模型最容易遇到的问题。WSL2 默认内存占用上限通常是物理内存的 50% 或者 8GB,而 Qwen3-8B-FP8 加载后光权重就要 9GB 左右,再加上 KV Cache 和计算图,整体内存很容易超过 16GB。如果服务启动后立刻被 kill,或者日志里出现Killed字样,十有八九是 OOM。
解决办法是前面提到的.wslconfig文件,把 memory 调到你模型实际需要的数值。我一般设成 32GB,这样 WSL2 里有足够的页缓存给模型权重和 CUDA context 使用。
6.2 GPU 显存不足或 KV Cache 分配失败
显存不足的表现有两种:一种是启动时报 OutOfMemory,另一种是启动成功但一旦请求变长就崩溃。先说第一种,启动时分配 GPU 显存失败,主要原因是--gpu-memory-utilization设置得太高,或者同时有其他程序占用了显存。你可以在 Windows 的任务管理器里看 GPU 显存占用,也可以在 WSL2 里执行nvidia-smi查看。把参数从 0.9 降到 0.8,一般就好了。
第二种情况,请求变长后崩溃,多半是--max-model-len设得太高,导致 KV Cache 预分配不够。解决办法是降低--max-model-len。比如从 32768 降到 16384,再配合--gpu-memory-utilization 0.85,基本能缓解。KV Cache 本质上是用显存换并发,上下文越长,同一个批次能处理的请求越少。
6.3 模型下载慢或中断
如果你发现 ModelScope 下载速度很慢,或者下载到一半断了,先检查磁盘空间。Qwen3-8B-FP8 的权重文件加起来接近 10GB,WSL2 默认磁盘大小会随使用自动增长,但如果宿主 C 盘空间不够,下载会失败。建议用snapshot_download的local_dir参数指定放在空间足够的分区。
另外 ModelScope 是支持断点续传的,重新执行一遍下载命令,它会自动检查已下载的文件。不要因为失败就删掉重来,先去目标目录看看,有些文件已经完整落盘了。
6.4 启动时出现 NCCL 相关报错
vLLM 在 WSL2 里单卡运行,一般不会触发复杂的 NCCL 问题。但如果看到类似:
[1/0] NCCL error: unhandled system error大概率是 WSL2 的共享内存或网络配置出了问题。一个有效的处理方式是检查本机是否同时运行了 Docker Desktop,两个虚拟化组件抢占资源时,NCCL 的初始化容易失败。先退出 Docker Desktop,重启 WSL:
wsl --shutdown再重新进入启动 vLLM。
6.5 我实测下来的性能数据
最后分享一个参考数据。我在 RTX 4090、24GB 显存、AMD Ryzen 9 5900X、WSL2 环境下,用默认参数启动 Qwen3-8B-FP8,单请求输出 2000 tokens 时,吞吐大约在每秒 120-150 tokens 左右。并发 8 个请求时,整体吞吐能到 500 tokens/s 以上,但单请求延迟会上升。如果你用的是 16GB 显存的卡,建议把--max-model-len限制在 16384 以内,否则长文本生成时很容易顶到显存上限。
对于只是做接口测试或原型验证,这个性能已经非常够用了。真正上生产、追求高并发吞吐的话,还是建议放到 Linux 服务器上,配置多卡并行和持续批处理优化,那样 vLLM 的潜力能发挥得更完整。
回到最开始的问题,Windows 上跑 vLLM 真的可行吗?我用 WSL2 跑了非常多轮之后,可以负责任地说,完全可行。刚开始遇到的那些乱七八糟的坑,基本上都是环境没理顺,或者显存分配策略不合适。把 WSL2 的基础配置、驱动版本、显存参数这三件事处理好,Qwen3-8B-FP8 从零到跑通,一个下午的时间足够了。