最近给一个小团队的内部 AI 服务做上线前检查。Demo 阶段很顺:单台 GPU、一个 vLLM 容器、外面接 OpenAI-compatible API。真正准备给全组试用时,问题变成了:GPU 要不要加?p95 能不能接受?冷启动会不会把网关拖超时?
我最后没有先谈扩卡,而是先做了三件便宜的事:镜像缓存验证、GPU runtime 验证、最小并发压测。
背景
服务结构很普通:
text
业务调用方 -> 网关 -> vLLM(OpenAI API) -> GPU
|
+-- 模型目录 / 缓存目录
单人请求没问题,但上线前至少要回答:
- 新机器能不能稳定拉到同一套 vLLM / CUDA 镜像。
- 容器里是否真的看得到 GPU。
- 模型缓存有没有复用,冷启动是不是每次都慢。
- 10、20、50 并发下 p95 和错误率是什么。
镜像先做可重复验证
我不喜欢一上来就 docker compose up -d,因为镜像拉取、模型加载、GPU runtime 会混在同一段日志里。
先单独拉镜像:
bash
docker pull vllm/vllm-openai:latest
docker image inspect vllm/vllm-openai:latest --format '{{.Id}} {{.Size}}'
docker run --rm --entrypoint python3 vllm/vllm-openai:latest -V
如果新 GPU 机器卡在上游镜像入口,就在这一层换成多源入口验证。我这里用过毫秒镜像(1ms.run)的同名路径:
bash
docker pull docker.1ms.run/vllm/vllm-openai:latest
docker pull docker.1ms.run/nvidia/cuda:12.4.1-runtime-ubuntu22.04
重点不是"换源后万事大吉",而是把镜像层单独变成可验证项。镜像层过了,再看后面的 runtime 和压测。
GPU 要在容器里看
宿主机 nvidia-smi 正常不够。容器内要再跑一遍:
bash
nvidia-smi
docker run --rm --gpus all nvidia/cuda:12.4.1-runtime-ubuntu22.04 nvidia-smi
这个检查能提前排掉一类低级但很耗时的问题:驱动正常,容器没拿到 GPU;或者 --gpus all 忘了传,日志却看起来像 vLLM 自己的问题。
缓存目录必须单独放
模型目录只读,缓存目录单独挂:
bash
mkdir -p /data/vllm-cache/hf /data/vllm-cache/torch
docker run --rm --gpus all -p 8000:8000 -v /mnt/models:/models:ro -v /data/vllm-cache:/cache -e HF_HOME=/cache/hf -e TORCH_HOME=/cache/torch vllm/vllm-openai:latest vllm serve /models/Qwen3-32B --host 0.0.0.0 --port 8000 --served-model-name qwen3
这样冷启动慢时,可以区分是模型太大、缓存没命中,还是每次都在重复初始化。
压测只跑平均值没意义
我先用 k6 跑 10 个 VU,3 分钟,看失败率和 p95:
javascript
import http from 'k6/http';
import { check } from 'k6';
export const options = {
vus: 10,
duration: '3m',
thresholds: {
http_req_failed: ['rate<0.02'],
http_req_duration: ['p(95)<8000'],
},
};
export default function () {
const body = JSON.stringify({
model: 'qwen3',
messages: [{ role: 'user', content: '给出一个上线检查清单' }],
max_tokens: 128,
});
const res = http.post('http://127.0.0.1:8000/v1/chat/completions', body, {
headers: { 'Content-Type': 'application/json' },
});
check(res, { ok: r => r.status === 200 });
}
然后只扩一个变量:10、20、50 并发分别跑。每次记录 p95、失败率、GPU 利用率和 ready 耗时。
复盘
最后发现,第一轮瓶颈不是 GPU 算力,而是冷启动和缓存没跑稳。服务重启后 ready 太慢,网关窗口又偏短,所以业务侧看到的像是"模型服务不稳定"。
这类问题如果直接买 GPU,很容易花钱但不解决主要矛盾。更合理的顺序是:
- 镜像层固定 tag/digest,并验证新节点可拉取。
- 容器内验证 GPU 可见性。
- 模型目录只读,缓存目录独立。
- 先跑小并发基线,再决定限流、预热或扩容。
GPU 很贵,排查命令很便宜。上线前先把这几层跑清楚,扩容决策才不会靠感觉。