3-4-7-Pod 保护之 PodDisruptionBudget

熊 0 阅读 容器

1. PDB基础

在 Kubernetes 中 , 为了保证业务不中断或业务SLA不降级 , 需要将应用进行集群化部署。通过PodDisruptionBudget 控制器可以设置应用POD集群处于运行状态最低个数 , 也可以设置应用POD集群处于运行状态的最低百分比 , 这样可以保证在主动销毁应用POD的时候 , 不会一次性销毁太多的应用POD , 从而保证业务不中断或业务SLA不降级。

Pod Disruption Budget (pod 中断 预算) 简称PDB , 含义其实是终止pod前通过 labelSelector 机制获取正常运行的pod数目的限制 , 目的是对自愿中断的保护措施。

pod健康 : 如果 Pod 的 .status.conditions 中包含 type="Ready" 和 status="True" 的项 , 则当前实现将其视为健康的 Pod。这些 Pod 通过 PDB 状态中的 .status.currentHealthy 字段被跟踪。

PDB一般适用于以下场景 :

  • 保障服务可用性 : 在进行计划性维护时 , PDB可以确保至少保持指定数量的Pod在运行 , 从而不影响服务的可用性。

  • 避免突发中断 : PDB可以防止在维护期间出现过多的Pod中断 , 确保应用程序的稳定运行。

自愿中断发生时 , 在短时间内 , 您的应用程序最多可以容许多少个实例被终止。

  • 无状态的前端 :

    • 关注点 : 不能让服务能力 ( serving capacity) 降低超过 10%

    • 解决方案 : 在 PDB 中配置 minAvailable 90%

  • 单实例有状态应用 :

    • 关注点 : 未经同意不能关闭此应用程序

    • 解决方案1 : 不使用 PDB , 并且容忍偶尔的停机

    • 解决方案2 : 在 PDB 中设置 maxUnavailable=0。与集群管理员达成一致 ( 不是通过Kubernetes , 而是邮件、电话或面对面) , 请集群管理员在终止应用之前与你沟通。当集群管理员联系你时 , 准备好停机时间 , 删除 PDB 以表示已准备好应对毁坏。并做后续处理

  • 多实例有状态应用 , 例如 consul、zookeeper、etcd :

    • 关注点 : 不能将实例数降低到某个数值 , 否则写入会失败

    • 解决方案1 : 在 PDB 中设置 maxUnavailable 为 1 ( 如果副本数会发生变化 , 可以使用此设置)

    • 解决方案2 : 在 PDB 中设置 minAvailable 为最低数量 ( 例如 , 当总副本数为 5 时 , 设置为3) ( 可以同时容忍更多的毁坏数)

  • 可以重新开始的批处理任务 :

    • 关注点 : 当发生自愿毁坏时 , Job仍然需要完成其执行任务

    • 解决方案 : 不创建 PDB。Job 控制器将会创建一个 Pod 用于替换被毁坏的 Pod

2. 参数详解

2.1 基础参数说明

PodDisruptionBudget 包含三个字段 :

  • 标签选择器 .spec.selector 用于指定 PDB 适用的 Pod。此字段为必填

  • .spec.minAvailable : 当完成驱逐时 , 最少仍然要保留多少个 Pod 可用。该字段可以是一个整数 , 也可以是一个百分比

  • .spec.maxUnavailable : 当完成驱逐时 , 最多可以有多少个 Pod 被终止。该字段可以是一个整数 , 也可以是一个百分比

在一个 PodDisruptionBudget 中 , 只能指定 maxUnavailable 和 minAvailable 中的一个。 maxUnavailable 只能应用到那些有控制器的 Pod 上。下面的例子中 , “期望的副本数” 是 PodDisruptionBudget 对应 Pod 的控制器的 .spec.replicas 字段 :

例子1 : minAvailable 为 5 时 , 只要 PodDisruptionBudget 的 selector 匹配的 Pod 中有超过 5 个仍然可用 , 就可以继续驱逐 Pod

例子2 : minAvailable 为 30% 时 , 至少保证期望副本数的 30% 可用

例子3 : maxUnavailable 为 5 时 , 最多可以有 5 个副本不可用(unthealthy)

例子4 : maxUnavailable 为 30% 时 , 最多可以有期望副本数的 30% 不可用

通常 , 一个 PDB 对应一个控制器创建的 Pod , 例如 , Deployment、ReplicaSet或StatefulSet。

注 : PodDisruptionBudget 并不能真正确保指定数量 ( 或百分比 ) 的Pod始终保持可用。例如 , 当 Pod 数量已经为 PDB 中指定的最小数时 , 某一个节点可能意外宕机 , 导致 Pod 数量低于 PDB 中指定的数量。 PodDisruptionBudget 只能保护应用避免受到 自愿中断的影响 , 而不是所有原因的毁坏。

maxUnavailable 为 0% ( 或0 ) 或者 minAvailable 为 100% ( 或与控制器的 .spec.replicas 相等 ) 将阻止节点排空任务。按照 PodDisruptionBudget 的语义 , 这种做法是允许的。

下面是两个 PDB 的例子:

使用 minAvailable

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zookeeper

使用 maxUnavailable

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: zookeeper

例如 , 如果 zk-pdb 对象选择的 Pod 对应的 StatefulSet 的 spec.replicas 为 3 , 则两个 PDB 含义相同。推荐使用 maxUnavailable 这种形式的定义 , 因为当控制器的 spec.replicas 发生变化时 , 应用受到的影响更小一些。例如 , 将其副本数伸缩到 10 , 如果使用 minAvailable=2 这种形式 , 则可能会有 8 个 Pod 被毁坏。而如果使用 maxUnavailable=1 这种形式 , 应用程序将可以保存 9 个可用实例。

2.2 百分比的舍入逻辑

minAvailable 或 maxUnavailable 可以指定为整数或者百分比。

  • 当指定一个整数时 , 代表 Pod 的数量。例如 , 设置 minAvailable 为 10 , 则至少 10 个 Pod 必须始终可用 , 即便是在毁坏发生时

  • 当指定一个百分比时 ( 例如 , 50% ) , 代表总 Pod 数量的一个百分比。例如 , 设置 maxUnavailable 为 50% , 则最多可以有 50% 的 Pod 可以被毁坏

如果指定这些值为一个百分数 , 其计算结果可能不会正好是一个整数。例如 , 假设有 7 个 Pod , minAvailable 设置为 50% , 你将很难判断 , 到底是 3 个还是 4 个 Pod 必须始终保持可用。Kubernetes 将向上舍入(round up to the nearest integer) , 因此 , 此处必须有 4 个 Pod 始终可用。

3. PDB 实际使用

你可以使用 kubectl 创建或更新 PDB 对象。

kubectl apply -f mypdb.yaml

使用 kubectl 来确认 PDB 被创建。

假设用户的名字空间下没有匹配 app: zookeeper 的 Pod , 用户会看到类似下面的信息:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               0                     7s

假设有匹配的 Pod(比如说3 个) , 那么用户会看到类似下面的信息:

kubectl get poddisruptionbudgets
NAME     MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
zk-pdb   2               N/A               1                     7s

ALLOWED DISRUPTIONS 值非 0 意味着干扰控制器已经感知到相应的 Pod , 对匹配的 Pod 进行统计 , 并更新了 PDB 的状态。

用户可以通过以下命令获取更多 PDB 状态相关信息:

kubectl get poddisruptionbudgets zk-pdb -o yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  annotations:
…
  creationTimestamp: "2020-03-04T04:22:56Z"
  generation: 1
  name: zk-pdb
…
status:
  currentHealthy: 3
  desiredHealthy: 2
  disruptionsAllowed: 1
  expectedPods: 3
  observedGeneration: 1

4. unhealthyPodEvictionPolicy

守护应用程序的 PodDisruptionBudget 通过不允许驱逐健康的 Pod 来确保 .status.currentHealthy 的 Pod 数量不低于 .status.desiredHealthy 中指定的数量。通过使用 .spec.unhealthyPodEvictionPolicy , 你还可以定义条件来判定何时应考虑驱逐不健康的 Pod。未指定策略时的默认行为对应于 IfHealthyBudget 策略。

策略包含:

  • IfHealthyBudget

    对于运行中但还不健康的 Pod (.status.phase="Running") , 只有所守护的应用程序不受干扰 ( .status.currentHealthy 至少等于 .status.desiredHealthy) 时才能被驱逐。

    此策略确保已受干扰的应用程序所运行的 Pod 会尽可能成为健康。 这对腾空节点有负面影响 , 可能会因 PDB 守护的应用程序行为错误而阻止腾空。 更具体地说 , 这些应用程序的 Pod 处于 CrashLoopBackOff 状态 ( 由于漏洞或错误配置) 或其 Pod 只是未能报告 Ready 状况。

  • AlwaysAllow

    运行中但还不健康的 Pod ( .status.phase="Running") 将被视为已受干扰且可以被驱逐 , 与是否满足 PDB 中的判决条件无关。

    这意味着受干扰的应用程序所运行的 Pod 可能没有机会恢复健康。 通过使用此策略 , 集群管理器可以轻松驱逐由 PDB 所守护的行为错误的应用程序。 更具体地说 , 这些应用程序的 Pod 处于 CrashLoopBackOff 状态 ( 由于漏洞或错误配置) 或其 Pod 只是未能报告 Ready 状况。

说明:处于 Pending、Succeeded 或 Failed 阶段的 Pod 总是被考虑驱逐。