GPU 节点故障自愈:Node Problem Detector 与自动隔离实战
详解如何在 Kubernetes GPU 集群中通过 Node Problem Detector(NPD)检测 GPU 故障、上报 NodeCondition、结合 Node Auto Remediation 实现节点自动隔离与修复,涵盖 NVIDIA XID 错误、ECC 故障、驱动崩溃等常见场景。
GPU 节点故障是大规模 AI 集群最常见的运维挑战之一:显卡 ECC 错误累积导致静默数据损坏、驱动崩溃导致 Pod 挂起、GPU 温度过高触发自动降频。如果没有自动化的故障检测和隔离机制,这类问题往往要等到用户投诉才被发现,造成训练任务长时间卡死或推理结果异常。
本文介绍以 Node Problem Detector(NPD)+ Node Auto Remediation 为核心的 GPU 故障自愈方案。
GPU 故障分类
| 故障类型 | 表现 | 影响 |
|---|---|---|
| XID 错误 | nvidia-smi 报 Xid 79/94/95 等 | GPU 计算挂起,Pod 无响应 |
| ECC 不可纠正错误(UE) | 显存数据损坏 | 静默计算错误,结果不可信 |
| ECC 可纠正错误(CE)高频 | 单比特翻转 | 预示硬件劣化,需关注 |
| 驱动崩溃 | /dev/nvidia* 消失 |
所有 GPU Pod 失效 |
| GPU 温度过高 | 结温 > 83°C(A100 阈值) | 降频,吞吐下降 50%+ |
| NVLink/PCIe 链路故障 | nv_link_errors 计数增长 | 多 GPU 训练通信失效 |
架构设计
GPU 节点
├── nvidia-smi / nvml 库
├── NPD DaemonSet(监控节点日志和系统状态)
│ ├── 检测 /var/log/nvidia/ XID 错误
│ ├── 检测 ECC 错误计数
│ └── 上报 NodeCondition → Kubernetes API
└── Node Auto Remediation(NAR)
├── 监听 NodeCondition 变化
├── 触发 cordon + drain + 重启
└── 修复后自动 uncordon
安装 Node Problem Detector
helm repo add deliveryhero https://charts.deliveryhero.io/
helm repo update
helm upgrade --install node-problem-detector deliveryhero/node-problem-detector \
--namespace kube-system \
--set image.tag=v0.8.19 \
--set settings.log_monitors[0]=/config/plugin/config/kernel.json \
--set settings.custom_plugin_monitors[0]=/config/plugin/config/nvidia-gpu-monitor.json
配置 NVIDIA GPU 自定义监控插件
NPD 支持自定义监控脚本,通过检测 GPU 状态上报 NodeCondition:
// /config/plugin/config/nvidia-gpu-monitor.json
{
"plugin": "custom",
"pluginConfig": {
"invoke_interval": "30s",
"timeout": "20s",
"max_output_length": 4096,
"concurrency": 1
},
"source": "nvidia-gpu-monitor",
"conditions": [
{
"type": "NvidiaGpuHealthy",
"reason": "NvidiaGpuHasNoError",
"message": "GPU is functioning normally"
},
{
"type": "NvidiaEccUncorrectableError",
"reason": "NvidiaGpuHasNoEccUncorrectableError",
"message": "GPU has no ECC uncorrectable errors"
},
{
"type": "NvidiaDriverNotLoaded",
"reason": "NvidiaDriverIsLoaded",
"message": "Nvidia driver is loaded"
}
],
"rules": [
{
"type": "permanent",
"condition": "NvidiaDriverNotLoaded",
"reason": "NvidiaDriverNotLoaded",
"path": "/config/plugin/config/gpu-check.sh",
"args": ["driver_loaded"],
"timeout": "10s"
},
{
"type": "permanent",
"condition": "NvidiaEccUncorrectableError",
"reason": "NvidiaEccUncorrectableError",
"path": "/config/plugin/config/gpu-check.sh",
"args": ["ecc_uncorrectable"],
"timeout": "10s"
},
{
"type": "permanent",
"condition": "NvidiaGpuHealthy",
"reason": "NvidiaGpuHasXidError",
"path": "/config/plugin/config/gpu-check.sh",
"args": ["xid_error"],
"timeout": "10s"
}
]
}
GPU 检测脚本
#!/bin/bash
# /config/plugin/config/gpu-check.sh
CHECK_TYPE=$1
case $CHECK_TYPE in
driver_loaded)
# 检查驱动是否加载
if ! ls /dev/nvidia0 &>/dev/null 2>&1; then
echo "Nvidia driver not loaded: /dev/nvidia0 not found"
exit 1
fi
# 检查 nvidia-smi 是否可用
if ! nvidia-smi > /dev/null 2>&1; then
echo "nvidia-smi command failed - driver may be crashed"
exit 1
fi
exit 0
;;
ecc_uncorrectable)
# 检查所有 GPU 的 ECC 不可纠正错误
ECC_ERRORS=$(nvidia-smi --query-gpu=ecc.errors.uncorrected.aggregate.total \
--format=csv,noheader,nounits 2>/dev/null | awk '{sum+=$1} END {print sum+0}')
if [ "$ECC_ERRORS" -gt 0 ]; then
echo "Found $ECC_ERRORS ECC uncorrectable errors"
exit 1
fi
exit 0
;;
xid_error)
# 检查 dmesg 中的 XID 严重错误(Xid 79、94、95 为严重错误)
CRITICAL_XIDS=$(dmesg --since "10 minutes ago" 2>/dev/null | \
grep -E "NVRM: Xid (79|94|95)" | wc -l)
if [ "$CRITICAL_XIDS" -gt 0 ]; then
echo "Found $CRITICAL_XIDS critical Xid errors in last 10 minutes"
exit 1
fi
exit 0
;;
esac
给脚本打入 ConfigMap 并挂载到 NPD Pod 中(通过 Helm values 或手动配置)。
部署 Node Auto Remediation(NAR)
NAR 是 NVIDIA GPU Operator 和 AMD GPU Operator 均支持的自动修复组件,也可使用社区项目 medik8s/node-healthcheck-operator。
使用 medik8s Node Healthcheck Operator
# 安装 Node Healthcheck Operator
kubectl apply -f https://raw.githubusercontent.com/medik8s/node-healthcheck-operator/main/config/default/kustomization.yaml
配置 NodeHealthCheck:
apiVersion: remediation.medik8s.io/v1alpha1
kind: NodeHealthCheck
metadata:
name: gpu-node-healthcheck
spec:
selector:
matchLabels:
nvidia.com/gpu.present: "true"
# 触发修复的条件
unhealthyConditions:
- type: NvidiaEccUncorrectableError
status: "True"
duration: 30s # 持续 30 秒才触发,避免抖动
- type: NvidiaDriverNotLoaded
status: "True"
duration: 60s
- type: NvidiaGpuHealthy
status: "False"
duration: 120s
# 修复动作:使用 SelfNodeRemediation(重启节点)
remediationTemplate:
apiVersion: self-node-remediation.medik8s.io/v1alpha1
kind: SelfNodeRemediationTemplate
name: automatic-reboot-template
namespace: medik8s
---
apiVersion: self-node-remediation.medik8s.io/v1alpha1
kind: SelfNodeRemediationTemplate
metadata:
name: automatic-reboot-template
namespace: medik8s
spec:
template:
spec:
remediationStrategy: Automatic
自定义修复逻辑(轻量方案)
如果不想引入额外 Operator,可以用简单的 Kubernetes Operator 逻辑实现:
# 监控脚本:检测 NodeCondition 并自动隔离(运行在独立 Pod 或 CronJob 中)
#!/bin/bash
while true; do
# 查找所有 GPU 节点中 Condition=True 的故障节点
UNHEALTHY_NODES=$(kubectl get nodes -l nvidia.com/gpu.present=true \
-o json | jq -r '.items[] |
select(
.status.conditions[] |
select(.type == "NvidiaEccUncorrectableError" and .status == "True")
) | .metadata.name')
for NODE in $UNHEALTHY_NODES; do
echo "[$(date)] 检测到故障节点: $NODE,开始隔离..."
# 标记不可调度
kubectl cordon $NODE
# 驱逐 Pod(保留 DaemonSet)
kubectl drain $NODE \
--ignore-daemonsets \
--delete-emptydir-data \
--grace-period=60 \
--timeout=300s
# 打上故障标签
kubectl label node $NODE gpu.health=unhealthy \
gpu.isolated-at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --overwrite
# 发送告警(Webhook 或邮件)
curl -X POST $ALERT_WEBHOOK \
-H "Content-Type: application/json" \
-d "{\"text\": \"GPU 节点 $NODE 已自动隔离,原因: ECC 不可纠正错误\"}"
echo "[$(date)] 节点 $NODE 已隔离完成"
done
sleep 60
done
关键 XID 错误码速查
运维人员需要了解常见 XID 错误的含义与处理方式:
| XID | 含义 | 处理建议 |
|---|---|---|
| 13 | 显存 ECC 错误 | 检查 ECC 计数,关注是否增长 |
| 31 | GPU 挂起(hang) | 立即重启节点 |
| 43 | GPU 驱动状态不一致 | 重启节点,若持续联系 NVIDIA 支持 |
| 61 | 内部微控制器中断故障 | 需硬件检测 |
| 63 | 行重映射记录故障(Row Remapping) | 显存物理损坏,需更换硬件 |
| 74 | 显存 NVLink 错误 | 检查 NVLink 连接 |
| 79 | GPU 挂起,需重置 | 立即隔离并重启 |
| 94 | 容器卸载时 GPU 未响应 | 驱逐 Pod,重启节点 |
| 95 | 非预期的 SRAM ECC 错误 | 可能是硬件损坏,需评估更换 |
# 查看节点 XID 错误历史
ssh <node-ip> "dmesg | grep -E 'NVRM: Xid' | tail -50"
# 实时监控 XID 错误
ssh <node-ip> "dmesg -w | grep 'NVRM: Xid'"
Prometheus 告警规则
groups:
- name: gpu-health
interval: 30s
rules:
# ECC 不可纠正错误(严重,立即告警)
- alert: GpuEccUncorrectableError
expr: |
nvidia_ecc_errors_uncorrected_aggregate_total > 0
for: 0m
labels:
severity: critical
annotations:
summary: "节点 {{ $labels.instance }} GPU {{ $labels.gpu }} ECC 不可纠正错误"
description: "ECC 不可纠正错误数 {{ $value }},显存数据可能已损坏,需立即隔离节点"
# ECC 可纠正错误高频(预警,不立即隔离)
- alert: GpuEccCorrectableErrorHigh
expr: |
increase(nvidia_ecc_errors_corrected_aggregate_total[1h]) > 100
for: 5m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} GPU {{ $labels.gpu }} ECC 可纠正错误频繁"
description: "过去 1 小时 ECC 可纠正错误 {{ $value }} 次,建议安排硬件检测"
# GPU 温度过高
- alert: GpuTemperatureCritical
expr: |
nvidia_temperature_gpu > 83
for: 3m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} GPU {{ $labels.gpu }} 温度过高"
description: "GPU 结温 {{ $value }}°C,超过 83°C 阈值,可能触发降频"
# 节点 GPU 资源全部不可用(驱动崩溃)
- alert: GpuNodeAllDevicesUnavailable
expr: |
kube_node_status_allocatable{resource="nvidia.com/gpu"} == 0
and on(node) kube_node_labels{label_nvidia_com_gpu_present="true"}
for: 2m
labels:
severity: critical
annotations:
summary: "GPU 节点 {{ $labels.node }} 所有 GPU 资源不可分配"
description: "该节点 GPU 驱动可能已崩溃,请检查 NPD 状态并考虑重启节点"
完整运维流程
日常巡检
# 每日检查 GPU 节点健康状态
kubectl get nodes -l nvidia.com/gpu.present=true \
-o custom-columns=NAME:.metadata.name,STATUS:.status.conditions[-1].type,READY:.status.conditions[-1].status
# 查看所有 GPU 节点的 ECC 错误
for node in $(kubectl get nodes -l nvidia.com/gpu.present=true -o name | cut -d/ -f2); do
echo "=== $node ==="
kubectl debug node/$node -it --image=nvidia/cuda:12.1.0-base-ubuntu22.04 -- \
nvidia-smi --query-gpu=index,ecc.errors.uncorrected.aggregate.total --format=csv
done
# 检查 NPD 上报的节点状态
kubectl get nodes -o json | jq -r '.items[] |
select(.metadata.labels["nvidia.com/gpu.present"] == "true") |
"\(.metadata.name): \(.status.conditions[] | select(.type | startswith("Nvidia")) | "\(.type)=\(.status)")"'
故障恢复确认
# 确认驱动已重新加载
ssh <node-ip> "nvidia-smi && echo '驱动正常'"
# 确认 ECC 错误已清除(或计数不再增长)
ssh <node-ip> "nvidia-smi --query-gpu=ecc.errors.uncorrected.aggregate.total --format=csv,noheader"
# 解除隔离
kubectl uncordon <node-ip>
kubectl label node <node-ip> gpu.health=healthy --overwrite
小结
GPU 故障自愈的核心闭环:NPD 检测 → NodeCondition 上报 → NAR 自动 drain+重启 → Prometheus 告警通知运维。关键原则:ECC 不可纠正错误要立即隔离(数据损坏影响业务),ECC 可纠正错误记录趋势(预测硬件寿命),XID 79/94/95 类重置级错误立即重启节点。自动化修复配合人工确认才是成熟运维,自动隔离后一定要有人工复核再解除。
