中断
本指南适用于希望构建高可用性应用程序的应用程序所有者,因此需要了解 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 前端可能希望确保为负载提供服务的副本数量永远不会低于总副本数量的某个百分比。
集群管理器和托管提供商应该使用尊重 PodDisruptionBudgets 的工具,通过调用驱逐 API 而不是直接删除 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 会计入中断预算,但工作负载资源(如部署和有状态集)在进行滚动升级时不受 PDB 的限制。相反,应用程序更新期间故障的处理方式是在特定工作负载资源的规范中配置的。
建议将 AlwaysAllow
不健康 Pod 驱逐策略 设置为您的 PodDisruptionBudgets,以支持在节点排空期间驱逐行为不端的应用程序。默认行为是等待应用程序 Pod 变得健康,然后才能继续排空。
当使用驱逐 API 驱逐 Pod 时,它会优雅地终止,遵守其PodSpec 中的 terminationGracePeriodSeconds
设置。
PodDisruptionBudget 示例
考虑一个具有 3 个节点的集群,node-1
到 node-3
。该集群运行着多个应用程序。其中一个应用程序最初有 3 个副本,称为 pod-a
、pod-b
和 pod-c
。另一个无关的没有 PDB 的 Pod,称为 pod-x
,也显示出来。最初,Pod 的布局如下
node-1 | node-2 | node-3 |
---|---|---|
pod-a 可用 | pod-b 可用 | pod-c 可用 |
pod-x 可用 |
所有 3 个 Pod 都是部署的一部分,它们共同拥有一个 PDB,该 PDB 要求始终至少有 2 个中的 3 个 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-y
作为 pod-x
的替换。
(注意:对于有状态集,pod-a
(将被称为类似 pod-0
的东西)需要完全终止,然后才能创建其替换(也称为 pod-0
但具有不同的 UID)。否则,该示例也适用于有状态集。)
现在集群处于以下状态
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
时,它将被拒绝,因为这将只留下一个 Pod 可用于部署。
部署为 pod-b
创建了一个名为 pod-e
的替换 Pod。由于集群中没有足够的资源来调度 pod-e
,因此排空将再次被阻止。集群最终可能处于以下状态
node-1 已排空 | node-2 | node-3 | 无节点 |
---|---|---|---|
pod-b 正在终止 | pod-c 可用 | pod-e 正在挂起 | |
pod-d 可用 | pod-y |
此时,集群管理员需要将节点添加回集群才能继续升级。
您可以看到 Kubernetes 如何根据以下因素改变中断发生的速率
- 应用程序需要的副本数量
- 优雅地关闭实例所需的时间
- 新实例启动所需的时间
- 控制器类型
- 集群的资源容量
Pod 中断条件
Kubernetes v1.26 [测试版]
启用后,将添加一个专用的 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,但不能保证。因此,DisruptionTarget
条件可能会添加到 Pod 中,但该 Pod 实际上可能不会被删除。在这种情况下,一段时间后,Pod 中断条件将被清除。当PodDisruptionConditions
功能开关启用时,除了清理 Pod 之外,Pod 垃圾回收器 (PodGC) 还会在它们处于非终止阶段时将它们标记为失败(另请参阅 Pod 垃圾回收)。
当使用 Job(或 CronJob)时,您可能希望将这些 Pod 中断条件用作 Job 的 Pod 失败策略 的一部分。
分离集群所有者和应用程序所有者角色
通常,将集群管理器和应用程序所有者视为具有有限相互了解的独立角色很有用。在以下情况下,这种职责分离可能是有意义的
- 当许多应用程序团队共享一个 Kubernetes 集群时,并且角色自然专业化
- 当使用第三方工具或服务来自动化集群管理时
Pod 中断预算通过提供角色之间的接口来支持这种角色分离。
如果您的组织中没有这种职责分离,则可能不需要使用 Pod 中断预算。
如何在集群上执行破坏性操作
如果您是集群管理员,并且需要对集群中的所有节点执行破坏性操作(例如节点或系统软件升级),以下是一些选项
- 在升级期间接受停机时间。
- 故障转移到另一个完整的副本集群。
- 没有停机时间,但可能对复制的节点和人工协调切换工作来说成本很高。
- 编写容错应用程序并使用 PDB。
- 没有停机时间。
- 最小的资源复制。
- 允许更多地自动化集群管理。
- 编写容错应用程序很棘手,但容忍自愿中断的工作在很大程度上与支持自动扩展和容忍非自愿中断的工作重叠。
下一步
按照步骤通过 配置 Pod 中断预算 来保护您的应用程序。
详细了解 排空节点
了解 更新部署,包括在部署过程中保持其可用性的步骤。