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












暂无评论内容