vLLM离线部署Qwen2.5模型时OOM后自动重启并加载检查点的方法

文章导读
vLLM 离线部署 Qwen2.5 时遇到 OOM,进程会直接退出,vLLM 自身不会自动拉起自己,也不会在重启后恢复当时的运行状态。要做到“OOM 后被重新拉起并加载模型权重”,需要借助 systemd、supervisor 这类外部进程守护工具,并在启动参数和模型加载路径上做配合。下面直接按排查和配置的顺序说明做法。
📋 目录
  1. 一 先确认 OOM 发生在显存还是内存
  2. 二 用 systemd 拉起服务并限制重启频率
  3. 三 检查点能恢复什么、不能恢复什么
  4. 四 重启后验证权重被正确加载
  5. 五 重启仍然 OOM 的常见原因
A A

vLLM 离线部署 Qwen2.5 时遇到 OOM,进程会直接退出,vLLM 自身不会自动拉起自己,也不会在重启后恢复当时的运行状态。要做到“OOM 后被重新拉起并加载模型权重”,需要借助 systemd、supervisor 这类外部进程守护工具,并在启动参数和模型加载路径上做配合。下面直接按排查和配置的顺序说明做法。

vLLM 没有内置 OOM 自动恢复能力。离线部署遇到 OOM 时,需要由 systemd 或同类守护进程完成“进程拉起”,模型权重可以在重启时重新加载,但显存里的 KV cache 和进行中的请求无法通过检查点恢复。因此处理重点应放在控制显存占用、配置受保护的重启策略,以及验证每次重启后确实加载了目标权重。

先确认 OOM 发生在显存还是内存

OOM 的表现不同,处理方向也不同。vLLM 在显存不足时通常会在自身日志里打印 CUDA out of memory;如果整个进程被系统杀掉,则可能是主机内存不足。先看系统日志确认类型:

dmesg -T | grep -i -E "killed process|oom" | tail -20
journalctl -k `--since` "10 minutes ago" | grep -i oom

如果 dmesg 里出现 python 进程被 OOM-killer 杀掉的记录,说明是系统内存或共享内存不足(比如 /dev/shm 太小);如果只是 vLLM 日志报 CUDA OOM,则优先调整显存相关参数。这一步的验证方式就是看日志中的进程是被信号杀掉,还是由 vLLM 自己抛出异常。

用 systemd 拉起服务并限制重启频率

vLLM 没有“OOM 后自动重启”这个配置项,离线部署建议直接用 systemd 管理服务。下面是一个 service 文件示例:

vLLM离线部署Qwen2.5模型时OOM后自动重启并加载检查点的方法
# /etc/systemd/system/vllm-qwen.service
[Unit]
Description=vLLM Qwen2.5 offline server
After=network-online.target

[Service]
User=root
ExecStart=/usr/bin/python -m vllm.entrypoints.openai.api_server \
    `--model` /models/Qwen2.5-7B-Instruct \
    `--gpu-memory-utilization` 0.80 \
    `--max-model-len` 8192 \
    `--host` 127.0.0.1 `--port` 8000
Restart=on-failure
RestartSec=30
StartLimitIntervalSec=600
StartLimitBurst=3

[Install]
WantedBy=multi-user.target

Restart=on-failure 会在进程非正常退出(包括被 OOM 杀掉)时自动重启;StartLimitIntervalSec 和 StartLimitBurst 限制 10 分钟内最多重启 3 次,防止服务反复崩溃导致系统负载异常。如果离线任务本身是一次性脚本,跑完就退出,就不应该用 on-failure,而要在上层调度脚本里根据退出码决定是否重跑。

检查点能恢复什么、不能恢复什么

标题里的“加载检查点”需要区分两种含义。如果指的是模型权重文件(safetensors 目录),那么 vLLM 每次启动都会重新加载,OOM 重启后自然会读一遍,不需要额外写“加载检查点”的代码。如果指的是训练时保存的 .pt 或 .bin checkpoint,vLLM 不能直接接收,需要先转换成 Hugging Face 的 safetensors 格式放到 `--model` 指向的目录。

vLLM离线部署Qwen2.5模型时OOM后自动重启并加载检查点的方法

如果指的是运行时状态,比如未处理完的请求、已经构建的 KV cache,那么这些数据只存在显存和内存里,进程被杀后全部丢失,重启无法恢复。离线部署时可以在上层把任务拆成小批次,让每个请求独立执行,失败后重跑对应批次即可。这一点需要在方案设计阶段就明确,否则会误以为重启后断点续跑。

重启后验证权重被正确加载

重启后要确认系统加载的是目标权重,而不是旧目录或错误路径。查看服务日志:

journalctl -u vllm-qwen `--since` "10 minutes ago" | grep -i -E "loaded|safetensors|checkpoint"
ls -lh /models/Qwen2.5-7B-Instruct/

再用一个最小请求验证推理能力:

vLLM离线部署Qwen2.5模型时OOM后自动重启并加载检查点的方法
curl http://127.0.0.1:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"/models/Qwen2.5-7B-Instruct","messages":[{"role":"user","content":"\u4f60\u597d"}],"max_tokens":64}'

请求能返回正常文本,说明权重加载成功且服务已经可用;如果请求时报显存不足,则说明重启后仍然 OOM,需要继续调低显存占用。

重启仍然 OOM 的常见原因

systemd 能把服务拉起来,但服务每几十秒又被杀掉,这种情况需要逐个排查:

  1. `--gpu-memory-utilization` 设得过高。显存里还要留出 CUDA context 和碎片空间,建议先调到 0.7 左右,再根据日志逐步上调。
  2. `--max-model-len` 设置过大。直接填模型原始的长上下文长度会让 KV cache 预分配超过显存,离线场景应根据实际输入长度调小。
  3. 主机内存不足。可以用 free -m 查看系统剩余内存,确认是不是 paged attention 或 tokenizer 等进程内部对象占用了过多 RAM。必要时在 systemd 单元里用 MemoryMax 限制内存占用。