文章目录
- 自愿干预和非自愿干预
- PDB
- PDB 示例
- 分离集群所有者和应用程序所有者角色
- 如何在集群上执行中断操作
自愿干预和非自愿干预
Pod 不会消失,除非有人(用户或控制器)将其销毁,或者出现了不可避免的硬件或软件系统错误。
我们把这些不可避免的情况称为应用的非自愿干预(Involuntary Disruptions)。例如:
- 节点下层物理机的硬件故障
- 集群管理员错误地删除虚拟机(实例)
- 云提供商或虚拟机管理程序中的故障导致的虚拟机消失
- 内核错误
- 节点由于集群网络隔离从集群中消失
- 由于节点资源不足导致 pod 被驱逐。
称其他情况为自愿干扰(Voluntary Disruptions)。 包括由应用所有者发起的操作和由集群管理员发起的操作。 典型的应用所有者的操作包括:
- 删除 Deployment 或其他管理 Pod 的控制器
- 更新了 Deployment 的 Pod 模板导致 Pod 重启
- 直接删除 Pod(例如,因为误操作)
集群管理员操作包括:
- 排空(drain)节点进行修复或升级。
- 从集群中排空节点以缩小集群(了解集群自动扩缩)。
- 从节点中移除一个 Pod,以允许其他 Pod 使用该节点。
PDB
即使你会经常引入自愿性干预,Kubernetes 提供的功能也能够支持你运行高度可用的应用。
作为一个应用的所有者,你可以为每个应用创建一个 PodDisruptionBudget(PDB)。
PDB 将限制在同一时间因自愿干预导致的多副本应用中发生宕机的 Pod 数量。 例如,基于票选机制的应用希望确保运行中的副本数永远不会低于票选所需的数量。 Web 前端可能希望确保提供负载的副本数量永远不会低于总数的某个百分比。
集群管理器和托管提供商应使用遵循 Pod Disruption Budgets 的工具,方法是调用Eviction API而不是直接删除 Pod。例如 kubectl drain 命令和 Kubernetes-on-GCE 集群升级脚本
例如,kubectl drain 命令可以用来标记某个节点即将停止服务。 运行 kubectl drain 命令时,工具会尝试驱逐你所停服的节点上的所有 Pod。 kubectl 代表你所提交的驱逐请求可能会暂时被拒绝, 所以该工具会周期性地重试所有失败的请求, 直到目标节点上的所有的 Pod 都被终止,或者达到配置的超时时间。
PDB 指定应用可以容忍的副本数量(相当于应该有多少副本)。 例如,具有 .spec.replicas: 5 的 Deployment 在任何时间都应该有 5 个 Pod。 如果 PDB 允许其在某一时刻有 4 个副本,那么驱逐 API 将允许同一时刻仅有一个(而不是两个)Pod 自愿干扰。
使用标签选择器来指定应用程序的一组 pod,这与应用程序的控制器(Deployment、StatefulSet 等)使用的相同。
Pod 控制器的 .spec.replicas 计算“预期的” pod 数量。使用对象的 .metadata.ownerReferences 值从控制器获取。
由于应用程序的滚动升级而被删除或不可用的 Pod 确实会计入中断预算,但控制器(如 Deployment 和 StatefulSet)在进行滚动升级时不受 PDB 的限制——在应用程序更新期间的故障处理是在控制器的规格(spec)中配置
使用驱逐 API 驱逐 pod 时,pod 会被优雅地终止
PDB 示例
假设集群有3个节点,node-1 到 node-3。集群中运行了一些应用,其中一个应用有3个副本,分别是 pod-a、pod-b 和 pod-c。另外,还有一个与它相关的不具有 PDB 的 pod,我们称为之为 pod-x。最初,所有 Pod 的分布如下
所有的3个 pod 都是 Deployment 中的一部分,并且它们共同拥有一个 PDB,要求至少有3个 pod 中的2个始终处于可用状态。
例如,假设集群管理员想要重启系统,升级内核版本来修复内核中的错误。集群管理员首先使用 kubectl drain 命令尝试排除 node-1。该工具试图驱逐 pod-a 和 pod-x。这立即成功。两个 Pod 同时进入终止状态。这时的集群处于这种状态:
Deployment 注意到其中有一个 pod 处于正在终止,因此会创建了一个 pod-d 来替换。由于 node-1 被封锁(cordon),它落在另一个节点上。同时其它控制器也创建了 pod-y 作为 pod-x 的替代品。
对于 StatefulSet,pod-a 将被称为 pod-1,需要在替换之前完全终止,替代它的也称为 pod-1,但是具有不同的 UID
当前集群的状态如下:
在某一时刻,pod 被终止,集群看起来像下面这样子:
此时,如果一个集群管理员试图排空(drain)node-2 或 node-3,drain 命令将被阻塞,因为对于 Deployment 只有2个可用的 pod,并且其 PDB 至少需要2个。经过一段时间,pod-d 变得可用。
现在,集群管理员尝试排空 node-2。drain 命令将尝试按照某种顺序驱逐两个 pod,假设先是 pod-b,然后再 pod-d。它将成功驱逐 pod-b。但是,当它试图驱逐 pod-d 时,将被拒绝,因为这样对 Deployment 来说将只剩下一个可用的 pod。
Deployment 将创建一个名为 pod-e 的 pod-b 的替代品。但是,集群中没有足够的资源来安排 pod-e。那么,drain 命令就会被阻塞。集群最终可能是这种状态:
此时,集群管理员需要向集群中添加回一个节点以继续升级操作。
您可以看到 Kubernetes 如何改变中断发生的速率,根据:
- 应用程序需要多少副本
- 正常关闭实例需要多长时间
- 启动新实例需要多长时间
- 控制器的类型
- 集群的资源能力
分离集群所有者和应用程序所有者角色
将集群管理者和应用程序所有者视为彼此知识有限的独立角色通常是很有用的。这种责任分离在这些情况下可能是有意义的:
- 当有许多应用程序团队共享一个 Kubernetes 集群,并且有自然的专业角色
- 使用第三方工具或服务来自动化集群管理
Pod Disruption Budget 通过在角色之间提供接口来支持这种角色分离。
如果您的组织中没有这样的职责分离,则可能不需要使用 Pod 中断预算。
如何在集群上执行中断操作
如果您是集群管理员,要对集群的所有节点执行中断操作,例如节点或系统软件升级,则可以使用以下选择:
- 在升级期间接受停机时间。
- 故障转移到另一个完整的副本集群。
- 没有停机时间,但是对于重复的节点和人工协调成本可能是昂贵的。
- 编写可容忍中断的应用程序和使用 PDB。
- 没有停机时间。
- 最小的资源重复。
- 允许更多的集群管理自动化。
- 编写可容忍中断的应用程序是很棘手的,但对于可容忍自愿中断,和支持自动调整以容忍非自愿中断,两者在工作上有大量的重叠。