先说结论:Windows 本身没有官方直接支持的 vLLM 安装包,但只要你愿意用 WSL2 这套方案,在一台普通的 Windows 电脑上把 vLLM 跑起来是完全可行的,而且推理性能不打折。这篇文章我就拿 Qwen3-8B-FP8 这个真实模型当靶子,把从环境配置、模型下载、服务启动到接口调用整条链路完整走一遍,中间会穿插我实际踩过的坑和调参记录。适合谁看?想在自己 Windows 主力机上体验 vLLM、本地跑开源大模型的开发者;已经在 Linux 上跑过 vLLM、但需要在 Windows 上快速复现验证的同学;还有那些为了部署模型不想折腾双系统、却发现网上教程全是清一色 Ubuntu 命令的人。
我会尽量把每条命令、每个参数都讲清楚“为什么这么设”,而不是让你无脑复制。毕竟部署这玩意儿,卡住你的一般不是官网文档没看,而是那些文档根本不写的小细节。
1. 为什么 Windows 上跑 vLLM 会这么折腾
1.1 vLLM 到底依赖了什么
很多人不理解,vLLM 明明是个 Python 包,为什么不能像普通库一样pip install完就直接用?核心原因在于 vLLM 不是一个单纯的模型推理脚本,而是一个深度绑定 Linux 底层的推理引擎。它的依赖起码有三个层次:
第一层是 PyTorch 和 CUDA,这个 Windows 上其实也有,问题不大;第二层是高性能通信库 NCCL,vLLM 在单卡和多卡场景下会通过它做张量并行、通信聚合,NCCL 官方基本只提供 Linux 版本,Windows 生态里很难直接跑通;第三层是共享内存、页缓存、文件锁这类操作系统能力,vLLM 的 PagedAttention 和调度器会直接调用这些底层机制,而 Windows 的进程模型和内存模型跟 Linux 差异很大,vLLM 官方根本没有维护 Windows 的原生适配分支。
所以网上那些“在 Windows 下硬装 vLLM”的做法,比如修改源码、自己编译 pynccl、替换动态链接库,不是不能用,而是每次 vLLM 升个小版本就崩一次,维护成本极高。与其折腾这些,不如老老实实走 WSL2——它本质上是一个由 Windows 托管的轻量级 Linux 虚拟机,具备完整的 Linux 内核,vLLM 在上面运行时感知不到任何 Windows 的存在。
1.2 三条可行路线,我帮你排过雷
我实际试过三条路线:原生 Windows 硬跑、WSL2 + Python 环境、Docker Desktop(底层也是 WSL2)。先说原生 Windows,这条路在 vLLM 的老版本里有过一些实验性的支持,但官方从未保证过稳定性。我在 Windows 11 上尝试过用pip install vllm装最新版,结果编译阶段就报错,NCCL 相关模块直接无法加载,后续不管是换 Python 版本还是装 CUDA Toolkit 都没有解决,浪费了不少时间。
第二条路是 WSL2 + 原生 Python 环境,这也是我推荐的主力方案。WSL2 提供了完整的 Linux 内核,NVIDIA 驱动可以通过 Windows 侧直接透传进去,你在 WSL 里执行nvidia-smi能看到和 Windows 宿主一模一样的显卡信息。vLLM 在这里运行时的性能损耗很小,因为 GPU 计算、显存访问都是直通的,CPU 和内存的额外开销也基本可以忽略。
第三条路是 Docker Desktop,它其实利用的也是 WSL2 后端。好处是环境隔离、便于复现,团队协作时尤其方便,缺点是镜像体积较大,而且 Docker Desktop 在 Windows 上偶尔会出一些共享文件系统的问题。具体怎么选,我整理了一个对比表:
| 方案 | GPU 直通 | 安装难度 | 性能 | 维护成本 | 适用场景 |
|---|---|---|---|---|---|
| Windows 原生硬跑 | 部分支持 | 极高 | 不稳定 | 高 | 不推荐 |
| WSL2 + Python | 完美 | 低 | 接近原生 | 低 | 个人开发、日常测试、长期服务 |
| Docker Desktop | 完美 | 中 | 略低于原生 | 中 | 团队协作、快速复现、多环境隔离 |
1.3 我最终推荐的组合
如果你是在自己的电脑上做本地推理服务,我建议直接上 WSL2 + Python 虚拟环境这条路。理由很简单:安装过程最少,所有依赖都由pip管理,出了问题排查路径也最短;后续想更新 vLLM 版本,只需要重新pip install --upgrade vllm就行,不会像 Docker 那样还要拉一个几个 GB 的新镜像。
这篇文章下面的所有操作,都是基于“Windows + WSL2(Ubuntu 22.04/24.04)+ Python 3.10/3.11 + 单张 NVIDIA 显卡”这个组合来写的。显卡驱动我只要求是较新的 NVIDIA 官方驱动,显存建议 16 GB 起步,24 GB 会比较宽裕;如果你的显卡只有 8 GB,也不是完全不能跑,但后面启动参数里的--max-model-len和--max-num-seqs就要卡得非常紧,体验会打折扣。
2. 环境准备:先把 WSL2 这块地基打扎实
2.1 一分钟开启 WSL2 和 Ubuntu
WSL2 的安装现在非常简单。在 Windows PowerShell(管理员模式)里执行一条命令:
wsl --install它会自动完成三件事:启用 WSL 功能、安装 WSL2 内核、默认给你装一个 Ubuntu 发行版。装完后重启系统,然后启动 Ubuntu 终端,设置一个用户名和密码就行。这里有个我常提醒别人的细节:这个用户名的密码要记牢,因为后续很多 sudo 操作都要用到,忘了只能去改配置文件,虽然不麻烦,但没必要。
装完先确认一下版本:
wsl -l -v如果输出的 Ubuntu 版本后面显示的是 2,说明已经是 WSL2。如果是 1,就手动升级一下:
wsl --set-version Ubuntu 2顺便把默认版本也设置成 2,免得以后装其它发行版时默认跑在 WSL1 上:
wsl --set-default-version 22.2 显示驱动与 CUDA 直通
这是整套方案里最容易出问题的环节,但理解之后又觉得特别简单。WSL2 里的 Ubuntu 不需要安装 NVIDIA 的 Linux 显卡驱动,它直接复用 Windows 宿主侧的驱动。你在 Windows 上装好并更新到较新的 NVIDIA 驱动(GeForce 或 Studio 驱动都可以),然后在 WSL 里执行:
nvidia-smi如果能看到类似下面的输出,就说明 GPU 直通已经生效:
+---------------------------------------------------------------------------------------+ | NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 | +---------------------------------------------------------------------------------------+这里有个关键判断标准:WSL 里显示的 Driver Version 和 Windows 上的一致,CUDA Version 是一个“最大支持版本”,并不代表 WSL 里已经装好了 CUDA Toolkit。vLLM 安装时不会依赖系统级的 CUDA,它有自己配套的 PyTorch,安装时会把对应的 CUDA 运行库一起带进来。
如果nvidia-smi提示找不到命令,大概率是 Windows 驱动版本太老,或者 WSL 内核太旧。解决方法是先执行wsl --update把内核更新到最新,再去 Windows 侧更新显卡驱动。这一步跑不通的话,后面所有环节都白搭,所以建议先在这停顿检查好。
2.3 内存、显存与磁盘:跑 8B 模型需要多大配置
这部分我用实际的模型来算一笔账。Qwen3-8B 有约 80 亿参数,BF16 格式下每个参数占 2 字节,光权重就需要约 16 GB 显存;而 FP8 格式下每个参数只占 1 字节,权重大约 9 GB 左右。注意这只是权重本身的占用,vLLM 启动时还要给 KV Cache、CUDA graph 和运行时开销预留空间。所以如果你想跑起来比较舒服,显存建议 16 GB 起步;24 GB 的显卡可以把上下文长度和并发数都调得比较高,体验会好很多。8 GB 显存的显卡也不是完全不能跑,把上下文长度砍到 8192、并发数压到 8,勉强能出结果,但不要对性能抱太大期望。
内存这边,WSL2 默认会动态占用 Windows 物理内存,但为了避免内存不足导致 OOM,最好手动限制一下。在 Windows 用户目录下创建或编辑.wslconfig文件,写入:
[wsl2] memory=32GB processors=16 swap=8GB localhostForwarding=true保存后执行wsl --shutdown重启 WSL2,配置就会生效。这里的 memory 建议根据你机器实际物理内存来填,不要超过真实大小的一半太多,否则 Windows 宿主机也会卡。磁盘方面,WSL2 的虚拟磁盘默认放在C:\Users\<用户名>\AppData\Local\Packages\...路径下,容量会自动扩展,但建议预留至少 50 GB 的可用空间,因为模型文件本身就接近 20 GB,加上虚拟环境、PyTorch 和各种依赖,整体空间消耗很容易就超过 30 GB。
2.4 两个系统之间的 IO 陷阱
这是很多人跑通服务后仍然觉得慢的根源。WSL2 访问 Windows 盘的路径(比如/mnt/c/、/mnt/d/)实际上是通过 9P 协议跨越了虚拟化边界,跨文件系统读写性能衰减非常明显,有时候比直接读 WSL 原生虚拟盘慢一个数量级。
我第一次部署时偷懒,把模型放在了 D 盘,然后在 WSL 里通过/mnt/d/models/Qwen3-8B-FP8去加载。结果是启动时模型权重文件的读取阶段肉眼可见地慢,一个 9 GB 的模型加载了将近二十分钟。后来我把模型拷贝到 WSL 的~/models/目录下,同样的加载过程两分钟就完成了。
所以这里有一个非常实诚的建议:模型文件一定要放在 WSL 的虚拟盘里,也就是/home/你的用户名/下面,不要放在/mnt/c、/mnt/d这些路径上。为了省事直接把模型放 Windows 盘,之后的每次启动、每次读取模型文件都会让你后悔。
3. 模型与推理引擎安装
3.1 为什么选 Qwen3-8B-FP8
Qwen3 系列是开源大模型里综合能力比较均衡的一档,尤其中文理解、代码生成、数学推理这几个方向表现都可圈可点。我选择 8B 这个尺寸,是因为它在“本地能跑起来”和“能力够用”之间取得了很好的平衡——比它小的模型做复杂任务明显吃力,比它大的(比如 14B)对显存和内存的要求就上了一个台阶。
FP8 后缀非常关键。FP8 是一种 8 位浮点数格式,和 BF16 相比,模型权重占用直接减半,这也是同一个模型能在 16 GB 显卡上相对从容运行的根本原因。可以这样理解:同样一个抽屉,原来只能放一份 Full HD 照片,现在用压缩格式放两份,质量会略有下降,但整体可用性大幅提升。Qwen3-8B-FP8 在 Hugging Face 和 ModelScope 上都有官方仓库,权重就是 FP8 格式的,vLLM 加载时会自动识别 safetensors 里的 dtype,一般不需要额外指定量化参数。
FP8 的实际效果,我在代码生成和中文问答场景下做过对比测试,和 BF16 版本相比大部分任务感知不出明显差异,但在一些对数值精度要求极高的场景(比如某些数学证明、长文本精确复述)会偶尔出现轻微偏差。如果你追求极致精度,可以改成跑 BF16 版本,前提是显卡显存足够。
3.2 下载模型:ModelScope 比 HF 稳
模型下载是这个流程里最容易让人崩溃的一环。Hugging Face 在国际网络环境下通常没问题,但国内网络访问经常超时,断点续传又不稳定。我的建议是直接用 ModelScope,它和 Hugging Face 的接口风格几乎一致,但服务器在国内,下载速度非常稳。
先安装 modelscope:
pip install modelscope然后执行下载:
mkdir -p ~/models/Qwen3-8B-FP8 modelscope download --model 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命名的分片权重文件。这些文件加起来大概 9 GB 出头,和前面算的权重占用相符。缺任何一个文件都可能导致加载失败,所以下载完最好先检查一遍再继续。
3.3 安装 vLLM(Python 环境版)
我建议用 Miniconda 管理 Python 环境,这样后续升级和迁移都干净利落。在 WSL 里安装 Miniconda:
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh安装过程中一路回车,最后选择“yes”让它初始化环境变量。然后创建一个独立的虚拟环境:
conda create -n vllm python=3.11 -y conda activate vllmPython 版本我推荐 3.11,这是当前 vLLM 经过充分测试的版本,3.10 也可以,但没必要用太新的 3.12 或 3.13,因为某些编译依赖对 Python 版本敏感,容易踩小坑。
接着安装 vLLM:
pip install --upgrade pip pip install vllm这一步会自动拉取 PyTorch(带 CUDA 版本)和一堆依赖,总下载量大概两三个 GB,需要等一段时间。装完后验证一下:
python -c "import vllm; print(vllm.__version__)"能正常输出版本号,说明安装成功。这里不需要手动安装 CUDA Toolkit,也不用自己去配置LD_LIBRARY_PATH,vLLM 自带的 PyTorch 已经把 CUDA 运行库都封装好了。
3.4 Docker 路线备用方案
如果你更偏好 Docker 的环境隔离方式,也可以走 Docker Desktop。安装 Docker Desktop 时选择启用 WSL2 后端,然后把 WSL 的 Ubuntu 集成进去。之后用一条命令就能启动服务:
docker run --gpus all -v ~/models:/models -p 8000:8000 vllm/vllm-openai:latest --model /models/Qwen3-8B-FP8 --host 0.0.0.0 --port 8000不过说实话,除非你是要在多台机器上快速复现一模一样的部署,否则我仍然觉得原生 Python 环境更轻量。Docker 镜像体积大、升级麻烦,而且文件共享层偶尔会出一些诡异的问题,比如 vLLM 加载权重时对文件锁的处理到了 Docker 挂载卷里会报权限错误。如果你是第一次接触 vLLM,我建议直接跳过 Docker 这条路线,先用 Python 环境把核心逻辑跑通,之后再按需引入 Docker。
4. 真正跑起来:vllm serve 的完整实操
4.1 第一行启动命令
环境准备好、模型下载好之后,激动人心的时刻来了。在 vLLM 的较新版本里,启动服务的标准命令是vllm serve。一个最小可用的命令长这样:
vllm serve ~/models/Qwen3-8B-FP8 \ --host 0.0.0.0 \ --port 8000这里--host 0.0.0.0是为了让服务监听所有网络接口,这样 Windows 宿主和其他局域网机器都能访问到;如果你只打算在本机跑,写--host 127.0.0.1也可以。初次启动时,vLLM 会先解析模型配置文件,然后加载分片权重,紧接着初始化 CUDA graph 和分配 KV Cache 显存。
你可能会在日志里看到大量跟 NCCL、pynccl 相关的输出,甚至某些警告。这里有个判断经验:如果你只有单卡,这些 NCCL 日志基本可以忽略,它只是在初始化单机通信,并不会真的跨越不同机器做通信。我之前第一次看到这些红色日志时还担心是不是环境出问题了,后来确认是正常输出。
看到Application startup complete或者类似的日志,说明服务已经起来了。这时候在 Windows 浏览器里直接访问http://localhost:8000/docs,能看到一个自动生成的 API 文档页面,说明服务已经正常对外工作。是的,WSL2 默认会把 Linux 里的端口转发到 Windows localhost,这一步不需要额外配置。
4.2 关键参数逐个拆解
只用上面的最小命令,服务能启动,但大概率跑得不是最优。因为我实际部署时发现,显存利用率、并发数、上下文长度这三个参数直接决定了服务的可用性和吞吐。下面是我经过多轮调整后比较推荐的一套启动参数:
vllm serve ~/models/Qwen3-8B-FP8 \ --host 0.0.0.0 \ --port 8000 \ --served-model-name qwen3-8b \ --max-model-len 32768 \ --gpu-memory-utilization 0.92 \ --max-num-seqs 64逐个解释。--served-model-name qwen3-8b给模型起了个短名字,因为 API 请求里的model字段要和服务端注册的名字保持一致,你总不想每次都传一长串路径。--max-model-len 32768表示最大上下文长度,这里设成 32K,它直接决定 KV Cache 的预分配大小,如果你的显存偏小,降到 8192 会明显降低显存压力。
--gpu-memory-utilization 0.92的意思是允许 vLLM 使用 92% 的显存,剩下的留给 CUDA context 和系统开销。这个值设成 0.9 左右比较安全,如果设得太高(比如 0.99),在某些显卡上会因为显存碎片导致分配失败。--max-num-seqs 64控制同时处理的序列数,这是个吞吐和显存的折中值。显存紧张时可以降到 16,显存充裕可以提高到 128。
如果启动时遇到奇怪的显存报错,可以加一个--enforce-eager参数,它会让 vLLM 关闭 CUDA graph 优化,用更朴素的方式执行推理,显存占用更低,但推理速度会有一定下降。这个参数适合先确认链路通不通,确认之后再去掉。
4.3 日志怎么看
vLLM 的启动日志其实隐藏了很多排查信息,很多人在服务启动失败时不知所措,其实就是没学会读日志。我建议重点关注三个地方。
第一是模型加载阶段,日志会显示Loading safetensors checkpoint shards以及每个分片的加载进度。如果这里卡住不动,多半是磁盘 IO 问题,回想一下是不是把模型放在了/mnt/d这种跨系统路径上。第二是 KV Cache 分配阶段,日志会输出类似# GPU blocks: XXXX的信息,这个数字乘以 block size 就是你当前配置下可用的 KV Cache 总量。如果这个数字非常小(比如只有几百),那说明显存被权重占掉太多,需要调低--max-model-len或--max-num-seqs。第三是最终的监听地址,日志会明确告诉你服务监听在哪个 IP 和端口上,如果你的请求发过去连接不上,先看看这里是不是0.0.0.0:8000。
另外,启动参数的完整记录也会在日志里以EngineArgs的形式打印出来。有一次我排查一个诡异的显存问题,就是靠日志里打印的启动参数发现之前脚本里写错了--max-model-len的值,这类问题如果只凭记忆很难发现。
4.4 设置 systemd 开机自启(可选)
如果你打算把 vLLM 当做一个常驻服务长期使用,那手动开着终端不是一个好方案。WSL2 较新版本支持 systemd,开启之后就可以用服务管理的方式运行 vLLM。
先编辑/etc/wsl.conf:
[boot] systemd=true然后重启 WSL2:
wsl --shutdown重新进入 WSL 后,创建一个 systemd 服务文件/etc/systemd/system/vllm.service:
[Unit] Description=vLLM Serving Qwen3-8B-FP8 After=network-online.target Wants=network-online.target [Service] Type=simple User=你的用户名 Environment="PATH=/home/你的用户名/miniconda3/envs/vllm/bin:/usr/local/bin:/usr/bin" ExecStart=/home/你的用户名/miniconda3/envs/vllm/bin/vllm serve /home/你的用户名/models/Qwen3-8B-FP8 --host 0.0.0.0 --port 8000 --served-model-name qwen3-8b --max-model-len 32768 --gpu-memory-utilization 0.92 --max-num-seqs 64 Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target注意ExecStart里要用绝对路径,因为 systemd 不会自动加载 conda 的环境变量。然后执行:
sudo systemctl daemon-reload sudo systemctl enable vllm sudo systemctl start vllm这样只要 WSL2 启动,vLLM 就会自动拉起,和后端服务器跑在 Linux 上的体验基本没什么区别。这个小坑我提过很多次:如果ExecStart里只写vllm而不是绝对路径,服务会一直报找不到命令,因为 systemd 的环境里没有 conda 的 PATH。
5. 验证推理:OpenAI 兼容接口调用
5.1 curl 一发入魂
vLLM 启动后默认提供 OpenAI 兼容的 API。这意味着之前你用 OpenAI SDK 写过的代码,只需要改一下base_url就能切到本地模型。用 curl 做一次最简单的验证:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b", "messages": [{"role": "user", "content": "用一句话解释什么是 KV Cache"}], "max_tokens": 256, "temperature": 0.7 }'注意model字段的值要和启动时--served-model-name保持一致。如果连不上,先检查服务日志是否还在运行,再用curl http://localhost:8000/health看健康检查接口是否有响应。
返回结果是一个标准的 OpenAI 格式 JSON,里面包含choices数组和usage字段,后者会告诉你这次请求用了多少 token。这个信息在估算成本和调优时非常有用,我建议一上手就养成看usage的习惯。
5.2 Python 客户端和流式输出
如果你要写代码调用,用 openai 这个 Python 包最省事:
from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", ) resp = client.chat.completions.create( model="qwen3-8b", messages=[ {"role": "system", "content": "你是一个乐于助人的助手。"}, {"role": "user", "content": "给我写一段快速排序的 Python 代码"}, ], max_tokens=1024, temperature=0.7, ) print(resp.choices[0].message.content)api_key随便填一个非空字符串就行,vLLM 默认不做鉴权,但接口格式要求这个字段必须存在。
流式输出在交互式应用里几乎是必须的,否则用户要等好几秒才能看到第一个字。vLLM 完整支持流式,只需要在请求里加一句stream=True:
resp = client.chat.completions.create( model="qwen3-8b", messages=[{"role": "user", "content": "讲一个关于程序员的冷笑话"}], max_tokens=512, stream=True, ) for chunk in resp: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True)这里有个实际经验:流式模式下,每个 chunk 的choices[0].delta.content可能为空(比如当次只返回了角色标记或结束标记),所以一定要加if delta的判断,否则控制台会打印一堆None,而且在一些 Web 框架里还容易报序列化错误。
5.3 快速压力测试与性能指标
服务跑通之后,我们可以做一个简单的压力测试,看看机器到底能承受多少并发。常见的大模型性能指标有三个:TTFT(Time To First Token,首 token 延迟)、TPOT(Time Per Output Token,平均每个输出 token 的耗时)和整体吞吐(token/s)。vLLM 自带了一个压测工具,可以直接用:
vllm bench serve qwen3-8b \ --host localhost \ --port 8000 \ --max-num-seqs 64 \ --num-prompts 100 \ --request-rate 10这个工具会往里并发发送 100 个请求,每个请求默认会生成一定数量的输出 token,最后统计出 TTFT 和吞吐量。如果你不想安装额外依赖,也可以自己写个脚本顶一顶,但 vLLM 自带工具的好处是参数标准化,结果可以直接对比。
我在一台 4070 Ti SUPER(16 GB 显存)上跑过一组数据供你参考:max-model-len=32768、max-num-seqs=64时,单请求 TTFT 大概在 100~200 ms 之间,输出速率大约 40~60 token/s,批量压测时总吞吐能到 300 token/s 以上。这个数字已经足够支撑个人使用或者小团队内部工具了。
6. 常见问题与排查技巧实录
6.1 常见问题速查表
部署过程中最容易遇到下面这几个问题,我把现象、原因和解决办法直接整理成了表格,方便你对照排查:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
WSL 里nvidia-smi执行报错 | Windows 驱动过旧、WSL 内核过旧 | 执行wsl --update,升级 Windows 显卡驱动 |
| 启动时 CUDA out of memory | 权重占用显存太多,或--gpu-memory-utilization设置过高 | 降低--max-model-len,降低--max-num-seqs,或将--gpu-memory-utilization降到 0.85 |
| 加载模型权重极慢 | 模型放在/mnt/d等跨系统路径 | 将模型拷贝到 WSL 虚拟盘~/models/下 |
Windows 浏览器访问localhost:8000失败 | 服务未监听0.0.0.0,或 WSL2 端口转发异常 | 确认启动参数里有--host 0.0.0.0,执行wsl --shutdown后重启 WSL2 |
| API 请求返回 model not found | model字段与--served-model-name不一致 | 要么请求里传完整模型路径,要么用短名字并且保持一致 |
| 启动日志出现 NCCL 相关红色报错 | 单卡场景下的初始化提示,或环境变量缺失 | 单卡基本忽略;多卡检查显卡间通讯是否正常 |
6.2 显存不足的排查模型
显存不足这个问题值得单独说说。很多人的第一反应是换更大的显卡,但很多时候其实是参数没调对。我建议按下面这个顺序排查:
第一,看权重本身的占用是否合理。Qwen3-8B-FP8 的权重大概 9 GB,如果你的模型路径指向了 BF16 版本,那权重占用会直接翻倍到 16 GB 以上,再大的显存也不够。第二,看上下文长度设置。--max-model-len每翻一倍,KV Cache 占用也基本翻一倍,从 32768 降到 8192,显存压力会立刻缓解。第三,看并发数。--max-num-seqs是同时处理的序列数,这个值过大会导致 KV Cache 被多个序列瓜分,单位序列可用的上下文长度就变小了。
如果你用了上述所有手段还是 OOM,再加一个--enforce-eager参数。CUDA graph 优化虽然能提升推理速度,但会额外占用几百 MB 到 1 GB 的显存,关闭之后能再挤出一部分空间。这套排查阶梯我几乎每次都能用上。
6.3 WSL2 独有问题的处理
WSL2 有别于原生 Linux 的几个问题,我在这里也一并说透。
第一个是内存占用。WSL2 默认的内存管理策略会尽可能多地占用 Windows 物理内存,导致 Windows 本体出现卡顿。这就是前面强调.wslconfig的原因,把内存限制在 32 GB 或更低能让两个系统和平共处。
第二个是 localhost 转发失败。大多数时候 WSL2 会自动把 Linux 里的端口映射到 Windows 的localhost,但偶尔会出现映射丢失的现象。我实测有效的解决方法是:
wsl --shutdown然后重新启动 WSL2,绝大多数端口转发问题都能解决。如果你的网络环境比较复杂,也可以通过networkingMode=mirrored让 WSL2 直接共享 Windows 的网络栈,端口访问会更直接。
第三个是磁盘空间膨胀。WSL2 的虚拟磁盘文件会随着你安装依赖、下载模型而不断增长,但删除文件之后,虚拟磁盘并不会自动缩小。长此以往,你可能会发现 Windows 的 C 盘空间被一个巨大的ext4.vhdx文件吃掉了。解决方法是在 PowerShell 里执行:
wsl --shutdown Optimize-VHD -Path "C:\Users\<用户名>\AppData\Local\Packages\CanonicalGroupLimited...\ext4.vhdx" -Mode Full注意Optimize-VHD是 Hyper-V 管理工具里的命令,如果没有安装 Hyper-V 管理功能可能会报错。不过这个属于进阶优化,一般跑个一两个月再处理一次就行。
最后说点我的个人体会。把这套流程完整跑通之后回头看,我发现 Windows 上部署 vLLM 真正卡人的地方往往不在 vLLM 本身,而在 Windows 和 WSL2 之间那层“网络和文件系统”的转换。模型放对位置、端口映射搞清楚、显存参数调恰当,剩下的就是很标准的 vLLM 使用流程。建议你第一次跑先用--max-model-len 8192 --max-num-seqs 16把链路走通,确认服务能稳定响应之后,再慢慢把上下文长度和并发数拉上去。这样即使中间出了问题,也能快速定位是环境问题还是参数问题。
如果你后续想把它接到更完整的应用里,vLLM 这个 OpenAI 兼容接口可以直接对接 Dify、FastGPT 这类开源应用,也可以通过 One-API 这类网关统一管控。先让服务跑起来,后面能玩的花样就很多了。