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[飞书通知]

部署 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 掉卡带来的影响。