3.4 抢占、重调度与弹性训练:让训练容忍中断 大模型训练动辄跑数天甚至数周,期间节点故障、网络抖动、Spot 实例被回收几乎是必然事件。一个不能容忍中断的训练系统,等于「赌这周不出事」——而工程化的核心,就是把这个赌注变成确定性保障。 3.4.1 训练中断的不可避免性 在真实生产环境里,长时间训练面临的中断源远比想象中多: 中断源 | 频率 | 影响 GPU 硬件故障 | 数月一次 | 单卡失效 节点重启/维护 | 数周一次 | 全节点 Pod 驱逐 网络抖动 | 偶发 | 通信超时 Spot/抢占实例回收 | 不确定 | 训练被强杀 高优先级任务抢占 | 按策略 | 训练让位 集群升级 | 季度级 | 全集群滚动重启 检查点写入失败 | 偶发 | 状态丢失风险 💡 判读:Meta
大模型训练动辄跑数天甚至数周,期间节点故障、网络抖动、Spot 实例被回收几乎是必然事件。一个不能容忍中断的训练系统,等于「赌这周不出事」——而工程化的核心,就是把这个赌注变成确定性保障。
在真实生产环境里,长时间训练面临的中断源远比想象中多:
| 中断源 | 频率 | 影响 |
|---|---|---|
| GPU 硬件故障 | 数月一次 | 单卡失效 |
| 节点重启/维护 | 数周一次 | 全节点 Pod 驱逐 |
| 网络抖动 | 偶发 | 通信超时 |
| Spot/抢占实例回收 | 不确定 | 训练被强杀 |
| 高优先级任务抢占 | 按策略 | 训练让位 |
| 集群升级 | 季度级 | 全集群滚动重启 |
| 检查点写入失败 | 偶发 | 状态丢失风险 |
💡 判读:Meta 在训练 OPT-175B 时公开过数据:在 1024 张 A100 上跑两个月,经历了 100+ 次中断,平均每天 1-2 次。这不是个例,而是大模型训练的常态。任何不预设「会中断」的训练系统设计都是失败的。
应对中断有三类策略,本节逐一展开:
检查点(Checkpoint)是大模型训练的生命线。它定期把训练状态保存到持久存储,中断后从最近的检查点恢复。
一个完整的训练检查点至少要包含:
检查点的工程实践要点:
| 实践 | 说明 |
|---|---|
| 保存频率 | 折中:太频繁影响吞吐,太稀疏丢失进度多。常见每 1000-5000 步一次 |
| 异步保存 | 检查点写入耗时大,用后台线程异步写,不阻塞训练 |
| 多副本 | 关键检查点写多副本(本地 + 远程),防单点丢失 |
| 校验和 | 保存 checksum,恢复时校验完整性 |
| 版本管理 | 保留最近 N 个检查点,定期清理旧的 |
| 显存到内存再到存储 | 先从 GPU 显存拷到 CPU 内存,再异步写存储,减少 GPU 等待 |
⚠️ 大模型检查点的成本:一个 70B 模型用 Adam 训练,检查点(权重 + 优化器状态)可达 1TB+。每次保存要把 1TB 写到存储,即使带宽 10GB/s 也要 100 秒。这就是为什么 ZeRO-3(第 5 章)等技术在显存切分之外,还研究检查点切分与异步保存。
K8s 的 PriorityClass(优先级类) 与 Preemption(抢占) 机制,是让训练与推理、生产与实验在同一集群共存的基石。
工作机制:
典型优先级分层:
| 层级 | 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 章会详细讲。
云厂商的 Spot 实例(AWS Spot、阿里云抢占式实例、GCP Preemptible)是云上算力的「特价区」——价格只有按需实例的 20%-30%,但随时可能被回收(通常 2 分钟通知)。这种特性对训练挑战巨大,但用得好可以大幅降本。
Spot 混部的工程要点:
| 实例类型 | 价格 | 可用性 | 适合 |
|---|---|---|---|
| 按需(On-Demand) | 100% | 高 | 生产、master 节点 |
| 预留(Reserved) | 60-70% | 高 | 长期稳定负载 |
| Spot | 20-30% | 不稳定 | 实验训练、可中断 worker |
⚠️ 现实代价:Spot 实例的回收频率有时极高(高峰期可能每小时一次),如果检查点机制不到位,一个训练可能反复重启、进度反复回滚,最终总耗时比 On-Demand 还长。Spot 降本的前提是「弹性恢复能力 + 检查点机制成熟」,否则反而更贵更慢。
传统的分布式训练(如 PyTorch DDP)要求 worker 数量在启动时固定,中途加减 worker 会导致训练崩溃。弹性训练(Elastic Training) 打破这个限制,让训练能容忍 worker 动态加入与退出。
PyTorch 在 1.11+ 引入了 Torchrun with Elastic Agent,支持弹性训练。其核心机制:
弹性训练的价值:
但弹性训练也有挑战:
| 挑战 | 说明 |
|---|---|
| 学习率调整 | worker 数量变化时,有效 batch size 变化,学习率要相应调整 |
| 数据切分 | 要保证 worker 变化后数据不重复、不遗漏 |
| 一致性 | 成员变化时的状态同步要保证全局一致 |
| 通信开销 | rendezvous 与重协商本身有开销 |
💡 判读:弹性训练是云原生 AI 的「圣杯」之一——它让训练能像在线服务一样弹性伸缩。但目前对张量并行、流水线并行等复杂策略的弹性支持仍有限,主要用于数据并行场景。第 5 章讲分布式训练时会再次涉及。
K8s 原生调度器是「一次调度、不再动」——Pod 一旦落到节点,就不再移动,即使后来出现更好的位置。这可能导致:
Descheduler 是 K8s 的重调度工具,周期性扫描集群,按策略驱逐并重新调度 Pod,优化整体布局。常见策略:
对 AI 场景,Descheduler 主要用于推理服务的负载均衡(迁移副本到空闲节点),但要慎用于训练 Pod——训练 Pod 是有状态的,迁移等于重启,要配合检查点机制使用。
把检查点、抢占、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 章完成了从「独占模式」到「满载弹性」的完整升级:
这四把刀合在一起,让 GPU 集群从「独占低效」走向「共享满载、弹性自愈」。第 4 章将离开调度底座,进入下一层——MLOps 流水线,讲清模型从数据到生产的工程化闭环。
第 3 章完结。下一章《第 4 章 MLOps 流程设计与特征存储》将进入流水线层,讲清模型从数据到生产的工程化闭环。