3.4 抢占、重调度与弹性训练:让训练容忍中断


文档摘要

3.4 抢占、重调度与弹性训练:让训练容忍中断 大模型训练动辄跑数天甚至数周,期间节点故障、网络抖动、Spot 实例被回收几乎是必然事件。一个不能容忍中断的训练系统,等于「赌这周不出事」——而工程化的核心,就是把这个赌注变成确定性保障。 3.4.1 训练中断的不可避免性 在真实生产环境里,长时间训练面临的中断源远比想象中多: 中断源 | 频率 | 影响 GPU 硬件故障 | 数月一次 | 单卡失效 节点重启/维护 | 数周一次 | 全节点 Pod 驱逐 网络抖动 | 偶发 | 通信超时 Spot/抢占实例回收 | 不确定 | 训练被强杀 高优先级任务抢占 | 按策略 | 训练让位 集群升级 | 季度级 | 全集群滚动重启 检查点写入失败 | 偶发 | 状态丢失风险 💡 判读:Meta

3.4 抢占、重调度与弹性训练:让训练容忍中断

大模型训练动辄跑数天甚至数周,期间节点故障、网络抖动、Spot 实例被回收几乎是必然事件。一个不能容忍中断的训练系统,等于「赌这周不出事」——而工程化的核心,就是把这个赌注变成确定性保障。

3.4.1 训练中断的不可避免性

在真实生产环境里,长时间训练面临的中断源远比想象中多:

中断源 频率 影响
GPU 硬件故障 数月一次 单卡失效
节点重启/维护 数周一次 全节点 Pod 驱逐
网络抖动 偶发 通信超时
Spot/抢占实例回收 不确定 训练被强杀
高优先级任务抢占 按策略 训练让位
集群升级 季度级 全集群滚动重启
检查点写入失败 偶发 状态丢失风险

💡 判读:Meta 在训练 OPT-175B 时公开过数据:在 1024 张 A100 上跑两个月,经历了 100+ 次中断,平均每天 1-2 次。这不是个例,而是大模型训练的常态。任何不预设「会中断」的训练系统设计都是失败的

应对中断有三类策略,本节逐一展开:

  1. 检查点与断点续训:保存状态,中断后从断点恢复。
  2. 抢占与优先级:主动让位与抢占,按业务重要性调度。
  3. 弹性训练:训练框架容忍 worker 数量变化,动态加入/退出。

3.4.2 检查点与断点续训:训练的「存档点」

检查点(Checkpoint)是大模型训练的生命线。它定期把训练状态保存到持久存储,中断后从最近的检查点恢复。

一个完整的训练检查点至少要包含:

  • 模型权重(Model Weights):当前模型参数。
  • 优化器状态(Optimizer State):Adam 的动量、方差等。
  • 学习率调度器状态(LR Scheduler):当前学习率与步数。
  • 数据加载器状态(DataLoader):当前 epoch、batch 位置,确保恢复后数据顺序一致。
  • 随机种子(Random State):保证可复现。

检查点的工程实践要点:

实践 说明
保存频率 折中:太频繁影响吞吐,太稀疏丢失进度多。常见每 1000-5000 步一次
异步保存 检查点写入耗时大,用后台线程异步写,不阻塞训练
多副本 关键检查点写多副本(本地 + 远程),防单点丢失
校验和 保存 checksum,恢复时校验完整性
版本管理 保留最近 N 个检查点,定期清理旧的
显存到内存再到存储 先从 GPU 显存拷到 CPU 内存,再异步写存储,减少 GPU 等待

⚠️ 大模型检查点的成本:一个 70B 模型用 Adam 训练,检查点(权重 + 优化器状态)可达 1TB+。每次保存要把 1TB 写到存储,即使带宽 10GB/s 也要 100 秒。这就是为什么 ZeRO-3(第 5 章)等技术在显存切分之外,还研究检查点切分与异步保存。

3.4.3 抢占与优先级:让训练为更重要的任务让位

K8s 的 PriorityClass(优先级类)Preemption(抢占) 机制,是让训练与推理、生产与实验在同一集群共存的基石。

工作机制:

  • 每个 Pod 通过 PriorityClass 声明优先级(一个整数)。
  • 当高优先级 Pod 因资源不足无法调度时,调度器会抢占低优先级 Pod——驱逐它们腾出资源。
  • 被抢占的 Pod 进入待调度状态,等下次资源可用时重启。

典型优先级分层:

层级 PriorityClass 典型场景 抢占行为
生产推理 prod-inference (1M) 线上 LLM 服务 永不让位
生产训练 prod-training (100K) 业务关键模型训练 让位于推理,抢占实验
实验训练 dev-training (10K) 算法实验、调参 让位前两者,可被抢占
调试试错 low-batch (1K) 数据处理、调试 最易被抢占
# PriorityClass 示例(伪代码) apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: prod-training value: 100000 preemptionPolicy: PreemptLowerPriority globalDefault: false

💡 判读:抢占机制让「夜间训练、白天推理」成为可能:白天推理流量高峰,训练被抢占让位;夜间推理低谷,训练 Pod 重新调度上来,利用闲置 GPU。这种「弹性混部」是大模型训练降本的关键工程实践,第 8 章会详细讲。

3.4.4 Spot 实例混部:用低廉算力跑昂贵训练

云厂商的 Spot 实例(AWS Spot、阿里云抢占式实例、GCP Preemptible)是云上算力的「特价区」——价格只有按需实例的 20%-30%,但随时可能被回收(通常 2 分钟通知)。这种特性对训练挑战巨大,但用得好可以大幅降本。

Spot 混部的工程要点:

  1. 检查点密度提高:因为 Spot 随时可能被回收,检查点频率要比 On-Demand 高(如每 500 步一次)。
  2. 优雅退出:监听云厂商的回收通知(通过实例元数据 API 或 K8s 的 Node 事件),收到通知后立即保存检查点再退出。
  3. 断点续训自动重启:训练 Operator 监测 Pod 被驱逐后,自动在新节点(可能是另一台 Spot)上从检查点重启。
  4. 混合实例策略:master 用 On-Demand(保证 master 不挂),worker 用 Spot(worker 挂了弹性恢复)。
  5. 容忍不均匀:训练框架要能容忍 worker 数量变化(见下节弹性训练)。
实例类型 价格 可用性 适合
按需(On-Demand) 100% 生产、master 节点
预留(Reserved) 60-70% 长期稳定负载
Spot 20-30% 不稳定 实验训练、可中断 worker

⚠️ 现实代价:Spot 实例的回收频率有时极高(高峰期可能每小时一次),如果检查点机制不到位,一个训练可能反复重启、进度反复回滚,最终总耗时比 On-Demand 还长。Spot 降本的前提是「弹性恢复能力 + 检查点机制成熟」,否则反而更贵更慢。

3.4.5 弹性训练:容忍 worker 数量动态变化

传统的分布式训练(如 PyTorch DDP)要求 worker 数量在启动时固定,中途加减 worker 会导致训练崩溃。弹性训练(Elastic Training) 打破这个限制,让训练能容忍 worker 动态加入与退出。

PyTorch 在 1.11+ 引入了 Torchrun with Elastic Agent,支持弹性训练。其核心机制:

  • Rendezvous(会合):worker 之间通过动态发现协议协商参与成员,不依赖固定的 WORLD_SIZE。
  • Rank 重编号:worker 加入/退出时,rank 自动重编号。
  • 状态恢复:成员变化时,从最近检查点恢复训练状态,确保一致性。

弹性训练的价值:

  • 容错:worker 故障时训练不停,自动降级继续。
  • 弹性扩缩:空闲资源多时加入 worker 加速,资源紧张时退出 worker 让位。
  • 配合 Spot:Spot 被回收时 worker 退出,新 Spot 起来后加入,训练连续进行。

但弹性训练也有挑战:

挑战 说明
学习率调整 worker 数量变化时,有效 batch size 变化,学习率要相应调整
数据切分 要保证 worker 变化后数据不重复、不遗漏
一致性 成员变化时的状态同步要保证全局一致
通信开销 rendezvous 与重协商本身有开销

💡 判读:弹性训练是云原生 AI 的「圣杯」之一——它让训练能像在线服务一样弹性伸缩。但目前对张量并行、流水线并行等复杂策略的弹性支持仍有限,主要用于数据并行场景。第 5 章讲分布式训练时会再次涉及。

3.4.6 重调度:周期性优化集群布局

K8s 原生调度器是「一次调度、不再动」——Pod 一旦落到节点,就不再移动,即使后来出现更好的位置。这可能导致:

  • 节点负载严重不均(一些节点过载、一些节点空闲)。
  • 反亲和规则失效(新 Pod 落地后,原来的分散布局被破坏)。
  • 碎片化(小 Pod 散布,无法腾出大块资源)。

Descheduler 是 K8s 的重调度工具,周期性扫描集群,按策略驱逐并重新调度 Pod,优化整体布局。常见策略:

  • RemoveDuplicates:同一 Deployment 的副本避免集中在少数节点。
  • LowNodeUtilization:把高负载节点的 Pod 迁移到低负载节点。
  • RemovePodsViolatingInterPodAntiAffinity:修复违反反亲和的布局。
  • RemovePodsViolatingNodeAffinity:节点标签变化后,迁移不再匹配的 Pod。

对 AI 场景,Descheduler 主要用于推理服务的负载均衡(迁移副本到空闲节点),但要慎用于训练 Pod——训练 Pod 是有状态的,迁移等于重启,要配合检查点机制使用。

3.4.7 弹性训练的完整恢复回路

把检查点、抢占、Spot、弹性训练、重调度串起来,一个生产级弹性训练系统的完整回路:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 360" font-family="sans-serif" font-size="12"> <text x="440" y="22" text-anchor="middle" font-size="15" font-weight="bold">弹性训练完整恢复回路</text> <!-- 训练核心 --> <rect x="60" y="60" width="160" height="80" rx="8" fill="#dbeafe" stroke="#2563eb"/> <text x="140" y="90" text-anchor="middle" font-weight="bold">弹性训练</text> <text x="140" y="110" text-anchor="middle">Torchrun Elastic</text> <text x="140" y="128" text-anchor="middle">动态成员</text> <!-- 检查点 --> <rect x="270" y="60" width="160" height="80" rx="8" fill="#fef9c3" stroke="#ca8a04"/> <text x="350" y="90" text-anchor="middle" font-weight="bold">检查点</text> <text x="350" y="110" text-anchor="middle">异步保存</text> <text x="350" y="128" text-anchor="middle">多副本</text> <!-- 中断源 --> <rect x="480" y="60" width="160" height="80" rx="8" fill="#fee2e2" stroke="#dc2626"/> <text x="560" y="90" text-anchor="middle" font-weight="bold">中断源</text> <text x="560" y="110" text-anchor="middle">Spot 回收</text> <text x="560" y="128" text-anchor="middle">抢占/故障</text> <!-- 恢复 --> <rect x="60" y="200" width="760" height="120" rx="8" fill="#dcfce7" stroke="#16a34a"/> <text x="80" y="225" font-weight="bold" fill="#15803d">恢复回路:</text> <text x="80" y="250" font-size="12">1. 监听回收通知(云元数据 API / K8s 事件)→ 优雅退出前立即保存检查点</text> <text x="80" y="272" font-size="12">2. Training Operator 检测 Pod 驱逐 → 在新 Spot 节点重新调度</text> <text x="80" y="294" font-size="12">3. 新 Pod 启动 → Torchrun Elastic rendezvous → 从最近检查点恢复状态</text> <text x="80" y="316" font-size="12">4. 训练继续,调整 batch size 与学习率适配新成员数</text> <!-- 连接箭头 --> <line x1="220" y1="100" x2="270" y2="100" stroke="#475569" stroke-width="1.5"/> <line x1="430" y1="100" x2="480" y2="100" stroke="#475569" stroke-width="1.5"/> <line x1="560" y1="140" x2="560" y2="200" stroke="#dc2626" stroke-width="2" stroke-dasharray="4,2"/> <line x1="350" y1="140" x2="350" y2="200" stroke="#ca8a04" stroke-width="2" stroke-dasharray="4,2"/> <line x1="140" y1="140" x2="140" y2="200" stroke="#2563eb" stroke-width="2" stroke-dasharray="4,2"/> </svg>

这个回路体现了云原生 AI 的「自愈性」——中断不再是灾难,而是被工程化处理的常态事件。这也是为什么大模型训练能在云端长期稳定运行的根本。

3.4.8 第 3 章小结与第 4 章预告

到这里,第 3 章完成了从「独占模式」到「满载弹性」的完整升级:

  1. GPU 共享(3.1):MIG/MPS/时分复用,把单卡利用率打满。
  2. 拓扑感知(3.2):NVLink/RDMA 亲和,让多卡通信跑得快。
  3. 批处理调度器(3.3):Volcano/Kueue,做 Gang 与公平调度。
  4. 弹性训练(3.4):检查点、抢占、Spot、弹性恢复,容忍中断。

这四把刀合在一起,让 GPU 集群从「独占低效」走向「共享满载、弹性自愈」。第 4 章将离开调度底座,进入下一层——MLOps 流水线,讲清模型从数据到生产的工程化闭环。

本节小结

  • 大模型训练的中断不可避免(Meta 训 OPT-175B 两个月经历 100+ 次中断),工程化核心是把中断变成确定性保障。
  • 检查点机制是训练的生命线:定期保存权重+优化器+调度器+数据状态,支持断点续训;大模型检查点可达 TB 级,要异步保存、多副本、校验和。
  • PriorityClass + Preemption 让训练与推理、生产与实验分层共存,支撑「夜间训练、白天推理」的弹性混部。
  • Spot 实例可降本 70%,但前提是弹性恢复 + 检查点机制成熟,否则反而更慢更贵。
  • 弹性训练(Torchrun Elastic)让 worker 动态加入/退出,是云原生 AI 的圣杯,目前主要用于数据并行。
  • Descheduler 周期性重调度优化布局,推理可用,训练需慎用并配检查点。
  • 检查点 + 抢占 + Spot + 弹性 + 重调度构成完整恢复回路,体现云原生 AI 的自愈性。

第 3 章完结。下一章《第 4 章 MLOps 流程设计与特征存储》将进入流水线层,讲清模型从数据到生产的工程化闭环。


发布者: 作者: 灏天文库 转发
评论区 (0)
U