Kubernetes v1.35: 通过就地重启 Pod 实现更高的效率

Kubernetes 1.35 版本引入了一项强大的新特性,满足了用户对 Pod 就地重启的迫切需求。 这项名为“重启所有容器”(Restart All Containers,1.35 版本为 Alpha 版)的特性, 相比于资源用量较高的删除并重建整个 Pod 的方式,能够更高效地重置 Pod 的状态。 该特性对于 AI/ML 工作负载尤为实用,使应用程序开发人员能够专注于核心训练逻辑, 同时将复杂的故障处理和恢复机制交给边车容器和声明式 Kubernetes 配置来处理。 凭借 RestartAllContainers 和其他计划中的增强特性, Kubernetes 将继续构建更灵活、更健壮、更高效的 AI/ML 工作负载平台。

启用 RestartAllContainersOnContainerExits 特性门控即可使用此新特性。 此 Alpha 特性扩展了容器重启规则特性, 该特性在 Kubernetes 1.35 中升级为 Beta 版。

问题:当单个容器重启不足以解决问题,而重新创建 Pod 成本过高时

Kubernetes 长期以来一直支持 Pod 级别的重启策略(restartPolicy), 最近也支持单个容器级别的重启策略。 这些策略非常适合处理单个独立进程中的崩溃。然而,许多现代应用程序具有更复杂的容器间依赖关系。例如:

  • 初始化容器通过挂载卷或生成配置文件来准备环境。如果主应用程序容器损坏了此环境, 仅仅重启该容器是不够的,需要重新运行整个初始化过程。
  • 监视边车监控系统健康状况。如果它检测到不可恢复但可重试的错误状态,则必须触发主应用程序容器从头开始重启。
  • 管理远程资源的边车发生故障。即使边车自行重启,主容器也可能因为尝试访问过时或损坏的连接而卡住。

在所有这些情况下,我们期望的操作并非重启单个容器,而是重启所有容器。 此前,实现此目的的唯一方法是删除 Pod,然后由控制器(例如 Job 或 ReplicaSet)创建一个新的 Pod。 这个过程缓慢且成本高昂,涉及调度器、节点资源分配以及网络和存储的重新初始化。

在处理大规模 AI/ML 工作负载(≥ 1000 个节点,每个节点一个 Pod)时,这种低效性会更加严重。 这些同步工作负载的一个常见要求是,当发生故障(例如节点崩溃)时, 必须重新创建集群中的所有 Pod 以重置状态,然后才能恢复训练, 即使其他 Pod 并未直接受到故障的影响。 同时删除、创建和调度数千个 Pod 会造成巨大的瓶颈。 此次故障造成的损失估计每月可能高达 10 万美元(资源浪费)。

处理 AI/ML 训练任务的这些故障需要复杂的集成,涉及训练框架和 Kubernetes, 而这两者通常都很脆弱且繁琐。 此特性引入了一种 Kubernetes 原生解决方案, 提高了系统健壮性,并使应用程序开发人员能够专注于其核心训练逻辑。

就地重启 Pod 的另一个主要优势在于,将 Pod 保留在其分配的节点上可以进行进一步的优化。 例如,可以实现与特定 Pod 标识绑定的节点级缓存, 而当 Pod 不必要地在不同的节点上重新创建时,这种优化方式是无法实现的。

引入 RestartAllContainers 操作

为了解决这个问题,Kubernetes v1.35 在容器重启规则中添加了一个新的操作:RestartAllContainers。 当容器以符合此操作规则的方式退出时,kubelet 会启动对 Pod 的快速就地重启。

这种就地重启非常高效,因为它保留了 Pod 最重要的资源:

  • Pod 的 UID、IP 地址和网络命名空间。
  • Pod 的沙箱及其所有连接的设备。
  • 所有卷,包括 emptyDir 和从 PVC 挂载的卷。

终止所有正在运行的容器后,Pod 的启动序列将从头开始重新执行。 这意味着所有初始化容器将按顺序再次运行,随后是边车容器和常规容器, 从而确保在已知良好的环境中完全重新启动。 除了临时容器(会被终止)之外,所有其他容器——包括之前成功或失败的容器——都将重新启动, 而不管它们各自的重启策略如何。

应用案例

1. 高效重启机器学习/批处理作业

对于机器学习训练作业, 在工作节点 Pod 发生故障时重新调度是一项代价高昂的操作, 会浪费宝贵的计算资源。 在一个拥有 1000 个节点的训练集群中, 重新调度带来的开销每月可能会浪费超过 10 万美元的计算资源

借助 RestartAllContainers 操作,你可以启用一种速度更快、混合的恢复策略来解决这个问题: 仅重新创建“故障”Pod(例如,位于不健康节点上的 Pod),同时对其余健康的 Pod 触发 RestartAllContainers 操作。基准测试表明,这可以将恢复开销从几分钟降低到几秒钟。

通过就地重启,监视器边车可以监控主训练过程。如果遇到特定的可重试错误, 监视器可以退出并返回指定的代码,从而触发工作 Pod 的快速重置, 使其能够从上一个检查点重新启动,而无需 Job 控制器的参与。Kubernetes 现在原生支持此特性。

有关未来开发和 JobSet 特性的更多详细信息,请参阅 KEP-467 JobSet 就地重启

apiVersion: v1
kind: Pod
metadata:
  name: ml-worker-pod
spec:
  restartPolicy: Never
  initContainers:
  # 此初始化容器将在每次就地重启时重新运行。
  - name: setup-environment
    image: my-repo/setup-worker:1.0
  - name: watcher-sidecar
    image: my-repo/watcher:1.0
    restartPolicy: Always
    restartPolicyRules:
    - action: RestartAllContainers
      onExit:
        exitCodes:
          operator: In
          # 监视器返回特定退出代码会触发 Pod 完全重启。
          values: [88]
  containers:
  - name: main-application
    image: my-repo/training-app:1.0

2. 重新运行初始化容器以确保干净状态

设想这样一种场景:初始化容器负责获取凭据或设置共享卷。 如果主应用程序发生故障,导致共享状态损坏,则需要重新运行初始化容器。

通过配置主应用程序在检测到此类损坏时以特定代码退出,你可以触发 RestartAllContainers 操作,从而确保初始化容器在应用程序重启之前提供一个干净的设置。

3. 处理高频率的类似任务执行

有些情况下,任务最好以 Pod 执行的形式呈现。每个任务都需要干净利落地执行。例如,游戏会话后端或队列项处理。 如果任务频率很高,运行完整的 Pod 创建、调度和初始化流程会非常耗费资源, 尤其是在任务执行时间可能很短的情况下。 Kubernetes 原生支持从头开始重启所有容器,无需自定义解决方案或框架即可处理这种情况。

使用方法

要试用此特性,你必须在运行 Kubernetes v1.35 或更高版本的 Kubernetes 集群组件(API 服务器和 kubelet)上启用 RestartAllContainersOnContainerExits 特性门控。 此 Alpha 特性扩展了 ContainerRestartRules 特性,后者已在 v1.35 版本中升级为 beta 版,并默认启用。

启用后,你可以将 restartPolicyRules 添加到任何容器(Init、边车或常规容器), 并使用 RestartAllContainers 操作。

该特性旨在方便现有应用程序使用。但是,如果应用程序不遵循某些最佳实践, 则可能会导致应用程序本身或可观测性工具出现问题。 启用此特性时,请确保所有容器都是可重入的,并且外部工具已准备好用于重新启动初始化容器。 此外,重启所有容器时,kubelet 不会运行 preStop 钩子。 这意味着容器必须设计为能够处理突然终止的情况,而无需依赖 preStop 钩子来实现优雅关闭。

观察重启

为了使重启过程可观察,Pod 的状态中添加了一个新的条件 AllContainersRestarting。 当触发重启时,此条件变为 True;当所有容器终止且 Pod 准备好重新开始其生命周期时, 此条件变为 False。这为用户和其他集群组件提供了关于 Pod 状态的清晰信号。

所有通过此操作重启的容器,其容器状态中的重启计数都会递增。

了解更多

我们期待你的反馈!

作为一项 Alpha 特性,RestartAllContainers 现已开放试用, 欢迎你提出任何使用案例和反馈意见。 此特性由 SIG Node 社区驱动。 如果你有兴趣参与、分享想法或做出贡献,请加入我们!

你可以通过以下方式联系 SIG Node: