k8s 入门 6: 干扰预算

本文介绍什么是干扰预算并举了一个例子加以说明,随后介绍设施干扰预算的方法。

什么是干扰

Pod 不会消失,除非有人(用户或控制器)将其销毁,或者出现了不可避免的硬件或软件系统错误。
干扰简单来说就是导致 Pod 消失的因素,可分为自愿的和非自愿的

设置干扰预算在一定程度上提升在 Pod 收到干扰时的可用性保证服务可用性。

非自愿干扰

• 节点下层物理机的硬件故障
• 集群管理员错误地删除虚拟机(实例)
• 云提供商或虚拟机管理程序中的故障导致的虚拟机消失
• 内核错误
• 节点由于集群网络隔离从集群中消失
• 由于节点资源不足导致 pod 被驱逐。

自愿干扰

• 删除 Deployment 或其他管理 Pod 的控制器
• 更新了 Deployment 的 Pod 模板导致 Pod 重启
• 直接删除 Pod(例如,因为误操作)
集群管理员操作包括:
• 排空(drain)节点进行修复或升级。
• 从集群中排空节点以缩小集群(了解集群自动收缩)。
• 从节点中移除一个 Pod,以允许其他 Pod 使用该节点。

应对非自愿干扰

以下是减轻非自愿干扰的一些方法:

• 确保 Pod 在请求中给出所需资源。
• 如果需要更高的可用性,请复制应用。 (了解有关运行多副本的无状态 和有状态应用的信息。)
• 为了在运行复制应用时获得更高的可用性,请跨机架(使用 反亲和性) 或跨区域(如果使用多区域集群)扩展应用。

什么是干扰预算

干扰预算(PDB) 限制在同一时间因自愿干扰导致的多副本应用中发生宕机的 Pod 数量。例如,基于票选机制的应用希望确保运行中的副本数永远不会低于票选所需的数量。 Web 前端可能希望确保提供负载的副本数量永远不会低于总数的某个百分比。

PDB 无法防止非自愿干扰; 但它们确实计入预算。

PodDisruptionBudget 例子

假设集群有 3 个节点,node-1node-3。集群上运行了一些应用。 其中一个应用有 3 个副本,分别是 pod-a,pod-b 和 pod-c。 另外,还有一个不带 PDB 的无关 pod pod-x 也同样显示出来。 最初,所有的 Pod 分布如下:

3 个 Pod 都是 deployment 的一部分,并且共同拥有同一个 PDB,要求 3 个 Pod 中至少有 2 个 Pod 始终处于可用状态。
例如,假设集群管理员想要重启系统,升级内核版本来修复内核中的缺陷。 集群管理员首先使用 kubectl drain 命令尝试腾空 node-1 节点。 命令尝试驱逐 pod-apod-x。操作立即就成功了。 两个 Pod 同时进入 terminating 状态。这时的集群处于下面的状态:

Deployment 控制器观察到其中一个 Pod 正在终止,因此它创建了一个替代 Pod pod-d。 由于 node-1 被封锁(cordon),pod-d 落在另一个节点上。 同样其他控制器也创建了 pod-y 作为 pod-x 的替代品。
(注意:对于 StatefulSet 来说,pod-a(也称为 pod-0)需要在替换 Pod 创建之前完全终止, 替代它的也称为 pod-0,但是具有不同的 UID。除此之外,此示例也适用于 StatefulSet。)

当前集群的状态如下:

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

此时,如果一个急躁的集群管理员试图排空(drain)node-2node-3,drain 命令将被阻塞, 因为对于 Deployment 来说只有 2 个可用的 Pod,并且它的 PDB 至少需要 2 个。 经过一段时间,pod-d 变得可用。

集群状态如下所示:

现在,集群管理员试图排空(drain)node-2。 drain 命令将尝试按照某种顺序驱逐两个 Pod,假设先是 pod-b,然后是 pod-d。 命令成功驱逐 pod-b,但是当它尝试驱逐 pod-d时将被拒绝,因为对于 Deployment 来说只剩一个可用的 Pod 了。

Deployment 创建 pod-b 的替代 Pod pod-e。 因为集群中没有足够的资源来调度 pod-e,drain 命令再次阻塞。集群最终将是下面这种状态:

此时,集群管理员需要增加一个节点到集群中以继续升级操作。

可以看到 Kubernetes 如何改变干扰发生的速率,根据:

• 应用需要多少个副本
• 优雅关闭应用实例需要多长时间
• 启动应用新实例需要多长时间
• 控制器的类型
• 集群的资源能力

如何设置干扰预算

可以设置干扰预算的对象:
• Deployment
• ReplicationController
• ReplicaSet
• StatefulSet

确定在自发干扰时,多少实例可以在短时间内同时关闭。

• 无状态的前端:
• 关注:不能降低服务能力 10% 以上。
• 解决方案:例如,使用 PDB,指定其 minAvailable 值为 90%。
• 单实例有状态应用:
• 关注:不要在不通知的情况下终止该应用。
• 可能的解决方案 1:不使用 PDB,并忍受偶尔的停机。
• 可能的解决方案 2:设置 maxUnavailable=0 的 PDB。 意为(Kubernetes 范畴之外的)集群操作人员需要在终止应用前与用户协商, 协商后准备停机,然后删除 PDB 表示准备接受干扰,后续再重新创建。
• 多实例有状态应用,如 Consul、ZooKeeper 或 etcd:
• 关注:不要将实例数量减少至低于仲裁规模,否则将出现写入失败。
• 可能的解决方案 1:设置 maxUnavailable 值为 1 (适用于不同规模的应用)。
• 可能的解决方案 2:设置 minAvailable 值为仲裁规模(例如规模为 5 时设置为 3)。 (允许同时出现更多的干扰)。
• 可重新启动的批处理任务:
• 关注:自发干扰的情况下,需要确保任务完成。
• 可能的解决方案:不创建 PDB。任务控制器会创建一个替换 Pod。

指定百分比时的舍入逻辑

minAvailable 或 maxUnavailable 的值可以表示为整数或百分比。

• 指定整数值时,它表示 Pod 个数。例如,如果将 minAvailable 设置为 10, 那么即使在干扰期间,也必须始终有 10 个 Pod 可用。

• 通过将值设置为百分比的字符串表示形式(例如 "50%")来指定百分比时,它表示占总 Pod 数的百分比。 例如,如果将 minAvailable 设置为 "50%",则干扰期间至少 50% 的 Pod 保持可用。
如果将值指定为百分比,则可能无法映射到确切数量的 Pod。例如,如果你有 7 个 Pod, 并且你将 minAvailable 设置为 "50%",具体是 3 个 Pod 或 4 个 Pod 必须可用并非显而易见。 Kubernetes 采用向上取整到最接近的整数的办法,因此在这种情况下,必须有 4 个 Pod。 当你将 maxUnavailable 值指定为一个百分比时,Kubernetes 将可以干扰的 Pod 个数向上取整。 因此干扰可以超过你定义的 maxUnavailable 百分比。 你可以检查控制此行为的代码。

指定 PodDisruptionBudget

一个 PodDisruptionBudget 有 3 个字段:
• 标签选择算符 .spec.selector 用于指定其所作用的 Pod 集合,该字段为必需字段。
• .spec.minAvailable 表示驱逐后仍须保证可用的 Pod 数量。即使因此影响到 Pod 驱逐 (即该条件在和 Pod 驱逐发生冲突时优先保证)。 minAvailable 值可以是绝对值,也可以是百分比。
• .spec.maxUnavailable (Kubernetes 1.7 及更高的版本中可用)表示驱逐后允许不可用的 Pod 的最大数量。其值可以是绝对值或是百分比。
用户在同一个 PodDisruptionBudget 中只能够指定 maxUnavailable 和 minAvailable 中的一个。

参考

[1] 干扰
[2] 配置干扰