ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

vLLM 报 UVA is not available?把日志贴给走 TaoToken 的 Codex 对照

2026/9/18 14:05:47 拓冰建站 浏览量
vLLM 报 UVA is not available?把日志贴给走 TaoToken 的 Codex 对照 UVA 报错时把 docker logs vllm 原文贴给走 TaoTokenhttps://taotoken.net/?utm_sourcetaotoken_aicg_blog_end的 Codex 逐条对照比翻十篇部署教程管用。RTX 2070 8 GiB 加 WSL2(Ubuntu 24.04) 加 Docker Desktop是本地推理引擎最便宜、也最容易翻车的组合vLLM 0.25 的 V1 引擎把 UVA统一虚拟寻址当成硬依赖而 WSL2 的 GPU-PV 虚拟化层默认不给你 CUDA pinned memory锁页内存容器一起来就直接抛RuntimeError: UVA is not available。这跟模型好不好、吞吐高不高没关系是纯环境层的问题所以你把那篇《推理引擎数据对比》从第一节抄到最后一节照样复现不出来——教程里的机器不是你这台。这一节不重跑对比表只拆第 4 节部署踩坑记录里的三条环境层报错UVA 不可用、可用显存只剩 6.98/8.0 GiB 装不下--gpu-memory-utilization 0.9、拉 HF 模型时蹦出CAS Client Error: HTTP 401, cas-server.xethub.hf.co。做法是你先在本地把docker logs vllm的报错原文、docker-compose.local-llm.yml里 vllm 服务那几行贴出来再让 Codex 对照日志判断该动哪个环境变量、该不该把模型从 3B 降到 1.5B。TaoToken 在这条链路里只干两件事给 Codex 一把 Key给一个统一出口容器、驱动、CUDA 它一律不碰。1. RTX 2070 WSL2 里 vLLM 抛 UVA is not available 的现场1.1 docker logs vllm 里真正该看的是这几行容器起不来的时候绝大多数人第一反应是docker compose up的输出但那玩意只给你一句exited with code 1。要拿的是完整日志docker ps -a | grep vllm docker logs --tail 200 vllm vllm-boot.log 21vllm-boot.log里通常长这样INFO vllm_engine.py] Using V1 engine INFO platform.py] Detected WSL2 environment, GPU-PV layer ERROR engine.py] RuntimeError: UVA is not available CUDA pinned memory allocation failed on device cuda:0注意Using V1 engine这一行。V1 引擎对 UVA 的依赖比老路径硬得多日志里出现pinned memory allocation failed基本就把范围锁死在WSL2 这一层不给锁页内存而不是 vLLM 装坏了或者显存不够。把这几行连同前后 30 行上下文原样贴进对话Codex 才能判断是启动参数问题还是虚拟化层问题只贴最后一行RuntimeError它能给你的只有泛泛而谈。1.2 GPU-PV 层为什么默认关掉 pinned memoryWSL2 的 GPU 是半虚拟化出来的Docker Desktop 又把 WSL2 套了一层。CUDA 想申请锁页内存得靠cudaHostAlloc走到宿主驱动那一侧而 GPU-PV 这条通道默认不把该能力暴露给容器里的进程。表现出来就是裸金属上跑得好好的 vLLM一进 WSL2 容器就报 UVA 不可用。所以排查顺序不是换模型而是确认这个开关在你这套环境里到底有没有生效。有人习惯在 compose 里塞一行开关就以为万事大吉实际情况是环境变量是否被读取、读取后是否真的影响了内存分配路径得靠日志对照。这里正好是 Codex 擅长的事你把日志、compose 片段、vLLM 版本号一起给它让它逐条列出哪一行日志对应哪个开关开关没生效的话证据是什么。1.3 6.98/8.0 GiB 的显存账本和 0.9 利用率UVA 之外同一节还记了第二个坑nvidia-smi在容器里报出来的可用显存是 6.98 GiB而 8.0 GiB 是标称值被系统、桌面合成、WSL2 的显存预留吃掉了一块。这时候你还写--gpu-memory-utilization 0.9等于让 vLLM 按 7.2 GiB 去规划 KV cache实际只有 6.98 GiB启动阶段就崩。算账很简单一个 3B 的 float16 权重本身大约 6 GiB 出头剩下不到 1 GiB 要分给 KV cache、激活值和 CUDA graph 的预留明显不够。降到 1.5Bfloat16 权重掉到 3 GiB 左右才留得出空间。这笔账不要凭感觉算把nvidia-smi输出、模型名、--max-model-len一起贴出来让 Codex 帮你列张表比你在脑子里估要准。2. 把 docker-compose.local-llm.yml 里 vllm 服务那几行摊开2.1 --dtype float16 与 --gpu-memory-utilization 该改哪个原文的 compose 里 vllm 服务的启动参数大概是这个形状也是我们排查的输入services: vllm: image: vllm/vllm-openai:latest ports: - 8000:8000 environment: HF_HUB_DISABLE_XET: 1 VLLM_WSL2_ENABLE_PIN_MEMORY: 1 command: --model Qwen/Qwen2.5-3B-Instruct --dtype float16 --gpu-memory-utilization 0.9 --max-model-len 4096 deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu]先分清两件事--dtype和--gpu-memory-utilization是软件账本UVA 报错是内存申请路径两者不是一处故障。--gpu-memory-utilization从 0.9 调到 0.86 左右是解决 6.98 GiB 装不下预算的问题UVA 那条要走锁页内存开关或者干脆确认这套 WSL2 版本下能不能绕过。把这两条分开写Codex 就不会给你一个把 0.9 改成 0.8的万能答案然后收工。2.2 HF_HUB_DISABLE_XET 与 CAS Client Error: HTTP 401拉模型时的另一条报错CAS Client Error: HTTP 401, cas-server.xethub.hf.co。这是 Hugging Face 的 Xet 存储后端在鉴权环节没过去跟 vLLM 本身无关跟你的账号 token 有没有配、模型是不是 gated 有关。工程上的处理顺序通常是先把 Xet 关掉走传统下载路径compose 里的HF_HUB_DISABLE_XET1就是这个作用如果还是 401再检查本地是否需要用HF_TOKEN拉受限模型。提醒一句HF_TOKEN是你自己的凭证在本机 shell 或.env里设置就行不要连同日志一起贴进对话。排障需要的是报错行和你的 compose 片段不需要一串真实 token。2.3 VLLM_WSL2_ENABLE_PIN_MEMORY 到底有没有被读进去VLLM_WSL2_ENABLE_PIN_MEMORY1这类开关最容易造成改了没用的错觉写了但没被读取或者被读取了但日志里没有任何体现。判断方法很朴素——把那行环境变量注释掉重启一次和打开时重启一次两份日志做 diff。如果两次日志在 pinned memory 相关行上完全一样说明这个开关在当前版本下没起作用那就要换思路而不是继续在 compose 里加变量。这一步交给 Codex 时把打开 / 关闭两份日志按顺序贴出来让它只回答三个问题两次启动在内存分配路径上有没有差别、还有哪些参数会影响这条路径、下一步该做的单个最小实验是什么。一次只验证一个变量比一口气改五个参数强太多。2.4 3B 换成 1.5B 是一次取舍不是认输把模型从 3B 降到 1.5B看起来像降级实际上是让推理引擎能启动这件事先成立。RTX 2070 只有 8 GiB 显存扣掉 WSL2 和桌面占用剩 6.98 GiB硬塞 3B 的 KV cache 只会一路 OOM 或者退回 CPU offload 把速度拖到没法用。先用 1.5B 跑通链路把 provider、端口、鉴权、返回格式全部验证一遍再考虑量化版本或者换更小的上下文长度这条路径比死磕 3B 靠谱。3. 给 Codex 配一条统一出口Key 与 ~/.codex/config.toml3.1 在落地页建 Key模型 ID 以模型广场为准排障对话也需要一把可用的 Key。打开 TaoToken注册账号后在控制台创建 API Key得到的字符串在本机记为YOUR_API_KEY。具体用哪个模型 ID不要凭记忆写直接看模型广场的当前列表把列表里的 ID 原样填进配置。这一步和原文里申请密钥的位置是对应的只是入口换成了上面这个地址。3.2 config.toml 里 model_provider 与 base_url 的写法Codex 的配置文件在~/.codex/config.toml不是 Claude Code 那套ANTHROPIC_*环境变量别混用。写出来的形状是# ~/.codex/config.toml model YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chatKey 放在环境变量里别写进配置文件export TAOTOKEN_API_KEYYOUR_API_KEYYOUR_API_KEY从 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建。这里有个容易混淆的点给 Codex 的 Base URL 是https://taotoken.net/api末尾不带/v1而你本地 vLLM 的 OpenAI 兼容地址是http://localhost:8000/v1末尾必须带/v1。两个地址长得很像用途完全不同一个通向统一出口一个通向你自己机器上的容器。3.3 贴日志的姿势上下文给够别让它猜配好之后把要问的东西按这个顺序组织一段docker logs vllm的报错原文含Using V1 engine那一行、docker-compose.local-llm.yml里 vllm 服务的完整片段、nvidia-smi里那行显存数字、vLLM 的镜像 tag 或版本号。然后明确要求它做对照而不是给建议清单比如逐条指出这些参数里哪些与 pinned memory 相关、哪些与显存预算相关、我该先改哪一个。Codex 在这里的角色是读日志、对参数、给下一步实验不是替你去改 compose 或重启容器。容器在你本地命令你自己敲结果再贴回来。这样收敛得最快也不会出现它替你臆想一个不存在的参数。4. 改完 compose 用 check-provider.ts 复验 provider - vllm 这条链4.1 重跑容器之后先确认的三件事改完--gpu-memory-utilization、模型大小或者锁页内存开关重启容器docker compose -f docker-compose.local-llm.yml up -d docker logs --tail 50 vllm确认三件事日志里不再出现UVA is not available、nvidia-smi在容器内的可用显存和你的预算对得上、curl http://localhost:8000/v1/models能返回模型列表。这三件事过了才轮到应用层验证顺序反了你会拿着一个根本没起来的服务去调 provider 配置越调越乱。4.2 check-provider.ts 最小对话怎么发原文 2.4 节的 check-provider.ts 就是为这一步准备的重写一版可直接跑的// check-provider.ts type ProviderConfig { name: string; baseURL: string; model: string; }; const provider: ProviderConfig { name: vllm, baseURL: http://localhost:8000/v1, model: YOUR_MODEL_ID, }; async function main(): Promisevoid { const res await fetch(${provider.baseURL}/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer YOUR_LOCAL_TOKEN, }, body: JSON.stringify({ model: provider.model, messages: [{ role: user, content: ping }], max_tokens: 16, }), }); console.log(provider , provider.name); console.log(baseURL , provider.baseURL); console.log(status , res.status); console.log(await res.text()); } main().catch((err) { console.error(err); process.exit(1); });跑起来npx tsx check-provider.ts输出里provider vllm、baseURL http://localhost:8000/v1、status 200再加上一段正常的choices内容说明 provider 到本地容器的这条链是通的。YOUR_LOCAL_TOKEN是本地 vLLM 的占位凭证很多本地部署默认不校验别把它和上面那把 Key 搞混。4.3 推理引擎数据对比表在这类报错面前怎么用对比表回答的是同样的硬件上哪个引擎更划算它回答不了这套 WSL2 环境为什么不让申请锁页内存。所以正确顺序是先让引擎能起来再谈吞吐和显存效率拿一份跑不起来的配置去对比数字得出的结论没有意义。等你把 1.5B 跑通、链路验证完再回头把同一模型分别丢给不同引擎跑同样的max-model-len和并发那张表才有参考价值。5. 回到控制台对账再看下一步5.1 这次排障对话有没有记上账Codex 那边对照日志跑了几轮去控制台看一眼这次调用有没有正常记上打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end进控制台看用量列表确认模型 ID 就是你从模型广场选的那个、请求时间对得上。对不上通常是model字段写错或者 Base URL 被顺手加了/v1回~/.codex/config.toml改回来即可。5.2 想让 Codex 长期跟着排障该走哪条路偶尔查一次报错用 TaoToken 模型对话 直接开一轮对话就够把vllm-boot.log和 compose 片段贴进去让它逐条对照如果你打算把这种贴日志、改参数、复验的循环做成日常可以看看 Coding Plan 是否合算Key 不够用了在 控制台 API Keys 里再建一把就行。最后留一句经验WSL2 上的本地推理引擎报错九成不在模型而在这一层虚拟化给不给你内存权限。下次再看到UVA is not available先别急着换模型把日志贴出来让 Codex 告诉你到底该动哪一行 compose。