GPU 在虚拟化直通(Passthrough)环境下运行时,偶尔会出现驱动丢失、NVML 报错等问题,导致容器内无法使用 GPU。本文介绍一个轻量级的自动巡检自愈方案,从检测到修复再到飞书通知,全自动闭环。
问题背景 链接到标题
在 PVE 虚拟机中通过 PCIe Passthrough 直通 NVIDIA GPU 时,容器内偶尔会出现:
Failed to initialize NVML: Unknown Error
重启容器后可短暂恢复,但一段时间后再次失效。这类问题在虚拟化环境中难以彻底根治(涉及 ASPM 电源管理、驱动状态等问题),需要一个主动巡检 + 自动修复的兜底机制。
架构总览 链接到标题
graph LR
GC[gpu-checker
每 60s 巡检] -->|docker exec nvidia-smi| OS[GPU 应用容器] GC -->|异常| A[自动修复] A -->|1.告警| AT[alert-transformer] A -->|2.重启| OS A -->|3.重检| B{恢复?} B -->|是| AT B -->|否| C[人工介入] AT -->|hooks| OC[OpenClaw] OC -->|飞书机器人| FS[飞书通知]
每 60s 巡检] -->|docker exec nvidia-smi| OS[GPU 应用容器] GC -->|异常| A[自动修复] A -->|1.告警| AT[alert-transformer] A -->|2.重启| OS A -->|3.重检| B{恢复?} B -->|是| AT B -->|否| C[人工介入] AT -->|hooks| OC[OpenClaw] OC -->|飞书机器人| FS[飞书通知]
部署 gpu-checker 链接到标题
巡检容器是一个独立的 Docker 容器,使用 docker:cli 镜像,通过挂载 docker.sock 来操作宿主机上的其他容器:
# /opt/gpu-checker/docker-compose.yaml
services:
gpu-checker:
image: m.daocloud.io/docker.io/docker:cli
container_name: gpu-checker
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./check.sh:/check.sh:ro
command: sh /check.sh
sudo mkdir -p /opt/gpu-checker
cd /opt/gpu-checker && docker compose up -d
巡检与自愈逻辑 链接到标题
核心脚本 check.sh 实现了一个完整的自动巡检 - 告警 - 修复 - 通知闭环:
主循环 链接到标题
每 60 秒:
├─ 冷却检查(距上次操作 < 300s 则跳过)
│
└─ 执行 docker exec nvidia-smi
├─ 正常 → 静默等待下一轮
│
└─ 异常(报错或非零退出码)
├─ 1. 发送 warning 告警 → alert-transformer
├─ 2. docker compose restart 目标容器
├─ 3. 等待 15s 让 GPU 初始化
├─ 4. 重检 nvidia-smi
│ ├─ 恢复 → 发送 resolved 通知
│ └─ 仍失败 → 发送 critical 告警
└─ 5. 设置 300s 冷却
三态告警 链接到标题
| 阶段 | 告警名 | 严重度 | 作用 |
|---|---|---|---|
| 发现异常 | OllamaGPUCheckFailed |
warning | 通知管理员正在自愈 |
| 自愈成功 | OllamaGPUCheckFailed → resolved |
info | 确认已恢复 |
| 自愈失败 | OllamaGPUAutoRepairFailed |
critical | 需人工介入 |
冷却机制 链接到标题
每次触发修复后,设置 300 秒冷却窗口,避免短时间内反复重启导致告警风暴和服务抖动:
COOLDOWN_SECONDS=300
cooldown_file="/tmp/gpu-checker-cooldown"
check_cooldown() {
if [ -f "$cooldown_file" ]; then
last_run=$(cat "$cooldown_file")
now=$(date +%s)
elapsed=$((now - last_run))
if [ $elapsed -lt $COOLDOWN_SECONDS ]; then
echo "冷却中,跳过检查(剩余 $((COOLDOWN_SECONDS - elapsed))s)"
return 1
fi
fi
return 0
}
set_cooldown() {
date +%s > "$cooldown_file"
}
告警流程 链接到标题
检测到 GPU 异常后,按以下流程处理:
1. POST https://alert-transformer/alert
└─ Body: {"alertname":"OllamaGPUCheckFailed","severity":"warning",...}
2. docker compose restart ollama-service
3. sleep 15
4. 重检 GPU
├─ 成功 → POST https://alert-transformer/alert (resolved)
└─ 失败 → POST https://alert-transformer/alert (critical)
其中告警 POST 的关键参数:
| 参数 | 值 | 说明 |
|---|---|---|
| alertname | OllamaGPUCheckFailed |
告警名称 |
| severity | warning / critical |
严重度 |
| status | firing / resolved |
状态 |
| instance | monkey |
主机标识 |
| description | 具体错误信息 | 用于排查 |
check.sh 完整代码 链接到标题
#!/bin/sh
set -e
TARGET_CONTAINER="ollama-service"
CHECK_INTERVAL=60
COOLDOWN_SECONDS=300
ALERT_ENDPOINT="http://alert-transformer:9091/alert"
cooldown_file="/tmp/gpu-checker-cooldown"
send_alert() {
local alertname="$1"
local severity="$2"
local status="$3"
local description="$4"
curl -s -X POST "$ALERT_ENDPOINT" \
-H "Content-Type: application/json" \
-d "{
\"alertname\": \"$alertname\",
\"severity\": \"$severity\",
\"status\": \"$status\",
\"instance\": \"monkey\",
\"description\": \"$description\"
}"
}
check_cooldown() {
if [ -f "$cooldown_file" ]; then
last_run=$(cat "$cooldown_file")
now=$(date +%s)
elapsed=$((now - last_run))
if [ $elapsed -lt $COOLDOWN_SECONDS ]; then
echo "冷却中,跳过检查(剩余 $((COOLDOWN_SECONDS - elapsed))s)"
return 1
fi
fi
return 0
}
set_cooldown() {
date +%s > "$cooldown_file"
}
check_gpu() {
docker exec "$TARGET_CONTAINER" nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader 2>&1
}
main() {
echo "GPU 巡检启动,监控目标: $TARGET_CONTAINER"
while true; do
if check_cooldown; then
result=$(check_gpu)
exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "检测到 GPU 异常: $result"
send_alert "OllamaGPUCheckFailed" "warning" "firing" "$result"
echo "尝试重启容器..."
docker compose -f /opt/ollama-service/docker-compose.yaml restart
sleep 15
if check_cooldown; then
result_after=$(check_gpu)
if [ $? -eq 0 ]; then
echo "GPU 已恢复"
send_alert "OllamaGPUCheckFailed" "info" "resolved" "GPU 已自动修复"
else
echo "GPU 仍异常,发送 critical 告警"
send_alert "OllamaGPUAutoRepairFailed" "critical" "firing" "$result_after"
fi
fi
set_cooldown
fi
fi
sleep $CHECK_INTERVAL
done
}
main
总结 链接到标题
通过 gpu-checker 容器,我们将 GPU 监控从被动响应转为主动巡检 + 自动修复:
- 每 60 秒自动检测 GPU 状态
- 异常即告警:第一时间通知管理员
- 自动修复:无需人工介入,重启即可恢复
- 冷却机制:避免告警风暴和服务抖动
- 飞书通知:通过 OpenClaw 实现告警闭环
这套方案在 monkey 机器上运行稳定,有效降低了 GPU 掉卡带来的影响。