中断
本指南适用于想要构建高可用性应用程序的应用程序所有者,因此他们需要了解可能会发生在 Pod 上的各种中断类型。
它也适用于想要执行自动化集群操作的集群管理员,例如升级和自动扩展集群。
自愿和非自愿中断
在有人(个人或控制器)销毁 Pod 之前,或者出现不可避免的硬件或系统软件错误之前,Pod 不会消失。
我们将这些不可避免的情况称为应用程序的 *非自愿中断*。例如:
- 支持节点的物理机器出现硬件故障
- 集群管理员错误地删除了 VM(实例)
- 云提供商或管理程序故障导致 VM 消失
- 内核崩溃
- 由于集群网络分区,节点从集群中消失
- 由于节点 资源不足,导致 Pod 被驱逐。
除了资源不足这种情况之外,所有这些情况大多数用户都应该熟悉;它们不是 Kubernetes 特有的。
我们将其他情况称为 *自愿中断*。这些包括应用程序所有者发起的操作和集群管理员发起的操作。典型的应用程序所有者操作包括:
- 删除管理 Pod 的部署或其他控制器
- 更新部署的 Pod 模板,导致重启
- 直接删除 Pod(例如,误操作)
集群管理员操作包括:
这些操作可能由集群管理员直接执行,也可能由集群管理员运行的自动化操作执行,或者由您的集群托管提供商执行。
询问您的集群管理员或咨询您的云提供商或发行版文档以确定是否为您的集群启用了任何自愿中断来源。如果未启用任何来源,则可以跳过创建 Pod 中断预算。
注意
并非所有自愿中断都受到 Pod 中断预算的约束。例如,删除部署或 Pod 会绕过 Pod 中断预算。处理中断
以下是减轻非自愿中断的一些方法
- 确保您的 Pod 请求其所需的资源。
- 如果您需要更高的可用性,请复制您的应用程序。(了解如何运行复制的 无状态 和 有状态 应用程序。)
- 对于在运行复制的应用程序时获得更高的可用性,请将应用程序分布在机架(使用 反亲和性)或区域(如果使用 多区域集群)之间。
自愿中断的频率各不相同。在基本 Kubernetes 集群上,没有自动化的自愿中断(只有用户触发的中断)。但是,您的集群管理员或托管提供商可能会运行一些额外的服务,这些服务会导致自愿中断。例如,推出节点软件更新会导致自愿中断。此外,集群(节点)自动扩展的一些实现可能会导致自愿中断以碎片化和压缩节点。您的集群管理员或托管提供商应该记录了应预期的自愿中断级别(如果有)。某些配置选项,例如 在您的 Pod 规范中使用 PriorityClasses 也会导致自愿(和非自愿)中断。
Pod 中断预算
Kubernetes v1.21 [稳定]
Kubernetes 提供了一些功能来帮助您运行高可用性应用程序,即使您引入了频繁的自愿中断。
作为应用程序所有者,您可以为每个应用程序创建一个 PodDisruptionBudget(PDB)。PDB 限制了复制应用程序中由于自愿中断而同时停机的 Pod 数量。例如,基于仲裁的应用程序希望确保运行的副本数量永远不会低于仲裁所需的副本数量。Web 前端可能希望确保为负载提供服务的副本数量永远不会低于总副本数量的某个百分比。
集群管理器和托管提供商应该使用通过调用 驱逐 API(而不是直接删除 Pod 或部署)来尊重 Pod 中断预算的工具。
例如,kubectl drain
子命令使您可以将节点标记为即将退出服务。当您运行 kubectl drain
时,该工具会尝试驱逐您要从服务中移除的节点上的所有 Pod。kubectl
代表您提交的驱逐请求可能会暂时被拒绝,因此该工具会定期重试所有失败的请求,直到目标节点上的所有 Pod 都终止,或者直到达到可配置的超时时间。
PDB 指定了应用程序可以容忍多少个副本,相对于它应该拥有的副本数量。例如,一个有 .spec.replicas: 5
的部署应该始终拥有 5 个 Pod。如果它的 PDB 允许一次拥有 4 个 Pod,那么驱逐 API 允许一次驱逐一个(但不能驱逐两个)Pod。
构成应用程序的 Pod 组使用标签选择器指定,与应用程序控制器(部署、有状态集等)使用的标签选择器相同。
“预期”的 Pod 数量是从管理这些 Pod 的工作负载资源的 .spec.replicas
计算出来的。控制平面通过检查 Pod 的 .metadata.ownerReferences
来发现拥有工作负载资源。
非自愿中断 无法通过 PDB 阻止;但是它们确实会计入预算。
由于应用程序滚动升级而被删除或不可用的 Pod 会计入中断预算,但工作负载资源(如 Deployment 和 StatefulSet)在执行滚动升级时不受 PDB 的限制。相反,应用程序更新期间错误的处理方式是在特定工作负载资源的规范中配置的。
建议将 AlwaysAllow
不健康 Pod 驱逐策略 设置为您的 PodDisruptionBudgets,以支持在节点排空期间驱逐行为不端的应用程序。默认行为是等待应用程序 Pod 变得 健康 才能继续排空。
当使用驱逐 API 驱逐 Pod 时,它会优雅地 终止,遵守其 PodSpec 中的 terminationGracePeriodSeconds
设置。
PodDisruptionBudget 示例
假设一个包含 3 个节点的集群,分别是 node-1
到 node-3
。该集群运行多个应用程序。其中一个应用程序最初有 3 个副本,分别称为 pod-a
、pod-b
和 pod-c
。另一个无关的 Pod,没有 PDB,称为 pod-x
,也显示在下面。最初,这些 Pod 的布局如下所示
node-1 | node-2 | node-3 |
---|---|---|
pod-a 可用 | pod-b 可用 | pod-c 可用 |
pod-x 可用 |
所有 3 个 Pod 都是部署的一部分,它们共同拥有一个 PDB,该 PDB 要求始终至少有 2 个 Pod 可用。
例如,假设集群管理员想要重新引导到一个新的内核版本以修复内核中的错误。集群管理员首先尝试使用 kubectl drain
命令排空 node-1
。该工具尝试驱逐 pod-a
和 pod-x
。这立即成功。两个 Pod 同时进入 terminating
状态。这使得集群处于以下状态
node-1 正在排空 | node-2 | node-3 |
---|---|---|
pod-a 正在终止 | pod-b 可用 | pod-c 可用 |
pod-x 正在终止 |
部署注意到一个 Pod 正在终止,因此它创建了一个名为 pod-d
的替换 Pod。由于 node-1
处于隔离状态,因此它将落在另一个节点上。另一个 Pod pod-y
也被创建为 pod-x
的替换 Pod。
(注意:对于 StatefulSet,pod-a
(将被命名为类似于 pod-0
的名称)需要完全终止,然后才能创建其替换(也称为 pod-0
但具有不同的 UID)。否则,此示例也适用于 StatefulSet。)
现在集群处于以下状态
node-1 正在排空 | node-2 | node-3 |
---|---|---|
pod-a 正在终止 | pod-b 可用 | pod-c 可用 |
pod-x 正在终止 | pod-d 启动中 | pod-y |
在某个时刻,Pod 终止,集群看起来像这样
node-1 已排干 | node-2 | node-3 |
---|---|---|
pod-b 可用 | pod-c 可用 | |
pod-d 启动中 | pod-y |
此时,如果一位心急的集群管理员尝试排干 node-2
或 node-3
,排干命令将被阻塞,因为部署只有 2 个可用 Pod,而其 PDB 至少需要 2 个。一段时间后,pod-d
变为可用。
集群状态现在看起来像这样
node-1 已排干 | node-2 | node-3 |
---|---|---|
pod-b 可用 | pod-c 可用 | |
pod-d 可用 | pod-y |
现在,集群管理员尝试排干 node-2
。排干命令将尝试按照某种顺序逐个驱逐这两个 Pod,例如先驱逐 pod-b
,然后驱逐 pod-d
。它将成功驱逐 pod-b
。但是,当它尝试驱逐 pod-d
时,它将被拒绝,因为那样会导致部署只有 1 个 Pod 可用。
部署会创建 pod-b
的替换,名为 pod-e
。由于集群中没有足够的资源来调度 pod-e
,因此排干将再次被阻塞。集群最终可能会处于以下状态
node-1 已排干 | node-2 | node-3 | 无节点 |
---|---|---|---|
pod-b 终止中 | pod-c 可用 | pod-e 挂起 | |
pod-d 可用 | pod-y |
此时,集群管理员需要向集群添加一个节点才能继续升级。
您可以看到 Kubernetes 如何根据以下因素调整中断发生的速度
- 应用程序所需的副本数量
- 优雅地关闭实例所需的时间
- 启动新实例所需的时间
- 控制器类型
- 集群的资源容量
Pod 中断条件
Kubernetes v1.31 [稳定]
添加了一个专用的 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,但不能保证。因此,DisruptionTarget
条件可能会添加到 Pod 中,但该 Pod 实际上可能不会被删除。在这种情况下,一段时间后,Pod 中断条件将被清除。除了清理 Pod 之外,Pod 垃圾回收器 (PodGC) 还会在 Pod 处于非终止阶段时将其标记为失败(另请参阅 Pod 垃圾回收)。
使用 Job(或 CronJob)时,您可能希望将这些 Pod 中断条件用作 Job 的 Pod 失败策略 的一部分。
分离集群所有者和应用程序所有者角色
通常,将集群管理器和应用程序所有者视为具有有限了解彼此的独立角色是有用的。在以下情况下,这种责任分离可能是有意义的
- 当许多应用程序团队共享一个 Kubernetes 集群时,并且角色具有自然的专业化
- 当使用第三方工具或服务来自动化集群管理时
Pod 中断预算通过在角色之间提供接口来支持这种角色分离。
如果您的组织中没有这种责任分离,则可能不需要使用 Pod 中断预算。
如何在集群上执行破坏性操作
如果您是集群管理员,并且需要对集群中的所有节点执行破坏性操作(例如节点或系统软件升级),以下是几种选择
- 在升级期间接受停机时间。
- 故障转移到另一个完整的副本集群。
- 无停机时间,但可能既要花费复制节点的成本,也要花费人类协调切换的成本。
- 编写容错应用程序并使用 PDB。
- 无停机时间。
- 最小的资源重复。
- 允许更多地自动执行集群管理。
- 编写容错应用程序很棘手,但容忍自愿中断的工作与支持自动缩放和容忍非自愿中断的工作在很大程度上是重叠的。
下一步
按照步骤通过 配置 Pod 中断预算 来保护您的应用程序。
了解有关 排干节点 的更多信息
了解有关 更新部署 的信息,包括在推出过程中保持其可用性的步骤。