跳转至

万卡集群运维:容错、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 小时就要故障恢复一次。运维能力决定训练效率。


参考文献


上级 · C. 分布式训练基础设施