ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Windows 上用 WSL 2 部署 vLLM 完整指南:GPU 推理环境搭建与踩坑记录

Windows 上用 WSL 2 部署 vLLM 完整指南:GPU 推理环境搭建与踩坑记录 一直觉得在 Windows 上跑大模型推理是一件很折腾的事。前阵子我拿到一台带 NVIDIA 显卡的 Windows 笔记本想把它变成一台能随时调用的本地推理服务试了一圈方案最后老老实实回到 WSL 2 把 vLLM 完整装了一遍。整个过程踩了不少坑但走通之后体验确实稳定现在把一步步流程和教训完整整理出来给同样想用 Windows WSL 2 部署 vLLM 的同学一条可以直接照抄的路。这篇内容适合下面几类人想在 Windows 机器上跑 vLLM 服务、本地开发大模型应用、做模型效果验证或者单纯想搞清楚 WSL 2 里 GPU 环境到底怎么配的。跟着这套流程走不需要你有很深的 Linux 经验但至少要知道命令行基本操作。1. vLLM 为什么值得装以及为什么 Windows 上必须走 WSL 2 这条路先把结论说在前面vLLM 是目前本地部署大模型时吞吐量表现最好的推理框架之一它最大的价值来自两项设计——PagedAttention 显存管理机制和 Continuous Batching 连续批处理策略。PagedAttention 的原理可以类比操作系统的虚拟内存管理。传统的推理框架在计算注意力时需要把完整的 KV Cache 放在连续显存里模型一长、并发一高显存碎片就出来了明明总量够用却报 OOM。vLLM 把 KV Cache 切割成固定大小的块类似内存分页可以零散存放并在计算时按索引取用显存利用率明显提升。加上它会在每个 step 动态合并多个请求一起计算而不是等一个请求完整跑完再处理下一个GPU 的算力基本不会闲着。实际跑 Qwen2.5 系列模型时并发请求的吞吐量比朴素 Hugging Face transformers 实现高出数倍这一点在长文本、多路请求场景下体感特别明显。那为什么 Windows 原生环境装 vLLM 很难受vLLM 从设计上就是面向 Linux 的。它的安装包依赖 CUDA 工具链的链接方式、Linux 的共享内存机制、以及大量在 Linux 上默认开启的内核级能力Windows 要么不支持要么行为不一致。而且官方 README 从没承诺过原生 Windows 支持遇到编译错误基本属于自己修非常痛苦。WSL 2 在这里的作用是提供一个与虚拟机类似的真实 Linux 内核但虚拟化开销比传统虚拟机低很多。对 vLLM 来说WSL 2 里的 Ubuntu 环境和一台普通的 Linux 服务器几乎没区别可以正常访问 GPU 驱动、使用共享内存、跑 CUDA 程序。更重要的是WSL 2 的 GPU 透传机制并不是把 GPU 完全模拟一遍而是通过宿主机驱动直接让 Linux 侧程序调用显卡性能损耗可以忽略不计。我实测过同一模型在同硬件上跑 WSL 2 和原生 Ubuntu单请求延迟和并发吞吐都非常接近。所以结论是Windows 用户跑 vLLMWSL 2 是当前综合考虑下最顺的一条路。原生 Linux 双启动性能虽然更好但日常使用不方便虚拟机方案 GPU 透传配置复杂且性能损耗明显Docker Desktop 的 WSL 2 后端本质上还是在 WSL 2 里跑不如直接在 WSL 2 里操作来得透明。2. WSL 2 安装与系统资源配置把基础打稳2.1 安装 WSL 2 并确认版本在 Windows 11 或较新的 Windows 10 上安装 WSL 2 比想象中简单。以管理员身份打开 PowerShell执行wsl --install这个命令会默认安装 Ubuntu 发行版并启用 WSL 2 所需的 Windows 功能组件。如果是 Windows 10 的旧版本可能会需要手动开启“适用于 Linux 的 Windows 子系统”和“虚拟机平台”两项功能然后重启并安装 WSL 2 内核更新包。安装完成后重启系统首次启动 Ubuntu 时会要求设置用户名和密码。用户名会作为 Linux 下的默认用户比如我用的用户名是will后续所有文件路径都从这个用户目录展开。重启后先确认发行版跑的是 WSL 2在 PowerShell 里执行wsl -l -v输出里应该能看到Ubuntu的 VERSION 列是2。如果还是 1执行wsl --set-version Ubuntu 2这一步很重要。WSL 1 没有真正的 Linux 内核很多系统调用不兼容vLLM 跑不起来。2.2 用 .wslconfig 控制内存、CPU 和交换分区WSL 2 默认会吃掉宿主机一半的内存这在跑大模型时经常不够用。vLLM 加载模型时需要较大的内存来放权重、tokenizer 和运行时数据同时还要给缓存留空间。默认配置下经常出现内存不足导致进程被杀。解决办法是在 Windows 用户目录C:\Users\你的用户名\下创建一个.wslconfig文件例如我的配置[wsl2] memory16GB processors8 swap8GB localhostForwardingtruememory是 WSL 2 能使用的最大内存我这里设置 16GBprocessors分配 8 核swap设置交换分区大小在内存不够时可以把部分数据换到磁盘localhostForwardingtrue保证 Windows 侧能通过localhost直接访问 WSL 2 里启动的服务这个开关默认就是开启的但显式写出来更安心。改完配置后需要重启 WSL 2 才会生效wsl --shutdown然后重新进入 Ubuntu。可以用free -h检查内存是否生效。2.3 系统更新与基础工具安装进入 Ubuntu 后先把系统包索引更新一遍sudo apt update sudo apt upgrade -y然后安装后面的步骤一定会用到的工具sudo apt install -y python3-venv python3-pip curl gitUbuntu 22.04 和 24.04 都自带 Python 3python3-venv 一定要装后面创建虚拟环境依赖它。git 和 curl 主要用于下载模型脚本和测试接口。这里补充一个建议如果不是有特殊原因不要直接改系统的全局 Python 环境。vLLM 的依赖项很多包括特定版本的 PyTorch、transformers、tokenizers 等直接装进系统环境很容易和已有的包冲突。后面都基于 venv 用。3. WSL 2 里的 GPU 透传与 CUDA 环境验证3.1 WSL 2 的 GPU 访问机制WSL 2 支持 GPU 透传但它的实现方式和 Docker GPU 直通不太一样。在 WSL 2 里你不需要也不应该在 Linux 侧单独安装 NVIDIA 驱动。WSL 2 启动时会自动把 Windows 宿主机已经装好的 NVIDIA 驱动映射到 Linux 侧你在 WSL 里看到的 GPU 实际上是由 Windows 驱动管理的。所以在 Windows 宿主机上你需要先确认已经安装了较新的 NVIDIA 驱动。可以打开 NVIDIA 控制面板或者直接运行nvidia-smi确认版本。我用的是 560 系列的驱动实测对 WSL 2 的 GPU 透传支持很好。驱动太旧会导致 WSL 里nvidia-smi报错或者 CUDA 调用失败。3.2 在 WSL 2 里验证 GPU 和 CUDA进入 Ubuntu 终端直接运行nvidia-smi重点看驱动的右上角版本信息。在 WSL 2 里如果显示的是 Windows 宿主机的驱动版本说明 GPU 已经成功透传进来了。比如我的输出顶部写着 “Driver Version: 566.03”这就是 Windows 宿主机驱动版本说明链路是通的。接下来验证 CUDA。vLLM 安装时依赖 CUDA runtime这里有两种选择只使用 pip 安装的 PyTorch 自带的 CUDA 库不装系统级 CUDA Toolkit额外安装 CUDA Toolkit包含 nvcc 编译器等。如果只是跑 vLLM第一种就够用了。但如果你之后想自己编译 CUDA 扩展、调试 kernel 或者跑一些需要 nvcc 的脚本建议顺手装一个 CUDA Toolkit。官方推荐从 NVIDIA 官网装但 Ubuntu 的 apt 源也可以虽然版本可能旧一些。我用的是在系统里安装 CUDA Toolkit 12.4 版本用起来没碰到兼容问题。装好后验证nvcc --version如果只是想快速确认 PyTorch 能调用 GPU不需要急着装完整 Toolkit。先创建虚拟环境后再装 torch 验证也不晚。3.3 常见 GPU 验证失败原因这里列举我遇到过的两个比较典型的问题。第一个是nvidia-smi报 “command not found”。这种情况经常发生在 WSL 刚装完、还没执行过 Windows 侧驱动更新的时候。在 Windows 宿主机上更新到最新 NVIDIA 驱动然后执行wsl --shutdown重启 WSL再进来就能看到了。第二个是 PyTorch 能装上但torch.cuda.is_available()返回 False。这个我之前排查了很久最后发现是 WSL 2 内核版本太旧导致的。执行wsl --update把 WSL 本身更新到最新版本就解决了。4. 创建 Python 虚拟环境并安装 vLLM4.1 创建项目目录与虚拟环境我比较习惯把所有模型相关的东西整理在一个固定目录里便于管理。在 WSL 2 里执行mkdir -p ~/projects/vllm-playground cd ~/projects/vllm-playground python3 -m venv vllm-env创建完成后激活source vllm-env/bin/activate看到终端行首出现(vllm-env)前缀就说明激活成功了。这个环境是独立的一套 Python 解释器和包管理空间所有安装操作都隔离在这个目录下不会污染系统环境。4.2 安装 vLLM安装 vLLM 本身只需要一条命令pip install vllm但这条命令的耗时和踩坑概率都不小。vLLM 依赖的 PyTorch 体积很大而且 pip 需要根据当前平台的 CUDA 版本决定下载哪一个 wheel。在国内网络环境下建议把 pip 换成清华源或阿里源下载速度会快很多pip install vllm -i https://pypi.tuna.tsinghua.edu.cn/simple安装过程中 pip 会自动拉取匹配的 PyTorch 版本。常见的坑是 pip 默认拉取的是 CPU 版 PyTorch导致 GPU 不可用。vLLM 的官方 wheel 会强制指定带 CUDA 的 PyTorch 版本正常情况下不会装错。装上之后可以先确认一下python -c import torch; print(torch.__version__, torch.cuda.is_available())输出里应该能看到torch 2.x.xcu12x这样的版本号并且torch.cuda.is_available()是True。如果这里显示 False请回到上一章排查驱动和 WSL 版本。如果网络环境不好或者你想避免官方源下载过慢可以考虑先手动安装 PyTorch 的 CUDA 版本pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124然后再装 vLLM。vLLM 检测到已有对应版本的 PyTorch 就不会重复下载了。不过要注意vLLM 和 PyTorch 版本有对应关系安装时尽量选接近官方文档要求的版本避免出现“不支持的组合”告警。4.3 检查 vLLM 是否安装成功还没下载模型之前可以先用一个无模型的命令快速确认 vLLM 能正常导入python -c from vllm import LLM; print(vLLM import success)这一步主要检查动态链接库是否完整。vLLM 包含一些编译好的 CUDA kernel导入时如果缺少依赖比如libcudnn.so或者libnccl.so会直接报ImportError。我在第一次装的时候遇到过libcublas.so.12: cannot open shared object file这个报错原因是我用 apt 装过一个旧版 CUDA系统里的库路径被覆盖了。解决办法是把虚拟环境里的site-packages下 vLLM 依赖的nvidia/cublas库路径加到LD_LIBRARY_PATHexport LD_LIBRARY_PATH$VIRTUAL_ENV/lib/python3.10/site-packages/nvidia/cublas/lib:$LD_LIBRARY_PATH如果想每次进入环境自动生效可以把这个 export 写到~/projects/vllm-playground/vllm-env/bin/activate文件的末尾。4.4 关于 Python 版本的选择vLLM 对 Python 版本有明确要求旧版本不支持太新的 Python新版本对旧 Python 也有最低限制。官方安装文档会列出支持的 Python 版本范围。以 Ubuntu 22.04 自带的 Python 3.10 为例配合较新版本的 vLLM 是没问题的。如果你用的是 Ubuntu 24.04 自带 Python 3.12需要确认你安装的 vLLM 版本是否支持 3.12不支持的话用update-alternatives切换 Python 版本或者在创建 venv 时显式指定python3.10 -m venv vllm-env需要注意Ubuntu 24.04 默认可能没有安装 python3.10需要从 deadsnakes PPA 或者源码安装。这部分操作比较啰嗦我建议新手直接用 Ubuntu 22.04省去一堆兼容性问题。5. 模型下载与目录规划5.1 选模型显存匹配是第一优先级vLLM 能跑的模型很多包括 LLaMA、Qwen、Mistral、Gemma 等。选模型的第一步不是看能力而是看你的显卡显存。权重加载需要显存KV Cache 也需要显存而且 KV Cache 往往是大头。这里列出我实测时常见的显存匹配关系GPU 显存推荐模型示例说明8GBQwen2.5-1.5B-Instruct / Qwen2.5-3B-Instruct直接跑max_model_len 不要开太大12GBQwen2.5-7B-Instruct / Llama-3.1-8B量化7B 全精度比较紧张需要调低 max_model_len16GBQwen2.5-7B-Instruct比较舒服可以开 8192 上下文24GBQwen2.5-14B-Instruct / 32B 量化14B 全精度能跑上下文长度适中40GBQwen2.5-72B 量化 / 开源大模型基本能尝试各种模型如果显存不满足模型需求vLLM 启动时会报 OOM 或者直接退出。你可以通过降低--max-model-len参数来缩小 KV Cache 占用但模型权重占用的显存是固定的低于权重需求就没法跑。5.2 下载模型ModelScope 镜像更稳模型下载方式主要有两个来源Hugging Face 和 ModelScope。Hugging Face 是全球最大的模型仓库但国内网络访问不稳定。ModelScope 是国内平台下载速度通常更快而且 vLLM 官方支持通过环境变量直接使用 ModelScope。我这边推荐的做法是优先从 ModelScope 下载因为速度快、网络稳定。步骤如下pip install modelscope然后下载模型拿 Qwen2.5-7B-Instruct 举例modelscope download --model Qwen/Qwen2.5-7B-Instruct --local_dir ~/models/qwen2.5-7b-instruct这个命令会把模型的权重、配置文件、tokenizer 等全部下载到~/models/qwen2.5-7b-instruct目录下。下载速度取决于网络Qwen2.5-7B 的权重文件大约 15GBModelScope 一般拉满带宽比 Hugging Face 快不少。如果你已经下载了 Hugging Face 格式的模型vLLM 能直接使用。关键是记住模型目录的绝对路径比如~/models/qwen2.5-7b-instruct。5.3 模型存放位置不要放 /mnt/c这是我在实际使用中踩过的大坑。WSL 2 访问 Windows 文件系统也就是/mnt/c下的路径走的是 9P 协议读写速率比 WSL 内部的 ext4 文件系统慢得多。把十几个 GB 的模型文件放在C:\models之类的位置vLLM 加载权重时需要从 /mnt/c 迁到内存那个速度简直让人怀疑人生。我曾经实测过同一个模型放在 /mnt/c 和放在 ~/models 下的加载时间差异前者慢了近 5 倍。所以模型优先放在 WSL 2 内部的 Linux 文件系统里。如果担心 WSL 磁盘占用太大可以在下载前规划好空间用df -h检查。模型很占空间删掉要谨慎。5.4 配置环境变量为了让 vLLM 正确找到模型常用两种方式在启动命令里直接传模型路径设置HF_HOME环境变量指向你存放模型缓存的目录。我习惯在~/.bashrc里追加export HF_HOME~/models export VLLM_USE_MODELSCOPEtrueVLLM_USE_MODELSCOPEtrue表示当 vLLM 在模型加载时找不到本地模型、需要去线上找时优先走 ModelScope 而非 Hugging Face。这对国内用户非常友好。设置完后source ~/.bashrc6. 离线推理验证与 OpenAI 兼容 API 服务启动6.1 用 Python 脚本做离线推理测试安装完成且模型下载好之后先用一个简单的 Python 脚本验证整个链路。创建一个test_infer.pyfrom vllm import LLM, SamplingParams llm LLM(model~/models/qwen2.5-7b-instruct, max_model_len8192) params SamplingParams(temperature0.7, top_p0.8, max_tokens512) prompts [ 用一句话介绍 WSL 2, 写一段 Python 代码实现快速排序 ] outputs llm.generate(prompts, params) for output in outputs: prompt output.prompt generated_text output.outputs[0].text print(fPrompt: {prompt}) print(fGenerated: {generated_text}) print(- * 50)运行python test_infer.py第一次执行时vLLM 会做 CUDA kernel 预热和模型加载耗时可能比较长屏幕上会打印类似Starting vLLM using 1 GPUs和Loading model weights took ... GB的信息。这一步正常输出就说明安装没白费。如果这里报错重点看是不是模型路径不对、显存不足、或者 CUDA 驱动版本过旧。6.2 启动 OpenAI 兼容 API 服务vLLM 最常用的场景之一是启动一个 OpenAI 兼容的 HTTP 服务这样你本地的任何 OpenAI SDK 或者工具都可以直接把 base_url 指过去。启动命令vllm serve ~/models/qwen2.5-7b-instruct --host 0.0.0.0 --port 8000参数说明--host 0.0.0.0监听所有网卡包括 WSL 2 的虚拟网卡--port 8000服务端口如果需要降低显存占用可以加上--max-model-len 4096。启动成功后在 Windows 宿主机浏览器直接访问http://localhost:8000/v1/models会看到 JSON 格式的模型列表。这就是很典型的验证接口是否正常的方式。如果想从命令行测试对话可以用 curlcurl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d {model: ~/models/qwen2.5-7b-instruct, messages: [{role: user, content: 你好}], max_tokens: 128}返回内容里会包含choices字段里面的message.content就是模型生成的回复。从 Windows 侧访问localhost:8000能通是因为 WSL 2 默认开启了 localhost 转发。如果你的 Windows 10 版本较旧可能需要手动在 PowerShell 里用netsh interface portproxy做端口映射把 Windows 的 8000 端口转发到 WSL 2 的虚拟 IP 上。不过一般情况下默认配置已经够用了。6.3 写一个小工具脚本提升体验为了提高后续使用效率我给 API 服务封装了一个简单的对话脚本放在chat.py里from openai import OpenAI client OpenAI(base_urlhttp://localhost:8000/v1, api_keyEMPTY) response client.chat.completions.create( model~/models/qwen2.5-7b-instruct, messages[{role: user, content: 你好}] ) print(response.choices[0].message.content)需要先pip install openai。用这个脚本可以快速测试服务是否在正常工作比 curl 直观很多。这套 API 兼容性的好处是你可以直接在本地把 base_url 换成 vLLM 服务地址然后继续用 LangChain、Dify 或者其他框架而不改代码。7. 实测中的十大坑与调优经验整个流程走下来我踩了不少问题这里集中记录一下很多问题不装到那时候根本想不到。7.1 共享内存不足导致进程崩溃vLLM 在多并发或处理长上下文时tokenizer 和调度器会在共享内存里临时放数据。WSL 2 里/dev/shm的默认大小是物理内存的一半如果内存不够大服务运行一段时间后可能直接报错RuntimeError: Shared memory area is too small排查方式df -h /dev/shm如果 Size 很小可以临时扩容sudo mount -o remount,size32G /dev/shm这条命令只在当前会话生效重启后会恢复默认。要持久化的话可以在/etc/fstab里加一条配置但更简单的做法是把这条命令放在启动脚本里。我当时直接把 WSL 的内存设置到了 32GB/dev/shm跟着变成 16GB实际使用没再触发过这个错误。7.2 首次启动长时间无输出不是卡死是编译vLLM 第一次加载模型时会根据当前 GPU 环境和模型配置现场编译部分 CUDA kernel。编译过程可能持续几分钟屏幕上甚至没有明显进度提示期间 CPU 占用很高。很多同学以为是安装坏了直接 CtrlC 取消结果下次启动又重新编译。其实一遍编译完成后结果会缓存在~/.cache/vllm目录下下次启动就快多了。遇到这个情况建议心态放稳等 5 到 10 分钟。也可以先执行nvidia-smi看看 GPU 是否在满载计算如果 GPU 利用率高说明正在编译或加载正常等待就行。7.3 模型放 /mnt/c 导致加载极慢这个问题前面提过但我愿意再强调一遍。WSL 2 的磁盘 IO 性能分两层访问 Linux 内部目录和访问 Windows 挂载盘差异非常大。模型权重文件动辄十几 GB放在 /mnt/c 下每次加载都像在用机械硬盘跑重负载。一定要把模型放到 WSL 2 自己的文件系统里比如~/models。同理如果未来把模型放到新挂载的磁盘也注意避免跨文件系统。7.4 宿主机需要关闭快速启动但影响不大和我之前用 Docker Desktop 时的经验不同WSL 2 里跑 vLLM 不用太纠结 Windows 的快速启动功能。不过在宿主机上长时间待机后偶尔会遇到 GPU 透传异常导致 CUDA 程序无响应。最有效的解决手段wsl --shutdown然后重新进入 WSL 2。这个操作会把 WSL 里所有进程关掉对日常开发来说损失不大但对刚启动的 vLLM 服务需要重新拉起。7.5 端口被占用如果启动vllm serve时提示端口被占用先确认是不是同一台机器上已经有一个旧的 vLLM 进程。用ps aux | grep vllm查看并清理旧进程。如果确认没有进程占用但端口起不来检查 Windows 侧的端口转发是否有残留。执行netsh interface portproxy show v4tov4必要时清理旧的 portproxy 规则。7.6 同时跑多个模型时显存不够vLLM 一个实例绑定一个模型如果同时想在多个模型间切换最直接的办法是先停掉一个服务再启动另一个。WSL 2 里 GPU 显存不会自动释放到 Windows 侧可能会出现 Windows 任务管理器显示显存占用不降的情况。这是正常的等进程消失一会儿后显存会慢慢释放。如果你经常需要切多个模型可以考虑把两个模型合成一个目录或者使用支持多模型的入口方案但那样复杂度会高很多。7.7 WSL 2 网络 IP 变化的问题WSL 2 每次重启虚拟机的 IP 地址都会变。如果你设置过基于具体 IP 的端口转发重启后就会失效。这也是我推荐直接通过localhost访问服务的原因。如果一定要从局域网里其他设备访问这台 WSL 里的 vLLM 服务建议用 Windows 侧做端口转发把 Windows 8000 端口转发到 WSL 2 的当前 IP但 IP 变化会导致配置失效得定期更新。这个场景更适合用一个固定内网 IP 的 Linux 机器来做WSL 2 定位在本地开发足够。7.8 当 max_model_len 过小时长文本对话被截断vLLM 的max_model_len参数决定 KV Cache 最多存多少 token。如果设置太小长文本对话或长文档输入会被直接截断损失内容。调大一点虽然显存占用增加但换来的是更自然的交互体验。开启服务时记得根据显存规模和实际场景设置这个参数。我在 16GB 显存下用 Qwen2.5-7B设置--max-model-len 8192是没问题的。7.9 单机多卡支持如果你的电脑有多张 NVIDIA 显卡vLLM 可以自动使用所有 GPU只要设置环境变量export CUDA_VISIBLE_DEVICES0,1然后正常启动即可。WSL 2 对多卡透传的支持依赖 Windows 驱动版本如果你是双卡用户建议先确认nvidia-smi能看到两张卡再尝试混卡推理。我这边只有单卡没有深度验证过双卡但官方文档说明 vLLM 天然支持张量并行和流水线并行WSL 2 环境不会额外增加限制。7.10 对比纯 CPU 模式和 Ollama最后说几句题外话。如果你将来遇到没有 NVIDIA GPU 的设备想用 WSL 2 vLLM 跑纯 CPU 模式直接劝退vLLM 官方不支持纯 CPU 推理它从底层依赖 CUDA GPU。这种情况下建议直接用 llama.cpp 或者 Ollama它们对 CPU 环境更友好。而且 Ollama 在 WSL 2 里安装也很方便直接把模型目录映射过来即可。vLLM 的优势始终是 GPU 吞吐和多路并发场景对了才是利器。就我个人这段时间的使用体验来说WSL 2 vLLM 这套组合非常适合在 Windows 机器上做本地模型服务和开发调试。想把它当生产服务器用还是建议换到纯 Linux 环境毕竟 WSL 2 本身的定位是开发工具而非生产部署平台。如果你正在用 Windows 又受够了各种环境问题按这篇流程走一遍基本能把踩坑概率降到最低。装好后你也能在 Windows 上得到一个响应很快的本地大模型 API 服务。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表