
在 Windows 上跑 vLLM 这件事我在本地一共折腾了将近两天。先说结论Windows 原生环境直接 pip install vllm 基本是走不通的最靠谱的路线就是 Docker Desktop WSL2 后端 NVIDIA Container Toolkit然后拉一个 vllm/vllm-openai 镜像把 Qwen3-8B-FP8 跑起来。这套组合我在自己那台 4070Ti 16G 的机器上已经稳定跑了一个多星期中间还顺手处理了端口冲突、显存溢出、容器重启之后连不上服务这几个问题。这篇文章就是把整个流程从头到尾拆开讲清楚包括为什么这么选、每一步命令背后的理由、以及实测遇到过的坑照着走基本能复现。整篇内容不会只停留在启动日志绿色 OK 那一步还包含推理请求怎么发、OpenAI 兼容接口怎么调、性能指标从哪里看。如果你也打算在 Windows 上本地部署 Qwen3-8B-FP8或者想用 vLLM 做后续的 API 服务开发这篇文章可以直接当操作手册用。1. 为什么 Windows 本地跑 vLLM 必须走 Docker1.1 Windows 原生环境装 vLLM 的坑在哪里很多人第一步想到的就是在 Windows 的 Python 环境里直接 pip install vllm但实测下来这条路非常难走。vLLM 的依赖链里有好几个组件在 Windows 上根本没有官方预编译包典型的就是 flash-attention 和部分 CUDA 扩展。安装的时候经常遇到编译器报错、找不到 nvcc、或者链接库不匹配就算你装了 Visual Studio Build Tools 和 CUDA Toolkit也大概率卡在编译环节。另一个问题是显存和内存的管理方式。vLLM 在 Linux 上可以直接通过 pynvml 之类的方式查 GPU 状态和显存分配在 Windows 上这套机制虽然也能工作但 Docker 容器里封装的 CUDA 运行库和宿主机驱动之间的匹配关系更难控制出了问题日志还特别绕排查起来很不友好。所以我的建议非常简单Windows 上不要和这个编译地狱死磕直接用 Docker 把整个 Python 环境、CUDA runtime、vLLM 二进制全部封装好宿主机只负责提供 NVIDIA 驱动和 GPU 透传能力。这也是目前社区里唯一可靠的方案。1.2 对比几条可行的部署路线我实际试过或者听过周边人试过的方案主要有这么几条方案可行度说明Windows 原生 pip 安装 vLLM极低依赖编译问题多官方也不支持WSL2 里直接建 Python 环境安装 vLLM中等比原生好一点但同样要处理 CUDA 和编译依赖WSL2 Docker 容器跑 vLLM高我最推荐环境隔离好复现方便双系统切 Linux高但麻烦性能最好但日常使用成本高WSL2 里直接建虚拟环境装 vLLM 我试过一次虽然比 Windows 原生好一些但还是会遇到 flash-attention 的编译问题而且升级 vLLM 版本的时候很痛苦。Docker 方案最大的优势就是你不需要关心 vLLM 内部用什么版本的 torch、什么版本的 CUDA runtime镜像里全部锁好了换版本就是换 tag。1.3 我选定的最终架构我这台机器的配置是 4070Ti 16G系统是 Windows 11驱动已经更新到较新的版本。最终采用的结构是这样的Docker Desktop 运行在 Windows 上使用 WSL2 后端容器内跑 vllm/vllm-openai 镜像挂载宿主机上的模型目录NVIDIA Container Toolkit 负责把宿主机的 GPU 透传给容器。模型文件用 HuggingFace 的官方仓库下载好Qwen3-8B-FP8 大概 8 个多 G放在固态盘上就可以。这套架构的好处是宿主机上只需要装三样东西NVIDIA 驱动、Docker Desktop、NVIDIA Container Toolkit。其他任何 Python 包、CUDA 组件都不用碰出了问题删掉容器重来十分钟就能恢复一个干净环境。2. 环境准备驱动、Docker Desktop 与 GPU 透传2.1 先确认你的显卡和显存够不够Qwen3-8B 是 80 亿参数规模的模型FP8 量化之后权重文件大约 8.6GB 左右。16G 显存的显卡可以跑但要注意别把 max-model-len 和 gpu-memory-utilization 设置得太激进否则很容易 OOM。我实测下来 16G 显存跑 FP8 版 8B上下文长度开到 8192 是比较稳的开到 16384 就可能出现显存压力。显存不光是给模型权重用的还要给 KV Cache、激活值、CUDA context 留出空间。FP8 相比 BF16 最直接的好处就是权重显存减半BF16 需要 16G 左右FP8 只要 8G 多省下来的显存刚好给 KV Cache 用。如果你的卡只有 8G 显存也不是完全不能跑但 max-model-len 要压到 4096而且最好开 enforce-eager 模式关掉 CUDA graph否则容易在启动阶段就报显存不足。2.2 安装 WSL2 和 Docker Desktop第一步是把 WSL2 装好。在管理员 PowerShell 里执行 wsl --install装完重启然后确认一下版本wsl --status 输出里应该显示 WSL 版本为 2。如果之前装过 WSL1记得用 wsl --set-version 发行版名 2 手动转换。接着下载 Docker Desktop 安装包安装的时候勾选 Use WSL 2 based engine。装完启动之后在 Settings - Resources - WSL Integration 里把你要用的发行版打开这样 Docker 命令才能在 WSL2 里正常工作。这一步很多人会漏不打开的话 WSL 里执行 docker ps 会报找不到 daemon。2.3 安装 NVIDIA Container ToolkitWindows 宿主机上其实不需要装完整的 CUDA Toolkit只需要 NVIDIA 驱动。容器的 CUDA 运行时是镜像自带的但驱动层需要把 GPU 能力透传给容器这就需要 NVIDIA Container Toolkit。安装方式比较直接在 WSL2 的发行版里执行官方给的脚本命令装好之后把 nvidia 的 runtime 加到 Docker daemon 配置里。这一步在 Docker Desktop 的新版本里其实有简化但手动配置一遍更稳妥。配置完了重启 Docker Desktop然后跑一下验证命令docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi如果输出能看到你的 GPU 型号和驱动版本说明 GPU 透传已经通了。这个验证容器很小几十兆跑完就删很干净。我在这步踩过一次坑就是 Docker 重启之后 nvidia runtime 没生效后来检查发现是 daemon.json 里的配置被 Docker Desktop 覆盖了重新加一次就好。3. 模型准备下载 Qwen3-8B-FP8 权重3.1 从哪个渠道下载模型文件Qwen 系列模型在 HuggingFace 上有官方仓库但这个模型比较新所以直接用 vLLM 拉的时候可能需要较新的 transformers 版本容器镜像一般没问题。下载工具我推荐用 huggingface-cli在宿主机 WSL2 环境里装一个独立的 Python 环境就行不用装任何深度学习库只要 huggingface_hub 和 hf_transfer 就行。下载命令大概长这样huggingface-cli download Qwen/Qwen3-8B-FP8 --local-dir /mnt/d/models/qwen3-8b-fp8我习惯把模型放在 /mnt/d/models 下面这样 Windows 的 D 盘和 WSL2 文件系统都能访问后面 Docker 挂载也方便。下载的时候可以设置 HF_ENDPOINT 环境变量来加速但这里我不展开了你自己按实际情况处理。下载完看一眼目录核心文件包括 model.safetensors.index.json、多个分片权重文件、config.json、tokenizer.json还有一个 generation_config.json。Qwen3 的 tokenizer 整合在 tokenizer.json 一个文件里不需要单独下载 vocab 文件这点和 Qwen2 系列保持一致。3.2 FP8 权重文件有什么讲究Qwen3-8B-FP8 这个版本是官方预先用量化方案转好的 FP8 精度权重vLLM 加载的时候能自动识别。和 BF16 版本相比最明显的就是模型文件从 16G 多缩减到 8G 多加载时间和显存占用都下来了。实际部署的时候要留意一点vLLM 对 FP8 的加载和推理支持已经比较成熟启动日志里会出现 FP8 相关的初始化信息。如果你用的是比较老的 vLLM 版本比如去年的镜像可能会遇到不识别 FP8 格式的问题那就直接拉最新的 vllm/vllm-openai 镜像不会有这个问题。还有一点值得提FP8 的推理质量相比 BF16 基本没有肉眼可见的下降至少在中文任务和代码生成任务上我没感觉到差距。换取的是更低的显存占用和更快的并发吞吐所以 FP8 版本做本地部署是很划算的选择。4. 用 Docker 启动 vLLM 服务4.1 推荐用 docker compose 管理别裸跑 docker run我一开始图省事直接用 docker run 跑命令长不说改参数还要把整个命令重新敲一遍或者翻历史记录。后来改成 docker-compose.yml 管理清爽很多而且后续升级镜像、改端口、加环境变量都在同一个文件里改。我的 compose 文件大概是这样的services: vllm-qwen3: image: vllm/vllm-openai:latest container_name: vllm-qwen3 runtime: nvidia environment: - NVIDIA_VISIBLE_DEVICESall - HUGGING_FACE_HUB_TOKEN${HF_TOKEN} volumes: - /mnt/d/models:/models ports: - 8000:8000 ipc: host shm_size: 16g command: - --model/models/qwen3-8b-fp8 - --served-model-nameqwen3-8b-fp8 - --port8000 - --host0.0.0.0 - --gpu-memory-utilization0.9 - --max-model-len8192 - --worker-use-ray0这里有几个关键点要解释一下。runtime: nvidia 是让容器使用 NVIDIA Container Toolkit 提供的 runtime没有这行 GPU 根本透传不进去。ipc: host 是 vLLM 官方推荐配置因为 vLLM 的多进程通信依赖共享内存如果不开 host 模式容器默认的 /dev/shm 只有 64M很容易崩溃报错往往和 shared memory 相关。shm_size 设成 16g 是双保险有些场景下 IPC host 不够再加一层共享内存保障。gpu-memory-utilization 的含义是 vLLM 最多可以占用多少比例的 GPU 显存这里设 0.9 表示能占用到 90%。如果设成 1.0加载权重时一旦激活值超一点就直接 OOM设太低又浪费显存KV Cache 会变得很小影响并发能力。16G 显存实测 0.9 是合理值。max-model-len 控制在 8192。有朋友问能不能开到 32K因为 Qwen3 本身支持长上下文。理论可以但 16G 显存开 32K 之后 KV Cache 占用会暴涨并发能力会断崖式下降甚至可能 OOM。所以本地单机部署8K 是兼顾长度和并发的折中方案。4.2 启动阶段哪些日志值得关注docker compose up -d 之后用 docker logs -f vllm-qwen3 跟踪日志。整个启动过程有几个阶段值得留意第一阶段是加载 tokenizer 和模型配置大概几秒钟。第二阶段是加载权重FP8 权重 8 个 G从机械盘加载可能要一分钟以上从固态盘就快很多二十秒左右。第三阶段是 CUDA graph 捕获这一步会在日志里看到 CUDA graph 相关输出如果显存比较紧张可以考虑加 --enforce-eager 跳过代价是每次请求的延迟略高、吞吐略有下降但启动更稳。最后看到类似 Starting vLLM API server on http://0.0.0.0:8000 的日志就说明服务已经起来了。我用的是 latest 镜像vLLM 版本应该比较新启动阶段会自动检测 Qwen3-8B-FP8 的量化格式并加载。4.3 启动之后立刻验证服务是否正常服务起来了不代表能正常推理我习惯先用 Python 的 requests 发一个最简请求看看整体链路是不是通的。Windows 宿主机上直接访问 localhost:8000 就行因为 Docker Desktop 会把端口映射到本机。import requests payload { model: qwen3-8b-fp8, messages: [ {role: user, content: 用一句话介绍你自己} ], max_tokens: 128 } resp requests.post(http://localhost:8000/v1/chat/completions, jsonpayload) print(resp.status_code) print(resp.json()[choices][0][message][content])如果这个能跑通说明模型加载、推理、接口返回全链路都没问题。第一次请求一般会比后续慢一些因为要完成 CUDA context 的初始化和 KV Cache 的预分配不用慌。5. 推理验证与性能实测5.1 OpenAI 兼容接口的调用方式vLLM 的服务接口完全兼容 OpenAI 的 chat completions 格式所以任何支持 OpenAI API 的客户端都能直接指向本地服务。我在测试时用 Python 的 openai 库验证过只需要改 base_url 就能切换from openai import OpenAI client OpenAI( api_keyEMPTY, base_urlhttp://localhost:8000/v1 ) resp client.chat.completions.create( modelqwen3-8b-fp8, messages[{role: user, content: 写一段 Python 快排代码}], max_tokens512, temperature0.7 ) print(resp.choices[0].message.content)api_key 随便填都行本地服务不校验这个设计对调试来说很省事。另外 Qwen3 系列的 chat template 已经内置在 tokenizer 里vLLM 会自动处理角色标签、终止符这些细节不需要手动拼 prompt。5.2 从 /metrics 看推理性能vLLM 自带 Prometheus 格式的 metrics 接口路径是 http://localhost:8000/metrics。虽然日常调试不一定需要完整监控系统但有几个指标值得手动看一眼gpu_cache_usage_perc 表示 KV Cache 占用率、num_requests_running 表示当前并发请求数、tpt 开头的指标是模型输出吞吐。我实测跑 Qwen3-8B-FP8 的场景大概是单请求时输出吞吐能到 50-80 tokens/s具体取决于显卡型号和 prompt 长度。16G 显存下开并发 8 个请求每个请求的延迟会有一定上升但吞吐总量是增加的。这个数据仅供参考不同卡、不同驱动、不同 vLLM 版本会有差异。5.3 用命令行快速压测vLLM 镜像里自带一个 bench 脚本叫 vllm bench serve用法很简单docker exec -it vllm-qwen3 python -m vllm.bench.benchmark_serving \ --model /models/qwen3-8b-fp8 \ --served-model-name qwen3-8b-fp8 \ --endpoint /v1/chat/completions \ --dataset-name sharegpt \ --num-prompts 50 \ --max-concurrency 4这个压测脚本会模拟多轮对话请求输出平均延迟、TTFT首 token 延迟和吞吐数据。我第一次跑的时候发现首 token 延迟偏高后来排查发现是 max_model_len 过大导致 KV Cache 碎片化调小之后明显改善。6. 常见问题与排查技巧实录6.1 典型报错速查表报错信息或现象可能原因解决方案容器启动即退出GPU 透传没配好或显存不足先跑 nvidia-smi 容器验证再看显存占用CUDA out of memorygpu-memory-utilization 过高或 max-model-len 过大调低到 0.85或改小 max-model-len请求返回 400 或 422模型名写错或 payload 格式不符检查 served-model-name确保 model 字段匹配首 token 延迟特别高KV Cache 碎片化或 prompt 过长减小 max-model-len或增大并发docker compose 拉镜像慢网络不稳定配置镜像加速器或提前拉好镜像服务重启后连不上容器端口映射失效检查 docker ps确认端口映射6.2 请求体太大导致 413 错误有次我在测试长文档总结的时候发现请求体稍微大一点服务直接返回 413 Request Entity Too Large。这个不是 vLLM 的问题而是 nginx 或者网关层的限制但本地 Docker 模式没有 nginx 也会遇到本质是 Docker 端口转发对请求大小有限制。解决方式很简单如果用的是 FastAPI 或直接 requests可以开启流式传输或者压缩请求体如果用的是反向代理需要在配置里调大 client_max_body_size。本地调试我一般直接把长文本切成多次请求。6.3 冷启动慢和热启动快的现象vLLM 服务每次冷启动都要加载权重、捕获 CUDA graph、预分配 KV Cache这些加起来可能要一两分钟。如果你大量使用服务不建议频繁重启容器因为每次重启都要重新经历这个漫长的加载过程。我的习惯是白天保持容器常驻晚上不用的时候 docker compose stop 挂起。注意 stop 只停容器不删容器下次 start 的时候模型权重加载会快不少因为系统文件缓存还在。如果用 down 的话会删掉容器下次要重新经历完整的冷启动。6.4 GPU 显存明明够却报 OOM这个坑我查了挺久。情况是权重加载看着没问题但一发起请求就报显存不足而且日志里看不到明显的错误点。后来发现是多个容器同时占用了 GPU特别是之前跑过别的模型或服务没释放显存。排查时先看宿主机 nvidia-smi把所有占用显存的进程列出来。用 docker compose down 清理掉其他容器再用 nvidia-smi 确认显存归零最后重启 vLLM 容器。还有一个隐藏原因wsl2 里如果之前有过进程崩溃显存可能没完全释放需要关掉 WSL 再重新进。6.5 并发请求变慢甚至排队时间长本地跑 16G 显存并发能力本来就不是无限大的。Qwen3-8B-FP8 在 0.9 显存利用率下8K 上下文的 KV Cache 大约能支撑 10-20 个并发请求再多就会进入排队状态表现为响应时间变长。如果并发需求高优先考虑把 max-model-len 调小比如只处理 4K 上下文KV Cache 占用会明显下降并发能力提升。或者换更大显存的显卡。软件层面还有个技巧就是给服务加 --max-num-seqs限制同时处理的序列数量这样每个请求的延迟会更平滑不会出现某个大请求霸占所有资源的极端情况。最后的实操体会整套环境搭完之后我现在日常工作流基本就是 Windows 上编辑代码通过 localhost:8000 调用本地的大模型服务跑起来非常顺手。回过头看最值得注意的几点一是别在 Windows 原生环境死磕安装依赖Docker 是唯一省心路线二是显存规划比模型选择更容易踩坑max-model-len 和 gpu-memory-utilization 这两个参数一定要按实际卡型调稍微激进一点就可能 OOM三是模型下载和容器管理最好固定成自动化脚本或 compose 模板后面再部署其他模型就是改个模型路径的事。如果你想把这条路继续走下去后面可以尝试把自己微调过的 LoRA 权重合并进去或者把多个模型放到同一个 vLLM 实例里做多模型路由再配合本地的向量库做一个完整知识库应用。反正基础设施已经打通了剩下的就是个逐步叠功能的过程。