中断

本指南适用于希望构建高可用应用程序,因此需要了解 Pod 可能发生何种中断的应用程序所有者。

它也适用于希望执行自动化集群操作(如升级和自动扩缩集群)的集群管理员。

自愿中断和非自愿中断

Pod 不会消失,除非有人(用户或控制器)将其销毁,或者发生无法避免的硬件或系统软件错误。

我们将这些不可避免的情况称为应用程序的 非自愿中断。例如:

  • 支持节点的物理机硬件故障
  • 集群管理员错误删除虚拟机(实例)
  • 云提供商或虚拟机管理程序故障导致虚拟机消失
  • 内核崩溃
  • 由于集群网络分区,节点从集群中消失
  • 由于节点 资源不足 导致 Pod 被驱逐。

除了资源不足的情况,所有这些情况对大多数用户来说都应该很熟悉;它们并非 Kubernetes 特有的。

我们将其他情况称为 自愿中断。这包括应用程序所有者启动的操作和集群管理员启动的操作。典型的应用程序所有者操作包括:

  • 删除管理 Pod 的 Deployment 或其他控制器
  • 更新 Deployment 的 Pod 模板导致重启
  • 直接删除 Pod(例如意外删除)

集群管理员操作包括:

  • 排空节点 以进行修复或升级。
  • 从集群中排空节点以缩减集群(了解 节点自动扩缩)。
  • 从节点中删除 Pod,以允许其他内容适合该节点。

这些操作可能由集群管理员直接执行,也可能由集群管理员运行的自动化程序执行,或者由你的集群托管提供商执行。

请咨询你的集群管理员或查阅你的云提供商或发行版文档,以确定你的集群是否启用了任何自愿中断源。如果未启用任何源,你可以跳过创建 Pod 中断预算。

处理中断

以下是一些缓解非自愿中断的方法:

  • 确保你的 Pod 请求 它所需的资源。
  • 如果需要更高的可用性,请复制你的应用程序。(了解如何运行复制的无状态有状态应用程序。)
  • 对于运行复制应用程序时更高的可用性,请将应用程序分散到不同的机架(使用 反亲和性)或不同可用区(如果使用 多可用区集群)。

自愿中断的频率各不相同。在一个基本的 Kubernetes 集群中,没有自动的自愿中断(只有用户触发的中断)。但是,你的集群管理员或托管提供商可能会运行一些导致自愿中断的额外服务。例如,推出节点软件更新可能会导致自愿中断。此外,某些集群(节点)自动扩缩的实现可能会导致自愿中断以进行碎片整理和节点压缩。你的集群管理员或托管提供商应该已经记录了可能出现的自愿中断级别(如果有)。某些配置选项,例如在你的 Pod 规约中 使用优先级类 也会导致自愿(和非自愿)中断。

Pod 中断预算

特性状态: Kubernetes v1.21 [stable]

Kubernetes 提供了多项功能,可帮助你在引入频繁自愿中断的情况下仍能运行高可用应用程序。

作为应用程序所有者,你可以为每个应用程序创建一个 PodDisruptionBudget (PDB)。PDB 限制了因自愿中断而同时停机的复制应用程序 Pod 的数量。例如,基于法定人数的应用程序希望确保运行的副本数量永远不会低于法定人数所需的数量。一个 Web 前端可能希望确保提供负载的副本数量永远不会低于总数的某个百分比。

集群管理器和托管提供商应使用遵守 PodDisruptionBudgets 的工具,通过调用 驱逐 API,而不是直接删除 Pod 或 Deployment。

例如,kubectl drain 子命令允许你将节点标记为停用。当你运行 kubectl drain 时,该工具会尝试驱逐你正在停用的节点上的所有 Pod。kubectl 代表你提交的驱逐请求可能会暂时被拒绝,因此该工具会定期重试所有失败的请求,直到目标节点上的所有 Pod 都终止,或者直到达到可配置的超时。

PDB 指定了应用程序可以容忍的副本数量,相对于它应该拥有的副本数量。例如,一个 .spec.replicas: 5 的 Deployment 在任何给定时间都应该有 5 个 Pod。如果它的 PDB 允许同时有 4 个 Pod,那么驱逐 API 将允许一次自愿中断一个(但不能是两个)Pod。

构成应用程序的 Pod 组使用标签选择器指定,该选择器与应用程序控制器(Deployment、StatefulSet 等)使用的选择器相同。

“预期”的 Pod 数量是根据管理这些 Pod 的工作负载资源的 .spec.replicas 计算的。控制平面通过检查 Pod 的 .metadata.ownerReferences 来发现所有者工作负载资源。

非自愿中断 不能通过 PDB 阻止;但是它们确实会计入预算。

由于应用程序滚动升级而被删除或不可用的 Pod 确实会计入中断预算,但工作负载资源(例如 Deployment 和 StatefulSet)在执行滚动升级时不受 PDB 的限制。相反,应用程序更新期间的故障处理是在特定工作负载资源的规范中配置的。

建议将 AlwaysAllow 不健康 Pod 驱逐策略 设置为 PodDisruptionBudgets,以支持在节点排空期间驱逐行为异常的应用程序。默认行为是等待应用程序 Pod 变为健康后才能继续排空。

当使用驱逐 API 驱逐 Pod 时,它会优雅终止,并遵守其 PodSpec 中的 terminationGracePeriodSeconds 设置。

PodDisruptionBudget 示例

考虑一个有 3 个节点的集群,从 node-1node-3。集群正在运行多个应用程序。其中一个应用程序最初有 3 个副本,分别命名为 pod-apod-bpod-c。另一个不相关的、没有 PDB 的 Pod,名为 pod-x,也显示在其中。最初,Pod 的布局如下:

node-1node-2node-3
pod-a 可用pod-b 可用pod-c 可用
pod-x 可用

所有 3 个 Pod 都是 Deployment 的一部分,它们共同拥有一个 PDB,该 PDB 要求在任何时候至少有 2 个 Pod 可用。

例如,假设集群管理员想要重启以使用新的内核版本来修复内核中的 bug。集群管理员首先尝试使用 kubectl drain 命令排空 node-1。该工具尝试驱逐 pod-apod-x。这立即成功。两个 Pod 同时进入 terminating 状态。这使集群处于以下状态:

node-1 正在排空node-2node-3
pod-a 正在终止pod-b 可用pod-c 可用
pod-x 正在终止

Deployment 注意到其中一个 Pod 正在终止,因此它创建了一个名为 pod-d 的替代 Pod。由于 node-1 已被隔离,它会落在另一个节点上。同时,某些进程也创建了 pod-y 作为 pod-x 的替代 Pod。

(注意:对于 StatefulSet,pod-a(可能被命名为 pod-0)需要完全终止,然后才能创建其替代 Pod(也命名为 pod-0 但具有不同的 UID)。否则,该示例也适用于 StatefulSet。)

现在集群处于以下状态:

node-1 正在排空node-2node-3
pod-a 正在终止pod-b 可用pod-c 可用
pod-x 正在终止pod-d 正在启动pod-y

在某个时刻,Pod 终止,集群如下所示:

node-1 已排空node-2node-3
pod-b 可用pod-c 可用
pod-d 正在启动pod-y

此时,如果一个不耐烦的集群管理员尝试排空 node-2node-3,排空命令将阻塞,因为 Deployment 只有 2 个可用 Pod,而其 PDB 要求至少 2 个。一段时间后,pod-d 变为可用。

集群状态现在如下所示:

node-1 已排空node-2node-3
pod-b 可用pod-c 可用
pod-d 可用pod-y

现在,集群管理员尝试排空 node-2。排空命令将尝试以某种顺序驱逐这两个 Pod,例如先 pod-b,然后 pod-d。它将成功驱逐 pod-b。但是,当它尝试驱逐 pod-d 时,它将被拒绝,因为这将导致 Deployment 只剩下一个可用 Pod。

Deployment 为 pod-b 创建一个名为 pod-e 的替代 Pod。由于集群中没有足够的资源来调度 pod-e,排空将再次阻塞。集群可能最终处于以下状态:

node-1 已排空node-2node-3无节点
pod-b 正在终止pod-c 可用pod-e 待定
pod-d 可用pod-y

此时,集群管理员需要将一个节点添加回集群以继续升级。

你可以看到 Kubernetes 根据以下因素来调整中断发生的速率:

  • 应用程序需要的副本数量
  • 优雅关闭实例所需的时间
  • 新实例启动所需的时间
  • 控制器的类型
  • 集群的资源容量

Pod 中断条件

特性状态: Kubernetes v1.31 [stable] (默认启用:true)

添加了一个专用的 Pod DisruptionTarget 条件,以指示 Pod 即将因中断而被删除。条件的 reason 字段还指示 Pod 终止的以下原因之一:

PreemptionByScheduler
Pod 将被调度器抢占,以便容纳一个更高优先级的 Pod。欲了解更多信息,请参阅Pod 优先级抢占
DeletionByTaintManager
Pod 将被 Taint Manager(它是 kube-controller-manager 中节点生命周期控制器的一部分)删除,因为 Pod 不容忍 NoExecute 污点;参见基于污点的驱逐。
EvictionByEvictionAPI
Pod 已被标记为使用 Kubernetes API 进行驱逐
DeletionByPodGC
绑定到不再存在的节点的 Pod 将被Pod 垃圾回收删除。
TerminationByKubelet
Pod 已被 kubelet 终止,原因可能是节点压力驱逐优雅节点关机,或者为了系统关键 Pod 而进行的抢占。

在所有其他中断场景中,例如由于超出 Pod 容器限制而导致的驱逐,Pod 不会收到 DisruptionTarget 条件,因为这些中断很可能是由 Pod 引起的,并且会在重试时再次发生。

除了清理 Pod 外,Pod 垃圾回收器 (PodGC) 还会将处于非终止阶段的 Pod 标记为失败(另请参阅 Pod 垃圾回收)。

使用 Job(或 CronJob)时,你可能希望将这些 Pod 中断条件作为 Job 的Pod 故障策略的一部分。

分离集群所有者和应用程序所有者角色

通常,将集群管理器和应用程序所有者视为相互了解有限的独立角色是有用的。这种职责分离在以下场景中可能很有意义:

  • 当有许多应用程序团队共享一个 Kubernetes 集群,并且角色存在自然专业化时
  • 当使用第三方工具或服务来自动化集群管理时

Pod 中断预算通过提供角色之间的接口来支持这种角色分离。

如果你的组织中没有这种职责分离,你可能不需要使用 Pod 中断预算。

如何对集群执行破坏性操作

如果你是集群管理员,并且需要对集群中的所有节点执行破坏性操作,例如节点或系统软件升级,这里有一些选项:

  • 在升级期间接受停机。
  • 故障转移到另一个完整的副本集群。
    • 无停机时间,但复制节点和协调切换的人力成本可能很高。
  • 编写具有中断容忍度的应用程序并使用 PDB。
    • 无停机时间。
    • 最小的资源重复。
    • 允许更多的集群管理自动化。
    • 编写具有中断容忍度的应用程序很棘手,但容忍自愿中断的工作与支持自动扩缩和容忍非自愿中断的工作在很大程度上是重叠的。

下一步

上次修改时间:2025 年 5 月 16 日太平洋标准时间下午 1:29:修复节点自动扩缩文档的错误链接 (307d6a6855)