vllm Docker 起来 8000 不通,是模型没下完?

docker ps 显示容器处于 Up,并不表示 vLLM 已经在 8000 端口监听。先执行 docker logs -f <容器名或容器ID>,至少要看到类似下面的日志,才说明 OpenAI 兼容服务开始接收请求:

INFO:     Uvicorn running on socket ('0.0.0.0', 8000) (Press CTRL+C to quit)

vLLM 容器启动后可能还在下载和加载模型权重;在这个阶段访问 8000 出现 Connection refused,通常不是端口映射写错,而是服务尚未监听。GitHub 上同类问题的已解决记录中,处理方式是等待 Uvicorn 启动,而不是调整并发参数。如果日志一直停住,则继续检查下载鉴权、网络、GPU 可见性和共享内存。

Docker 启动的几个落点:镜像、端口、共享内存

需要启动 OpenAI 兼容 HTTP 服务时,优先使用 vllm/vllm-openai。基础镜像 vllm/vllm 不能直接套用相同的 OpenAI 服务入口;GitHub issue 中可工作的启动方式使用的是 vllm/vllm-openai 镜像。

先单独验证镜像能否拉取:

docker pull vllm/vllm-openai:latest

拉取失败属于镜像仓库、DNS 或代理链路问题,与 vLLM 参数无关。若直接访问 Docker Hub 受限,参考资料给出的代理前缀是 docker.m.daocloud.io,可以有限度地尝试替换镜像地址:

docker pull docker.m.daocloud.io/vllm/vllm-openai:latest

这类代理并非 vLLM 官方镜像站,是否使用应服从团队的安全和供应链要求。latest 适合临时排查,正式环境最好固定经过验证的版本标签或镜像摘要,避免不同机器在不同时间拉到了不同内容。

确认镜像后,可以先使用下面的单容器模板:

docker run --runtime=nvidia --gpus all \
  --name vllm \
  -p 8000:8000 \
  --ipc=host \
  -v "$HOME/.cache/huggingface:/root/.cache/huggingface" \
  --env HUGGING_FACE_HUB_TOKEN="$HF_TOKEN" \
  vllm/vllm-openai:latest \
  --model "<模型路径或 Hugging Face 模型名>" \
  --served-model-name "<对外暴露名>"

这里最容易混淆的是宿主机端口和容器端口。-p 8000:8000 中,左边是宿主机端口,右边才是 vLLM 容器端口;如果宿主机 8000 已被占用,改成 -p 8001:8000,调用地址也相应改成 http://127.0.0.1:8001/v1。服务已经输出 Uvicorn 监听日志但宿主机仍访问不到时,可以先用下面的命令确认 Docker 实际发布了哪个映射:

docker port vllm

--ipc=host 对应 Compose 中的 ipc: host。多卡部署示例通常会带上这项配置,为进程提供更大的共享内存环境;它解决的不是端口监听问题,也不应该因为一次下载期的 Connection refused 就盲目加入。小模型能够正常运行时,可以结合日志判断是否确实需要。

Hugging Face 缓存目录挂载后,后续重启不必重复下载全部文件。私有或受限模型再通过 HUGGING_FACE_HUB_TOKEN 传入令牌;公开模型不需要时可以删除这一行。如果希望完全避开启动期下载,直接挂载已经准备好的本地模型更省事:

-v /srv/models:/models:ro

然后把 --model 改成 /models/<本地模型目录>。使用 ModelScope 时,也可以先用 snapshot_download 下载到宿主机,再把目录挂进容器。

docker run 的优势是参数直接、启动失败时容易看到完整命令;docker compose 更适合固定模型、缓存目录和端口,并交给 Compose 管理生命周期。两种方式没有必然的性能差异,区别主要在排查和维护方式。下面的 Compose 示例适用于支持 GPU device reservation 的 Docker Compose 与 NVIDIA Container Toolkit 环境:

services:
  vllm:
    image: vllm/vllm-openai:latest
    ipc: host
    ports:
      - "8000:8000"
    volumes:
      - ${HOME}/.cache/huggingface:/root/.cache/huggingface
      - /srv/models:/models:ro
      - /data02/log:/var/log/vllm
    environment:
      HUGGING_FACE_HUB_TOKEN: "${HF_TOKEN:-}"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities:
            - gpu
    command:
      - --model
      - /models/<本地模型目录>
      - --served-model-name
      - <对外暴露名>

启动和查看日志仍然使用:

docker compose up -d
docker compose logs -f vllm

挂载 /var/log/vllm 只是提供一个可写目录,并不会自动把 Docker 的标准输出保存进去;vLLM 的启动、KV cache 和异常信息仍应先从 docker compose logs 或 docker logs 获取。如果当前 Compose 版本不接受示例里的 GPU reservation 配置,应先核对 Compose 与 NVIDIA 运行时版本,不要为了绕过解析错误随意删除 GPU 配置。

OpenAI 兼容层跑通后怎么用

服务真正启动后,先请求模型列表。这比只看容器状态更能确认 API 是否可用:

curl -sS http://127.0.0.1:8000/v1/models

vLLM 提供的常见兼容路径包括 /v1/models、/v1/completions 和 /v1/chat/completions。聊天请求可以直接这样发送:

curl -sS http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "<served-model-name>",
    "messages": [
      {"role": "user", "content": "用一句话解释 vLLM"}
    ],
    "max_tokens": 64,
    "stream": false
  }'

已有 OpenAI SDK 时,通常只需把 base_url 指向 http://<宿主机地址>:<端口>/v1。base_url 已经包含 /v1 时,不要再让客户端拼接成 /v1/v1;请求体中的 model 也应填写 --served-model-name 指定的名称,而不是想当然地填写 Hugging Face 仓库名。

工具调用并非所有模型都支持。参考资料列出的 Hermes 格式可以这样开启:

--enable-auto-tool-choice \
--tool-call-parser hermes

hermes、llama3_json 等解析器对应不同模型的输出模板,参数名和可用解析器也会随 vLLM 版本变化。应当以当前模型的工具调用格式为准,再用当前镜像里的 vllm serve --help 核对选项。

旧 issue 中出现过 fastchat not found,当时与 fastchat、transformers 的版本变化有关,而不是 Docker 端口配置问题。现在遇到类似错误时,不建议把旧启动脚本或旧镜像参数原样复制到新版本;先记录镜像标签和 vLLM 版本,再按当前版本的入口与依赖重新创建容器。

并发参数怎么取舍:显存、批大小、并发数互相牵动

并发参数不是彼此独立的旋钮。max-model-len 控制上下文边界,max-num-seqs 控制调度批次里的序列数量,max-num-batched-tokens 控制一次批处理的 token 预算,而 gpu-memory-utilization 决定 vLLM 希望使用多少 GPU 显存。参考资料列出的参数职责以及 AWQ、Tensor Parallel 的部署背景可用于进一步核对。

参数 实际限制 调整方向
--max-model-len 单个请求可使用的最大上下文 长上下文导致 KV cache 压力时优先降低,但请求的输入与输出总长度必须放得下
--gpu-memory-utilization vLLM 的目标显存使用比例 不是越大越好;降低只会给其他进程留空间,也可能进一步压缩 KV cache
--max-num-seqs 单个调度批次可处理的序列数量上限 显存紧张时降低;更多 HTTP 请求可能转入排队,不等于同步执行数
--max-num-batched-tokens 一次批处理允许处理的 token 上限 与并发序列数共同影响吞吐和峰值显存,没有固定比例可照抄
--quantization awq 或 gptq 按对应量化格式加载模型权重 单卡装不下模型权重时考虑,同时需要验证精度和硬件兼容性
--tensor-parallel-size N 将模型切到 N 张 GPU 单卡容量不足时使用,通信拓扑会影响吞吐,不是普通并发旋钮

调整这些参数时需要权衡 KV cache 压力、排队情况以及精度与吞吐损耗,不存在固定比例或普遍适用的单一旋钮。

gpu-memory-utilization 的默认值不能从旧文章直接照抄。参考资料中出现过 0.9 的常见值,也有降低到 0.85 的保守测试值,但默认值和内存计算方式会随版本变化,最终应以当前镜像中的帮助信息为准。如果启动阶段连模型权重都放不下,降低上下文长度不会凭空腾出一块显卡;这时应考虑量化、Tensor Parallel,或者先停止占用同一张卡的其他进程。相反,如果服务可以启动,但在长 prompt 或并发增加后 OOM,重点才应转向 --max-model-len、--max-num-seqs 和批 token 预算。

单卡调优时,不要同时修改所有参数。先把 --max-model-len 限制到业务真正需要的长度,再逐步增加 --max-num-seqs,观察吞吐是否继续上升、队列是否拉长;--max-num-batched-tokens 则根据模型、prefill 策略和当前版本的调度方式单独调整。降低并发可能减少峰值显存,却把等待转移成队列时间;提高批 token 上限可能增加吞吐,也可能抬高单批资源占用。判断依据应是压测结果,而不是某个博客给出的固定组合。

多卡部署前,先确认可见 GPU 数量和通信拓扑:

nvidia-smi topo -m

--tensor-parallel-size 2 还是 4,应由模型能否装入、GPU 数量和显存余量决定。Tensor Parallel 会引入卡间通信,NVLink 或 PCIe 拓扑都会影响实际性能,不能把“容器能看到四张卡”直接等同于“四卡一定更快”。跨节点部署还涉及更复杂的并行和通信策略,不应只增加一个参数解决。

日志是判断显存究竟卡在哪一层的主要依据。可以先过滤启动阶段的关键信息:

docker logs vllm 2>&1 | grep -Ei 'EngineCore|KV cache|prefix cache|OOM'

不同版本字段名称会变化,但应重点核对 EngineCore 对 KV cache 的估算、实际可用显存以及是否发生 OOM。开启 --enable-prefix-caching 后,还要观察重复前缀的命中情况;它对共享系统提示词的请求更有意义,对每次都不同的 prompt 不会凭空降低 KV cache 成本。

跑通之后,下一步应使用 vllm bench serve 或现有并发客户端做一轮固定输入、固定输出长度的压测,逐级增加并发,比较成功率和 P99 延迟,而不是只看 tokens/s。开始前先查看当前版本支持的参数:

docker exec vllm vllm bench serve --help

随后用同一组请求调整 --max-num-seqs 和 --max-num-batched-tokens,再用日志中的 KV cache 状况反过来校正 --max-model-len。最终默认值和参数名称,以容器当前版本的命令为准:

docker exec vllm vllm serve --help
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容