万卡集群运维:容错、Checkpoint、健康检查¶
更新日期:2026-04-15
一、万卡训练的运维挑战¶
一个统计:在 2000 GPU 集群中,单 GPU MTBF ≈ 1000 小时。按概率估算,整个集群平均每 30 分钟就有一张 GPU 出问题。万卡训练必须假设故障是常态而非异常。
| 故障类型 | 频率 | 平均恢复时间 | 应对策略 |
|---|---|---|---|
| 单 GPU 失效 | ~30 min/次(2K 集群) | 5-30 min | 自动隔离 + 弹性恢复 |
| 节点失效 | ~小时级 | 30-60 min | 备用节点替换 |
| 网络抖动 | ~天级 | 数秒 | NCCL retry + 容错 |
| 存储故障 | ~周级 | 小时级 | 多副本 checkpoint |
| 电源 / 冷却 | ~月级 | 数小时 | 跨可用区冗余 |
| 软件 bug | 不定 | 数小时-数天 | rolling release + canary |
二、Checkpoint 策略¶
2.1 频率权衡¶
过于频繁: - 优点: 故障后丢失少 - 缺点: 训练速度受影响 (每次 checkpoint 要停几分钟)
过于稀疏: - 优点: 训练速度不受影响 - 缺点: 故障后可能丢失数小时工作
黄金平衡: - 每 30 分钟到 1 小时一个 checkpoint - 每 8-24 小时一个永久 checkpoint (不覆盖) - 实时 checkpoint 用于"最坏情况"恢复
2.2 Checkpoint 类型¶
2.3 异步 Checkpoint¶
# 同步 checkpoint (坏做法):
# - 所有 GPU 停止训练
# - 全部保存到磁盘 (几分钟)
# - 恢复训练
# 代价: 每次停 5-10 分钟
# 异步 checkpoint (好做法):
def async_checkpoint():
# Step 1: 快速 dump 到 CPU/NVMe (秒级)
cpu_state = copy_to_cpu(gpu_state) # ~30 秒
# Step 2: 训练继续!不等待磁盘写入
# Step 3: 后台线程将 CPU state 写入磁盘
threading.Thread(target=lambda: save_to_disk(cpu_state)).start()
# 代价: 只停约 30 秒
# 风险: 如果 checkpoint 未完成时再故障, 可能丢失这个版本
2.4 分布式 Checkpoint¶
# 每个 rank 只保存自己的部分
# 恢复时并行读取, 快
# PyTorch DCP (Distributed Checkpoint)
import torch.distributed.checkpoint as dcp
# 保存
state = {'model': model.state_dict(), 'optimizer': opt.state_dict()}
dcp.save(state_dict=state, checkpoint_id='/path/checkpoint_10000')
# 加载 (即使并行度变了也能加载)
state = {...} # placeholder
dcp.load(state_dict=state, checkpoint_id='/path/checkpoint_10000')
三、故障检测与自动恢复¶
3.1 健康检查¶
class ClusterHealthMonitor:
def check_all_gpus(self):
issues = []
for rank in range(world_size):
# 1. GPU 可达性
if not ping_gpu(rank):
issues.append(f"Rank {rank} unreachable")
# 2. GPU 内存
mem_free = get_gpu_free_mem(rank)
if mem_free < 10 * GB:
issues.append(f"Rank {rank} low memory: {mem_free}GB")
# 3. GPU 温度
temp = get_gpu_temp(rank)
if temp > 85:
issues.append(f"Rank {rank} overheating: {temp}°C")
# 4. ECC 错误计数
ecc_errs = get_ecc_errors(rank)
if ecc_errs > ECC_THRESHOLD:
issues.append(f"Rank {rank} ECC errors: {ecc_errs}")
# 5. NVLink 健康
nvlink_errors = get_nvlink_errors(rank)
if nvlink_errors > 0:
issues.append(f"Rank {rank} NVLink errors: {nvlink_errors}")
# 6. 网络检查
for pair in ALL_NODE_PAIRS:
bw = test_bandwidth(pair)
if bw < EXPECTED_BW * 0.8:
issues.append(f"Low bandwidth between {pair}: {bw}")
return issues
3.2 慢节点 (Straggler) 检测¶
万卡训练中,单个慢 GPU 拖累整个集群(因为同步训练需要等最慢的)。
# 监控每 rank 的迭代时间
def detect_stragglers(iter_times_per_rank):
median = statistics.median(iter_times_per_rank)
stragglers = []
for rank, t in enumerate(iter_times_per_rank):
if t > median * 1.5:
stragglers.append(rank)
return stragglers
# 处理慢节点:
# 1. 告警人工介入
# 2. 从训练中移除 (减少一个 DP rank, 重组拓扑)
# 3. 强制迁移到备用节点
3.3 自动重启¶
# 训练脚本的容错包装
while True:
try:
training_process = subprocess.Popen([
'torchrun', '--nnodes', str(n_nodes),
'train.py', '--resume-from', latest_checkpoint
])
training_process.wait()
if training_process.returncode == 0:
break # 正常完成
else:
# 故障
logger.error(f"Training crashed with code {training_process.returncode}")
# 1. 分析日志找出故障节点
bad_nodes = analyze_crash_log()
# 2. 隔离故障节点
remove_from_cluster(bad_nodes)
# 3. 找替代节点
new_nodes = provision_spare_nodes(len(bad_nodes))
# 4. 更新节点列表, 重启
update_node_list(new_nodes)
latest_checkpoint = find_latest_valid_checkpoint()
except Exception as e:
logger.error(f"Outer error: {e}")
time.sleep(60) # 冷却
四、弹性训练 (Elastic Training)¶
PyTorch Elastic (torchrun) 提供了基本的弹性能力,但 Megatron 的 TP+PP 需要所有 rank 存在,弹性能力有限。
五、NCCL 调试¶
# NCCL 超时是最常见的故障之一
# 环境变量帮助 debug
env_vars = {
'NCCL_DEBUG': 'INFO', # 详细日志
'NCCL_DEBUG_SUBSYS': 'ALL', # 所有子系统
'NCCL_TIMEOUT': '1800', # 30 min 超时
'NCCL_ASYNC_ERROR_HANDLING': '1', # 异步错误处理
'TORCH_NCCL_BLOCKING_WAIT': '0', # 非阻塞等待
'NCCL_IB_HCA': '=mlx5', # 限定 IB 卡
'NCCL_SOCKET_IFNAME': 'eth0', # 指定以太网卡
'NCCL_TOPO_DUMP_FILE': '/tmp/topo.xml', # 保存拓扑
}
# 诊断步骤:
# 1. nccl-tests 验证基础性能
# 2. NCCL_DEBUG=INFO 看具体算法选择
# 3. 对比理论 vs 实际带宽
# 4. 单独测试故障链路
六、成本监控¶
# 万卡训练每小时成本 = $1000+
# 每天 = $24K+
# 一个月 = $720K+
# 必须实时监控成本浪费
def monitor_cost_efficiency():
# 1. MFU 监控 (能力 utilization)
current_mfu = compute_mfu()
expected_mfu = 45 # %
if current_mfu < expected_mfu - 5:
alert(f"MFU dropped to {current_mfu}%, expected {expected_mfu}%")
# 2. GPU 利用率 (hardware utilization)
gpu_util = get_avg_gpu_utilization()
if gpu_util < 80:
alert(f"GPU utilization low: {gpu_util}%")
# 3. 故障造成的停机时间
downtime_ratio = total_downtime / total_time
if downtime_ratio > 0.05: # >5%
alert(f"High downtime: {downtime_ratio:.1%}")
七、万卡训练工具栈¶
| 工具 | 用途 |
|---|---|
| torchrun | PyTorch 启动器,基本弹性 |
| Slurm | HPC 作业调度 |
| Kubernetes + Volcano | 云原生调度 |
| Megatron + NeMo | 训练框架 |
| wandb / mlflow | 实验跟踪 |
| Prometheus + Grafana | 监控仪表板 |
| ElasticSearch + Kibana | 日志聚合 |
| NVIDIA DCGM | GPU 遥测 |
八、案例:LLaMA-3 训练事故¶
Meta 公开了 LLaMA-3 训练的故障统计 (16K H100, 54 天): 参考 LLaMA-3 Technical Report。
启示:大厂用 LLaMA-3 级规模训练时,平均每 3 小时就要故障恢复一次。运维能力决定训练效率。
参考文献¶
-
[1] ML Engineering: Training Performance 指南
-
[2] LLaMA-3 Technical Report (运维部分). 2024. 论文
-
[4] NVIDIA DCGM
-
[6] OPT-175B Training Log — 公开的训练事故日志
↑ 上级 · C. 分布式训练基础设施