集群运营与故障处理——让万卡集群稳定运行 / Operations and Failure Recovery for Large GPU Clusters
📅 创建时间:2026-06-03 🏷️ 标签:#AI-Infra #集群侧 #运营 #监控 #故障 #DCGM #SRE 📚 前置知识:[[00-cluster-infra-overview]](集群全景)[[01-gpu-cluster-hardware]](硬件架构)[[03-gpu-virtualization]](虚拟化) 📚 相关知识:[[05-multi-tenant-management]](多租户)[[../training-infra/09-training-engineering]](训练工程)
场景:凌晨 3 点的告警和随之而来的 72 小时战斗
┌─────────────────────────────────────────────────────────────┐
│ │
│ 凌晨 3:00,集群告警: │
│ │
│ [CRITICAL] Pod-7 GPU 节点 NVLink 降级 │
│ [CRITICAL] 2 台 H100 服务器 PCIe 链路错误 │
│ [WARNING] 作业 #20245 MFU 持续低于 40%(应该 > 55%) │
│ │
│ 值班 SRE 的第一反应: │
│ → 作业 #20245 是 405B 模型预训练,跑了 48 小时 │
│ → Checkpoint 最新保存是 12 小时前 │
│ → 如果这 2 台机器彻底故障,需要重跑 12 小时 = 浪费百万 │
│ │
│ 决策: │
│ → 立即隔离故障节点(cordon + drain) │
│ → 评估:剩余 1022 张卡能否继续跑? │
│ → 作业 #20245 是 TP=8 → 需要每节点 8 卡 │
│ → 1022 ÷ 8 = 127.75,凑不够完整的 8 的倍数 │
│ → 必须杀掉重跑?还是等维修? │
│ │
│ 这就是集群运营的日常: │
│ 技术问题只是开始,真正的挑战是快速决策和损失最小化。 │
│ │
└─────────────────────────────────────────────────────────────┘第1节:监控体系
GPU 监控架构
┌─────────────────────────────────────────────────────────────┐
│ GPU 集群监控体系架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 数据采集层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ DCGM(Data Center GPU Manager) │ │
│ │ → NVIDIA 官方监控工具,每秒采集 GPU 指标 │ │
│ │ → DCGM Exporter → 暴露 Prometheus 格式指标 │ │
│ │ → 支持:GPU 利用率 / 显存 / 温度 / ECC / NVLink │ │
│ │ │ │
│ │ nvidia-smi(基础采集) │ │
│ │ → 简单,但精度和功能不如 DCGM │ │
│ │ │ │
│ │ 节点级 exporters(Node Exporter) │ │
│ │ → CPU / 内存 / 磁盘 / 网络 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ 数据存储层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Prometheus(时序数据库) │ │
│ │ → Pull 模式采集,自动发现(K8s ServiceMonitor) │ │
│ │ → 存储 30 天数据(可配置) │ │
│ │ → 支持 PromQL 查询 │ │
│ │ │ │
│ │ 可选:Thanos / Cortex(长期存储 + 高可用) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ 可视化 & 告警层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Grafana(仪表盘) │ │
│ │ → 集群总览 / 作业级详情 / 节点健康 │ │
│ │ │ │
│ │ AlertManager(告警) │ │
│ │ → 邮件 / Slack / PagerDuty / Webhook │ │
│ │ → 告警分级:Critical / Warning / Info │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘DCGM 核心指标
bash
# DCGM 监控指标详解
# 1. GPU 利用率
DCGM_FI_DEV_GPU_UTIL # 算力利用率(%)
DCGM_FI_DEV_SM_CLOCK # SM 时钟频率(MHz)
DCGM_FI_DEV_MEMORY_CLOCK # 显存时钟频率(MHz)
# 2. 显存
DCGM_FI_DEV_FB_USED # 显存使用量(MB)
DCGM_FI_DEV_FB_FREE # 显存剩余(MB)
DCGM_FI_DEV_FB_USED_PCT # 显存使用率(%)
# 3. 温度 & 功耗
DCGM_FI_DEV_TEMP_GPU # GPU 温度(℃)
DCGM_FI_DEV_POWER_USAGE # 功耗(W)
DCGM_FI_DEV_POWER_LIMIT # 功耗上限(W)
# 4. ECC 错误(内存可靠性)
DCGM_FI_DEV_ECC_SBE_VOL_TOTAL # 单比特错误(可纠正)
DCGM_FI_DEV_ECC_DBE_VOL_TOTAL # 双比特错误(不可纠正)
# 5. NVLink 状态
DCGM_FI_DEV_NVLINK_TX_BYTES # NVLink 发送字节数
DCGM_FI_DEV_NVLINK_RX_BYTES # NVLink 接收字节数
DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_L0 # NVLink CRC 错误
# 6. PCIe
DCGM_FI_DEV_PCIE_TX_THROUGHPUT # PCIe 发送带宽
DCGM_FI_DEV_PCIE_RX_THROUGHPUT # PCIe 接收带宽
DCGM_FI_DEV_PCIE_REPLAY_COUNTER # PCIe 重传次数
# 7. XID 错误(NVIDIA 错误码)
DCGM_FI_DEV_XID_ERRORS # XID 错误(48=ECC错误,79=NVLINK错误)告警阈值配置
yaml
# Prometheus alerting rules for GPU cluster
groups:
- name: gpu-cluster-alerts
interval: 30s
rules:
# CRITICAL: GPU 故障
- alert: GPUGone
expr: DCGM_FI_DEV_GPU_TEMPERATURE == 0
for: 1m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu }} on {{ $labels.instance }} is gone"
description: "GPU temperature dropped to 0, likely unreachable"
# CRITICAL: ECC 双比特错误(不可纠正)
- alert: ECCDoubleBitError
expr: delta(DCGM_FI_DEV_ECC_DBE_VOL_TOTAL[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "Uncorrectable ECC error on {{ $labels.instance }} GPU {{ $labels.gpu }}"
description: "Double-bit ECC error detected. GPU may become unstable."
# WARNING: GPU 过热
- alert: GPUTemperatureHigh
expr: DCGM_FI_DEV_TEMP_GPU > 83
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} temperature {{ $labels.value }}C on {{ $labels.instance }}"
description: "GPU temperature exceeds 83C. Throttling may occur above 83C."
# WARNING: GPU 利用率异常低
- alert: LowGPUUtilization
expr: DCGM_FI_DEV_GPU_UTIL < 10
for: 15m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} utilization {{ $labels.value }}% on {{ $labels.instance }}"
description: "GPU running but idle for 15 minutes. Possible NCCL hang or job failure."
# WARNING: 显存即将耗尽
- alert: GPUOutOfMemory
expr: (DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_FREE) > 0.95
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu }} memory {{ $labels.value }}% on {{ $labels.instance }}"
description: "GPU memory usage above 95%. OOM risk."
# WARNING: NVLink 降级
- alert: NVLinkDegraded
expr: DCGM_FI_DEV_NVLINK_CRC_FLIT_ERROR_COUNT_L0 > 100
for: 5m
labels:
severity: warning
annotations:
summary: "NVLink errors on {{ $labels.instance }} GPU {{ $labels.gpu }}"
description: "NVLink CRC errors exceeding threshold. May indicate hardware issue."
# WARNING: PCIe 重传过多
- alert: PCIeErrors
expr: rate(DCGM_FI_DEV_PCIE_REPLAY_COUNTER[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "PCIe replay errors on {{ $labels.instance }} GPU {{ $labels.gpu }}"
description: "PCIe replay rate elevated. May indicate cable/connector issue."
# INFO: 节点离线
- alert: GPUNodeDown
expr: up{job="nvidia-dcgm-exporter"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "GPU exporter down on {{ $labels.instance }}"
description: "DCGM exporter unreachable. Node may be down or unreachable."第2节:GPU 健康检查
GPU 健康状态评估矩阵
┌─────────────────────────────────────────────────────────────┐
│ GPU 健康状态评估矩阵 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 检查维度: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 维度 │ 检查方法 │ 阈值 │ │
│ │ ────────────┼────────────────────┼──────────────────│ │
│ │ ECC │ nvidia-smi -q -e │ DBE > 0 = 警告 │ │
│ │ 温度 │ nvidia-smi -q -t │ > 83℃ = 降频 │ │
│ │ 功耗 │ nvidia-smi -q -p │ > 功耗上限 = 降频│ │
│ │ NVLink │ nvidia-smi nvlink │ CRC 错误 > 阈值 │ │
│ │ 显存带宽 │ DCGM Bandwidth Test│ < 理论值 90% = 异常│ │
│ │ SM 频率 │ nvidia-smi -q -c │ 持续低于额定 = 降频│ │
│ │ XID 错误 │ nvidia-smi -q -x │ 任何未处理错误 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ GPU 健康状态分级: │
│ 🟢 Healthy:所有指标正常 │
│ 🟡 Degraded:某项指标异常但可继续运行 │
│ 🔴 Unhealthy:需要立即隔离维修 │
│ │
└─────────────────────────────────────────────────────────────┘GPU 健康检查脚本
bash
#!/bin/bash
# gpu_health_check.sh - 节点 GPU 健康检查脚本
NORMAL=0
DEGRADED=1
UNHEALTHY=2
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
for gpu_id in $(nvidia-smi --query-gpu=index --format=csv,noheader); do
status=$NORMAL
issues=""
# 1. 检查 ECC 错误
dbe=$(nvidia-smi -i $gpu_id --query-gpu=ecc.dbe.current --format=csv,noheader)
if [ "$dbe" -gt 0 ]; then
status=$UNHEALTHY
issues="$issues [CRITICAL] ECC Double-Bit Error: $dbe"
fi
# 2. 检查温度
temp=$(nvidia-smi -i $gpu_id --query-gpu=temperature.gpu --format=csv,noheader)
if [ "$temp" -gt 90 ]; then
status=$UNHEALTHY
issues="$issues [CRITICAL] Temperature: ${temp}C"
elif [ "$temp" -gt 83 ]; then
[ $status -lt $DEGRADED ] && status=$DEGRADED
issues="$issues [WARNING] Temperature: ${temp}C (throttling)"
fi
# 3. 检查 XID 错误
xid=$(nvidia-smi -i $gpu_id --query-gpu=xid.errors --format=csv,noheader)
if [ "$xid" != "N/A" ] && [ -n "$xid" ]; then
status=$UNHEALTHY
issues="$issues [CRITICAL] XID Error: $xid"
fi
# 4. 检查 NVLink 状态
nvlinks=$(nvidia-smi nvlink -e $gpu_id -s 2>/dev/null | grep "Error" | wc -l)
if [ "$nvlinks" -gt 0 ]; then
[ $status -lt $DEGRADED ] && status=$DEGRADED
issues="$issues [WARNING] NVLink CRC errors: $nvlinks"
fi
# 输出结果
if [ $status -eq $NORMAL ]; then
echo -e "GPU $gpu_id: ${GREEN}HEALTHY${NC}"
elif [ $status -eq $DEGRADED ]; then
echo -e "GPU $gpu_id: ${YELLOW}DEGRADED${NC}$issues"
else
echo -e "GPU $gpu_id: ${RED}UNHEALTHY${NC}$issues"
fi
done第3节:节点故障处理
故障分类与响应
┌─────────────────────────────────────────────────────────────┐
│ GPU 节点故障分类与响应流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 类型 1:可恢复故障(Soft Failure) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 症状:单比特 ECC 错误、临时温度过高、NVLink 偶发错误 │ │
│ │ 影响:当前作业可能受影响,但 GPU 仍可用 │ │
│ │ 响应: │ │
│ │ 1. 记录日志(DCGM metrics) │ │
│ │ 2. 继续监控(频率提高到每分钟) │ │
│ │ 3. 如果错误持续增加,触发 Unhealthy │ │
│ │ 4. 暂不隔离,等待进一步确认 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 类型 2:硬件故障(Hard Failure) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 症状:双比特 ECC 错误、NVLink 持续 CRC 错误、XID 79 │ │
│ │ 影响:GPU 完全不可用,必须隔离 │ │
│ │ 响应: │ │
│ │ 1. 立即标记节点为不可调度(cordon/drain) │ │
│ │ 2. 通知 SRE + 记录故障工单 │ │
│ │ 3. 评估对运行中作业的影响 │ │
│ │ 4. 安排硬件维修 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 类型 3:网络故障(影响整个 Pod) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 症状:IB 端口 Down、交换机端口 Down │ │
│ │ 影响:整个 Pod 的 GPU 无法跨节点通信 │ │
│ │ 响应: │ │
│ │ 1. 确认物理链路(光纤、模块) │ │
│ │ 2. 检查交换机状态 │ │
│ │ 3. 如果是交换机故障:联系网络 SRE │ │
│ │ 4. 作业需要 Checkpoint 后重启(可能损失部分训练) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘Kubernetes 节点隔离
bash
# Kubernetes 节点故障处理
# 1. 标记节点为不可调度(阻止新作业调度到该节点)
kubectl cordon <node-name>
# 输出:node/<node-name> cordoned
# 2. 驱逐节点上的 Pod(可选,等待作业自然结束)
kubectl drain <node-name> \
--ignore-daemonsets \
--delete-emptydir-data \
--force \
--timeout=300s
# 3. 查看节点状态
kubectl get nodes <node-name>
# 输出中 CONDITIONS 应显示:
# MemoryPressure=False | KubeletNotReady (原因:节点不响应)
# 4. 故障节点恢复后,重新加入集群
kubectl uncordon <node-name>
# 5. 批量处理多个节点(故障后)
kubectl cordon $(kubectl get nodes -l "node-group=gpu" --no-headers | grep "True" | awk '{print $1}')Slurm 节点管理
bash
# Slurm 节点故障处理
# 1. 将节点标记为 DOWN
sudo scontrol update NodeName=node007 State=DOWN Reason="NVLink failure"
# 2. 查看节点状态
sinfo -N -l
# 3. 查看 DOWN 节点的作业
squeue --nodelist=node007
# 4. 将故障节点的作业重新调度(--nohold 立即重新排队)
# 方式1:删除后重新排队
scancel --nodelist=node007 --signal=SIGUSR1 # 触发作业 Checkpoint
# 等待 Checkpoint 完成
scancel --nodelist=node007 --jobid=ALL
# 作业会重新入队
# 5. 节点维修完成后,恢复
sudo scontrol update NodeName=node007 State=RESUME
# 6. 查看故障历史
sacct --nodecount=node007 --starttime=2026-06-01第4节:集群升级
滚动升级策略
┌─────────────────────────────────────────────────────────────┐
│ GPU 集群滚动升级策略 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 升级类型与风险: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 类型 │ 风险 │ 停机时间 │ 回滚方案 │ │
│ │ ────────────────┼──────────┼───────────┼─────────────│ │
│ │ CUDA 版本 │ 高 │ 全部重跑 │ 镜像回滚 │ │
│ │ 驱动版本 │ 中 │ 部分重跑 │ 驱动回滚 │ │
│ │ 框架版本 │ 低 │ 无(热更)│ 镜像回滚 │ │
│ │ NCCL 版本 │ 低 │ 作业重启 │ NCCL 降级 │ │
│ │ 交换机固件 │ 高 │ 全集群 │ 固件回滚 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 滚动升级原则(以驱动升级为例): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. 维护窗口选择:选择作业最少的时间(通常是凌晨) │ │
│ │ 2. 分批处理:每次升级 10% 节点,观察 30 分钟 │ │
│ │ 3. 作业影响:正在运行的作业需要 Checkpoint 后重启 │ │
│ │ 4. 回滚准备:保留上一版本驱动,保留旧版本 NCCL │ │
│ │ 5. 灰度验证:先升级 2 台节点,跑完整测试套件 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 升级顺序: │
│ → 驱动升级(需要重启) │
│ → NCCL 升级(作业重启即可) │
│ → 框架升级(可热更) │
│ → Kubernetes 组件升级(Pod 滚动重启) │
│ │
└─────────────────────────────────────────────────────────────┘升级检查清单
┌─────────────────────────────────────────────────────────────┐
│ 集群升级执行检查清单 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 升级前(1 周前准备): │
│ □ 通知所有用户升级时间和影响范围 │
│ □ 确认回滚方案和回滚步骤已文档化 │
│ □ 准备回滚镜像/驱动/NCCL 版本 │
│ □ 执行完整 Checkpoint(所有重要作业) │
│ □ 选择升级窗口(低利用率时段) │
│ □ 升级监控:Grafana 告警静默(避免升级期间告警风暴) │
│ │
│ 升级中(每次节点升级): │
│ □ 记录升级前 GPU 状态(nvidia-smi -q > upgrade_log) │
│ □ 隔离节点(cordon/drain) │
│ □ 安装新驱动/CUDA/NCCL │
│ □ 重启节点 │
│ □ 验证节点健康:gpu_health_check.sh │
│ □ 验证驱动版本:nvidia-smi │
│ □ 验证 NCCL 版本:nccl-test │
│ □ 恢复调度(uncordon) │
│ □ 等待 30 分钟观察,无异常再继续下一批 │
│ │
│ 升级后(1 天后): │
│ □ 恢复监控告警 │
│ □ 确认所有节点健康 │
│ □ 确认运行中的作业正常 │
│ □ 通知用户升级完成 │
│ □ 记录升级日志和任何遇到的问题 │
│ │
└─────────────────────────────────────────────────────────────┘第5节:容量规划
集群容量规划模型
┌─────────────────────────────────────────────────────────────┐
│ GPU 集群容量规划 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 容量规划的核心问题: │
│ "我们需要在 X 时间内完成 Y 模型的训练, │
│ 需要多少 GPU?当前集群够用吗?" │
│ │
│ 估算公式: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 所需 GPU 数量 = 模型训练时间目标 │ │
│ │ ─────────────────────────── │ │
│ │ (单卡 MFU × 有效训练时间) │ │
│ │ │ │
│ │ 其中: │ │
│ │ → 单卡 MFU:H100 ~60%(考虑通信开销) │ │
│ │ → 有效训练时间:每天 24 小时 × 利用率 │ │
│ │ → 实际经验值:单卡约 0.5-0.6 TFLOPS(训练场景) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 集群利用率目标设定: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 指标 │ 目标值 │ 说明 │ │
│ │ ──────────────────┼───────────┼──────────────────│ │
│ │ 整体 GPU 利用率 │ > 85% │ 包含所有作业 │ │
│ │ 训练作业平均 MFU │ > 50% │ 排除 IO 等待 │ │
│ │ 集群平均等待时间 │ < 4 小时 │ 新作业入队到启动 │ │
│ │ 作业失败率 │ < 5% │ 因故障导致的失败 │ │
│ │ Checkpoint 频率 │ 每 2 小时│ 减少故障损失 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 扩容信号: │
│ → 平均等待时间 > 8 小时:需要扩容 │
│ → 队列积压 > 3 倍日均需求:需要扩容 │
│ → 集群利用率持续 > 95%:接近容量上限 │
│ │
└─────────────────────────────────────────────────────────────┘升华:运营的价值在于预防
┌─────────────────────────────────────────────────────────────┐
│ 集群运营的工程哲学 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 监控是一切的基础: │
│ → 没有监控的集群 = 盲人开飞机 │
│ → 告警太多 = 被淹没(狼来了效应) │
│ → 告警太少 = 问题发生了才知道 │
│ → 阈值设置是艺术:平衡噪音和漏报 │
│ │
│ 2. Checkpoint 是集群的"存档点": │
│ → 每隔 2 小时保存一次 = 最多损失 2 小时训练 │
│ → 没有 Checkpoint = 故障 = 作业归零 │
│ → 这是集群运营和训练工程的交界 │
│ │
│ 3. 升级是计划内的故障: │
│ → 升级必然导致作业重启 │
│ → 做好准备(Checkpoint)后,升级只是可控的维护 │
│ → 不升级的"稳定"是虚假的稳定(漏洞和 bug 在积累) │
│ │
│ 4. 容量规划要走在需求之前: │
│ → GPU 采购周期 3-6 个月 │
│ → 今天的需求是半年前的规划结果 │
│ → 现在开始规划 = 一年后的容量 │
│ │
│ 一句话总结: │
│ 集群运营的目标是把"故障"变成"维护", │
│ 把"紧急"变成"计划内"。 │
│ │
└─────────────────────────────────────────────────────────────┘"AI 可查 vs 必须理解"清单
AI 可查:
✅ DCGM Exporter 的 Prometheus metrics 完整列表
✅ Grafana Dashboard 的 JSON 模板
✅ 特定型号 GPU 服务器的硬件维修手册
✅ 交换机固件升级的具体步骤
必须理解:
🔴 GPU 健康检查的维度(ECC / 温度 / NVLink / XID)
🔴 Kubernetes/Slurm 的节点隔离操作(cordon/drain vs scontrol update DOWN)
🔴 GPU 故障分类与响应流程(Soft / Hard / Network)
🔴 滚动升级的灰度原则和回滚方案
🔴 集群容量规划的核心指标(利用率 / 等待时间 / 失败率)
🔴 为什么"监控是一切的基础"——告警阈值设置的平衡艺术学习状态:🟡 开始学习