在 Kubernetes 上运行 AI 训练任务时,最容易产生的误解之一,就是认为“Pod 自动重启”就等于“训练自动恢复”。实际上,两者解决的是完全不同的问题。Kubernetes负责的是容器和工作负载的生命周期管理,而训练断点恢复属于机器学习框架和训练应用本身的状态管理。一个 Pod 可以在几秒钟后重新启动,但如果训练程序没有保存 checkpoint,重新启动以后仍然可能从 epoch 0 或 global step 0 开始。
先看 Kubernetes 最基础的重启机制。Pod 的 restartPolicy 包括 Always、OnFailure 和 Never。对于由 Deployment、StatefulSet 或其他控制器管理的工作负载,控制器还会负责维持期望的副本数量。restartPolicy 的作用主要是决定容器退出以后 kubelet 是否在原 Pod 中重新启动容器,并不能简单理解为“训练失败以后 Kubernetes 自动重新创建一个全新的训练任务”。如果 Pod 被删除、节点故障,或者上层 Job 控制器判断任务需要重新创建 Pod,则又是另一层机制。
对于 Kubernetes Job 来说,情况更加清楚。Job 的职责是确保一次任务最终达到成功状态,可以通过 backoffLimit 等参数控制失败后的重试次数。如果训练容器退出并返回非零状态,Job controller 可以根据 Job 的配置创建新的 Pod。但新 Pod 启动以后,训练程序究竟从哪里开始执行,并不是 Kubernetes 决定的。
这就是“任务重启”和“断点恢复”的分界线。
假设一个大型模型已经训练了18个小时,在第19小时因为 CUDA OOM、节点故障、网络异常或者训练代码错误而退出。如果 Kubernetes 重新创建 Pod,而训练程序启动时只是重新加载模型初始权重,那么这18个小时的计算结果实际上没有被利用。新的训练进程可能从头读取数据、重新初始化 optimizer、重新建立 learning-rate scheduler,然后再次开始训练。
如果训练程序在运行过程中保存了 checkpoint,那么情况完全不同。checkpoint 通常不只是一个模型权重文件。对于真正需要精确恢复训练状态的任务,还可能包括 optimizer state、学习率调度器状态、当前 global step、epoch、随机数状态、混合精度 scaler 状态,以及分布式训练所需要的其他元数据。
以 PyTorch 为例,简单保存 model.state_dict() 可以恢复模型参数,但这并不一定能够实现严格意义上的“从中断位置继续训练”。如果 optimizer 使用 Adam 一类算法,optimizer 本身保存的大量状态同样影响后续参数更新。如果只恢复模型权重,却没有恢复 optimizer 和 scheduler 状态,重新启动后的训练轨迹可能与中断之前并不一致。
因此,一个比较完整的训练恢复流程通常是这样的:训练进程周期性生成 checkpoint,将 checkpoint 写入 Pod 生命周期之外的持久化存储;训练 Pod 因故障退出以后,新 Pod 启动;启动脚本或者训练框架检查指定位置是否存在最新 checkpoint;如果存在,就加载 checkpoint;然后恢复 global step、optimizer、scheduler 和其他必要状态,继续训练。
这里最关键的一点是:checkpoint不能依赖 Pod 本地临时文件系统。
Pod 本身是高度易失的计算单元。容器重新创建以后,原来的容器文件系统通常不能作为可靠的数据持久化手段。因此,训练 checkpoint 一般需要保存到持久卷或者对象存储。例如 Kubernetes 环境可以使用通过 CSI 提供的 PersistentVolume,也可以让训练程序直接通过 S3、Google Cloud Storage 或其他对象存储 API 写入 checkpoint。
这也是为什么不应该把大型 AI checkpoint 放进 ConfigMap 或 Secret。ConfigMap 和 Secret主要用于配置数据、凭据以及相对小规模的键值数据,并不是为数十GB甚至数百GB的模型 checkpoint 设计的。大型训练状态应该交给专门的持久化存储系统。
对象存储在 AI 训练环境中尤其常见,因为训练计算节点和存储生命周期可以相互独立。GPU 节点可能因为抢占、扩缩容或者故障而消失,但 S3 或 GCS 中的 checkpoint 可以继续存在。新的 Pod 即使被调度到完全不同的节点,也能够通过网络重新获取 checkpoint。
当然,这里会产生一个非常现实的工程问题:checkpoint本身可能非常大。
假设模型参数、optimizer state 和其他训练状态合计达到数百GB,每隔几分钟完整保存一次 checkpoint,会给存储系统和网络带来巨大压力。GPU 训练可能非常昂贵,但如果 checkpoint 写入阶段让数十台 GPU 等待 I/O,昂贵的计算资源同样会被浪费。
因此,大规模训练通常需要在 checkpoint 频率、恢复时间、存储成本和 I/O 压力之间进行设计。例如,可以按照固定时间间隔、固定 global step 或重要训练阶段保存 checkpoint,也可以保留最近几个 checkpoint,并删除更早的版本。对于超大模型,还可以考虑增量 checkpoint、分片 checkpoint、异步保存等机制,具体取决于训练框架和存储系统。
分布式训练的情况更加复杂。
假设一个训练任务由32个 GPU worker 组成,其中一个 worker 因为 GPU Xid error 或节点故障退出。此时并不是简单地“把这个 Pod 重启一下”就结束了。许多分布式训练作业需要所有 worker 重新建立通信组,重新初始化 distributed process group,并恢复一致的训练状态。涉及 PyTorch Distributed、NCCL 或其他分布式训练组件时,一个 worker 的故障可能导致整个训练作业停止。
因此,大规模训练环境通常需要在 Kubernetes 的工作负载管理层和训练框架之间建立更明确的协作机制。Kubernetes负责提供计算资源、调度 Pod、处理节点故障和生命周期;训练框架负责模型状态、优化器状态以及训练进度;而训练 Operator 或作业控制器则可以负责更高层次的训练任务编排。
例如 Kubernetes 生态中的训练 Operator 可以把一个分布式训练任务抽象成一个训练作业,而不是让工程师手工管理几十个 Pod。发生 worker 故障时,Operator 可以根据具体训练框架的机制协调 Pod 重建和任务恢复。不过,Operator 本身仍然不能凭空制造 checkpoint。如果训练程序从来没有保存过训练状态,Kubernetes再聪明也只能重新启动一个没有历史记忆的训练进程。
这可以用一个很简单的工程逻辑来理解:Kubernetes负责“人还在不在”,checkpoint负责“人做到哪一步了”。
前者属于基础设施生命周期管理,后者属于应用状态持久化。两者缺一不可。
还有一个经常被忽略的问题是存储卷和 Pod 调度之间的关系。如果 checkpoint 存储在 PersistentVolume 上,那么新的 Pod 被调度到不同节点之后,必须确认该存储卷能够按照对应的访问模式重新挂载。例如某些存储系统适合单节点读写,而分布式训练可能需要多个 worker 同时访问。这时 StorageClass、CSI driver、volume access mode、网络吞吐以及存储后端性能都会成为实际设计因素。
如果训练程序直接使用对象存储,则不存在传统意义上把整个 checkpoint 目录挂载到 Pod 中的问题,应用可以通过 SDK 或专门的工具读取对象存储中的 checkpoint。这样计算节点与训练数据、模型状态之间的耦合程度通常更低,但代价是每次 checkpoint 保存和恢复都要经过网络 I/O,因此网络带宽和对象存储吞吐能力必须纳入设计。
VolumeSnapshot则是另一个概念。它依赖底层存储系统和 CSI snapshot 能力,可以对 PersistentVolume 做快照,在需要的时候恢复卷状态。它对于数据库、数据卷或者特定训练工作负载的存储保护很有价值,但不能把它简单理解为“AI checkpoint机制”。真正决定训练能否从某一个 global step 继续运行的,仍然是训练应用保存了什么状态。
对于短时间的实验性微调任务,完整的 checkpoint 机制可能不需要设计得特别复杂。如果模型规模较小,训练只需要几十分钟,而且重新运行的成本很低,那么失败后直接重新启动任务可能是可以接受的。工程师需要考虑的是实现 checkpoint 的复杂度是否值得。
但对于持续数天甚至数周的大模型预训练、微调或者分布式训练,情况完全不同。GPU 集群的计算成本、数据读取成本以及训练时间都很高。如果任务运行几十个小时以后因为一次节点故障而全部从头开始,浪费的不只是几小时 GPU 时间,还包括数据加载、集群调度和人工排障所消耗的时间。
因此,真正成熟的 AI 训练平台通常会把“失败重试”和“断点恢复”同时设计进去。
失败重试解决的是“训练进程死了以后如何再次启动”;断点恢复解决的是“再次启动以后如何从上一次可靠保存的位置继续”。前者可以由 Kubernetes Job、工作负载控制器以及 Operator 等基础设施完成,后者则必须由训练框架、checkpoint机制和持久化存储共同完成。
两者还涉及不同的故障类型。例如,容器因为 Python exception 退出,可能只需要重新启动;GPU 因为硬件错误导致节点失效,则可能需要重新调度到另一台节点;整个计算节点消失,训练任务需要依赖外部持久化 checkpoint;如果对象存储本身不可访问,那么即使 Kubernetes 成功创建了新 Pod,训练也可能无法恢复。
所以,一个可靠的 AI 训练系统不应该把“Pod Running”当成训练可靠性的指标。真正应该观察的是训练任务是否能够在计算实例发生故障之后恢复,以及恢复以后是否能够继续保持正确的训练状态。
从架构上看,可以把整个系统分成四层:Kubernetes负责计算资源和 Pod 生命周期,Job 或训练 Operator负责任务编排,训练框架负责模型与优化器状态,而 PersistentVolume 或对象存储负责让这些状态跨越 Pod 生命周期保存下来。只有四层配合起来,才能真正实现生产环境中的容错训练。
对于 AI 基础设施工程师来说,最值得记住的一句话其实很简单:Pod 重启解决的是“进程重新活过来”,checkpoint 恢复解决的是“训练从过去的位置继续”。如果没有后者,前者再快,也可能只是让 GPU 更快地从头浪费一次计算。
Kubernetes 如何处理 AI 训练任务失败,任务重启与训练断点恢复有什么区别
图片说明:示意图 图片来源:Public Domain(公有领域)
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP