K8s:通过 Pod 干扰预算(PDB)提高节点故障、维护期间 Pod 频繁调度时工作负载的可用性
不念过去,不畏将来,不负余生
写在前面
- 分享一些
Pod Disruption Budgets(PDB)
的笔记 - 博文内容涉及:
- 为什么需要
PDB
? - 什么是
PDB
? PDB
可以做什么?- 配置创建
PDB
Demo
- 为什么需要
- 理解不足小伙伴帮忙指正
不念过去,不畏将来,不负余生
为什么需要 PDB
对于对高可用
要求很高的一些容器化应用,例如一些有状态
的工作负载,比如数据库,分布式协调服务等, K8s 集群中 Pod 频繁的调度是不能容忍的一件事。 尤其涉及到应用集群数据同步,共识,心跳
等诸多因素. 容易造成可用性降低,数据延迟甚至潜在的数据丢失。
集群中的 Pod 正常情况下不会频繁的调度,即使存在大量的超售超用,也可以通过 Qos 等手段在准入的时候控制。当然,除非有人操作,或者节点故障等一些因素的干扰。
在 k8s 中,我们把这些干扰分为两类,自愿干扰和非自愿干扰
:
非自愿干扰(Involuntary Disruptions)
的情况常见下面一些场景:
- 节点下层物理机的硬件故障
- 集群管理员错误地删除虚拟机(实例)
- 云提供商或虚拟机管理程序中的故障导致的虚拟机消失
- 内核错误
- 节点由于集群网络隔离从集群中消失
- 由于节点资源不足导致 pod 被驱逐。
自愿干扰(Voluntary Disruptions)
的情况常见下面一些场景:
- 排空(drain)节点进行修复或升级。
- 从集群中排空节点以缩小集群。
- 从节点中移除一个 Pod,以允许其他 Pod 使用该节点。
- 修改了工作负载调度规则(策略)
对于非自愿干扰
,常常是不可避免的,能做的只能是减轻, k8s 官方提供的一些非自愿干扰的方法:
- 确保 Pod 在请求中给出所需资源。
- 如果需要更高的可用性,考虑增加合适的副本
- 为了在运行复制应用时获得更高的可用性,请
跨机架
(使用 反亲和性) 或跨区域
(如果使用多区域集群)扩展应用,考虑拓扑分布。
对于 自愿干扰
来讲,可能由集群管理员直接执行,也可能由集群管理员所使用的自动化工具执行,或者由集群托管云厂商 自动执行。 k8s 提供了Pod干扰预算(PDB)
来 解决这些问题,支持运行高度可用的应用。
PDB 是什么?
用最简单的话描述,Pod Disruption Budgets(PDB)
是 K8s 中的一项功能,可以确保在进行维护、升级或扩展集群
等自愿操作时,不会影响应用程序的稳定性
,从而提高可用性。
PDB 是确保 K8s 环境中高可用性的强大功能,强烈建议在生产环境中使用。
简单介绍一下 PDB
的发展历程
- Pod Disruption Budgets 首次引入在2016年 Kubernetes
v1.4
版本中,开始作为Alpha
版本提供 - 2017 年 Kubernetes
v1.6
版本中被标记为Beta
版本,使其更易于使用 - 到了 Kubernetes
v1.8
,PDB 增加了更多的功能,包括针对故障域的限制和管理多个 Pod 组合的能力。 - 在 Kubernetes
v1.14
中,引入了 TopologySpreadConstraints API(拓扑分布约束),以更精确地控制 Pod 的分布,并进一步完善了 PDB 的功能。 - 经过长时间的测试和验证后,在 Kubernetes
v1.21
版本中,PDB 被标记为stable
版本。这意味着其 API 已经稳定,并且与未来版本兼容
PDB 可以做什么?
可以为每个工作负载(deployment或者statefulSet)
创建一个 PodDisruptionBudget(PDB)
。
PDB 将限制在同一时间因自愿干扰导致的多副本应用中发生宕机的 Pod 数量
。
例如:
- 基于
选举投票机制
的应用集群希望确保运行中的副本数永远不会低于票选所需的数量。 Web 前端
可能希望确保提供负载的副本数量
永远不会低于总数的某个百分比。
PDB 可以指定工作负载可以容忍的副本数量
(相当于应该有多少副本)。
例如,具有 .spec.replicas: 5
的 Deployment 在任何时间都应该有 5 个 Pod。 如果 PDB
允许其在某一时刻有 4 个副本,那么驱逐 API 将允许同一时刻仅有一个(而不是两个)Pod 自愿干扰
。这里和 滚动升级机制
的优先级需要考虑一下。
由于应用的滚动升级而被删除或不可用的 Pod 确实会计入干扰预算
, 但是工作负载资源
(如 Deployment 和 StatefulSet) 在进行滚动升级时不受 PDB 的限制
。 应用更新期间的故障处理方式是在对应的工作负载资源的 spec
中配置的。
一些自愿干扰场景中使用PDB分析
确定在自发干扰时,多少实例可以在短时间内同时关闭。其中 minAvailable
表示最小活跃 pod。maxUnavailable
表示最大不活跃 Pod ,可以表示为整数或百分比
无状态的前端
:
- 关注:不能降低服务能力 10% 以上。解决方案:例如,使用 PDB,指定其
minAvailable
值为 90%。
单实例有状态应用
:
- 关注:不要在不通知的情况下终止该应用。解决方案
- 不使用 PDB,并忍受偶尔的停机。
- 设置
maxUnavailable=0
的 PDB。 意为(Kubernetes 范畴之外的)集群操作人员需要在终止应用前与用户协商, 协商后准备停机,然后删除 PDB 表示准备接受干扰,后续再重新创建。
多实例有状态应用
,如 Consul、ZooKeeper 或 etcd:
- 关注:不要将实例数量减少至低于
仲裁规模
,否则将出现写入失败。解决方案- 设置
maxUnavailable
值为 1 (适用于不同规模的应用)。 - 设置
minAvailable
值为仲裁规模(例如规模为 5 时设置为 3)。 (允许同时出现更多的干扰)。
- 设置
可重新启动的批处理任务:
- 关注:自发干扰的情况下,需要确保任务完成。解决方案:不创建 PDB。 任务控制器会创建一个替换 Pod。
配置创建 PDB
用 PodDisruptionBudget 来保护应用的一般步骤:
- 确定想要使用 PodDisruptionBudget (PDB) 来保护的应用。
- 考虑应用对干扰的反应。
- 以 YAML 文件形式定义 PDB。
- 通过 YAML 文件创建 PDB 对象
想要保护通过内置的 Kubernetes 控制器指定的应用,这是最常见的使用场景,支持下面一些控制器
- Deployment
- ReplicationController
- ReplicaSet
- StatefulSet
minAvailable
或 maxUnavailable
的值可以表示为整数或百分比。需要考虑指定百分比时的舍入逻辑:
- 指定整数值时,它表示 Pod 个数。
- 设置为百分比的字符串表示形式(例如 “50%”)来指定百分比时,它表示占总 Pod 数的百分比。
如果将值指定为百分比,则可能无法映射到确切数量的 Pod。 Kubernetes 采用向上取整
到最接近的整数的办法。
一个 PodDisruptionBudget 有 3 个字段:
.spec.selector
用于指定其所作用的 Pod 集合,该字段为必需字段。.spec.minAvailable
表示驱逐后仍须保证可用的 Pod 数量。即使因此影响到 Pod 驱逐 (即该条件在和 Pod 驱逐发生冲突时优先保证)。.spec.maxUnavailable
(Kubernetes 1.7 及更高的版本中可用)表示驱逐后允许不可用的 Pod 的最大数量。其值可以是绝对值或是百分比。
注意事项:
- 用户在同一个
PodDisruptionBudget
中只能够指定maxUnavailable
和minAvailable
中的一个。 maxUnavailable
只能够用于控制存在相应控制器
的 Pod 的驱逐(即不受控制器控制的 Pod 不在maxUnavailable
控制范围内)。- 如果将
maxUnavailable
的值设置为 0%(或 0)或设置 minAvailable 值为 100%(或等于副本数) 则会阻止所有的自愿驱逐。将无法成功地腾空(drain )运行其中一个 Pod 的节点.
在下面的示例中, “所需副本” 指的是相应控制器的 scale
,控制器对 PodDisruptionBudget 所选择的 Pod 进行管理。
示例 1:设置 minAvailable 值为 5 的情况下,驱逐时需保证 PodDisruptionBudget 的 selector 选中的 Pod 中 5 个或 5 个以上处于健康状态。
1 | apiVersion: policy/v1 |
示例 2:设置 minAvailable 值为 30% 的情况下,驱逐时需保证 Pod 所需副本的至少 30% 处于健康状态。
1 | apiVersion: policy/v1 |
示例 3:设置 maxUnavailable 值为 1 的情况下,驱逐时需保证所需副本中最多 1 个处于不可用状态。
1 | apiVersion: policy/v1 |
示例 4:设置 maxUnavailable 值为 30% 的情况下,只要不健康的副本数量不超过所需副本总数的 30% (取整到最接近的整数),就允许驱逐。如果所需副本的总数仅为一个
,则仍允许该单个副本中断, 从而导致不可用性实际达到 100%。
1 | apiVersion: policy/v1 |
一个 Demo
1 | ┌──[root@vms100.liruilongs.github.io]-[~] |
1 | ┌──[root@vms100.liruilongs.github.io]-[~] |
1 | ┌──[root@vms100.liruilongs.github.io]-[~] |
1 | ┌──[root@vms100.liruilongs.github.io]-[~] |
部分字段解释:
- maxUnavailable: 指定在维护期间可以离线的 Pod 最大数量。
- selector: 定义受 PodDisruptionBudget 约束的 Pod 集合。
- status: 包含当前 PodDisruptionBudget 的状态信息。
- conditions: 描述当前是否允许进行 Pod 离线的状态(例如,在维护期间)。
- currentHealthy: 当前正在运行的与选择器匹配的健康 Pod 数量。
- desiredHealthy: PodDisruptionBudget 想要保持的最小健康 Pod 数量。
- disruptionsAllowed: 在当前时间段内允许的 Pod 离线次数。
- expectedPods: 根据选择器计算出来的应该存在的 Pod 数量。
- observedGeneration: 用于检测 PodDisruptionBudget 对象是否已被更新。如果观察到的世代与实际世代不同,则会标记为过时状态。
使用 Pod 干扰预算有几个好处,包括:
保持高可用性
:PDB 确保始终提供最少数量的副本,这有助于在节点维护或故障期间保持关键工作负载的高可用性。自动管理中断
:PDB 可在节点维护或故障期间自动管理工作负载中断,从而减少手动干预的需要。改进了集群稳定性
:通过防止同时中断太多副本,PDB 可以帮助提高 Kubernetes 群集的稳定性。节约成本
:通过确保关键工作负载的高可用性,PDB 可以帮助减少停机时间和数据丢失,这可能会给企业带来高昂的代价。
关于 Pod 干扰预算(PDB) 和小伙伴们分享到这里,这里只是简单介绍了 PDB,对于原理没有太多介绍,k8s 官网有一个有趣的例子,篇幅问题没有整理,感兴趣可以去看看,大概意思说, 通过 PDB 限制,k8s 可能会在某些时间进入阻塞状态,延迟对一些 API 调用(命令)的响应,等到符合 PDB 限制,在做成响应,优雅的处理自愿干扰对 Pod 副本的影响,重而改变干扰发生的速率,Pod 调度的影响。
关于 K8s 如何改变干扰发生的速率,除了 PDB ,还要考虑下面一些因素:
- 应用需要多少个副本
- 优雅关闭应用实例需要多长时间
- 启动应用新实例需要多长时间
- 控制器的类型
- 集群的资源能力
值得一说的是 ,干扰预算并不能真正保证指定数量/百分比的 Pod 一直处于运行状态,
预算只能够针对自发的驱逐提供保护
,而不能针对所有 Pod不可用的诱因
。
例如:当 Pod 集合的规模处于预算指定的最小值时,承载集合中某个 Pod 的节点发生了故障,这样就导致集合中可用 Pod 的数量低于预算指定值。
生活加油哈 ^_^
网易云看到一句话,蛮喜欢… 我曾以为总有一天,我能够改变一些什么,可当我看见她渐渐淹没在人海的时候,我才明白,我们这一生除了相遇大概注定一事无成了。
博文部分内容参考
© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知,这是一个开源项目,如果你认可它,不要吝啬星星哦 :)
https://kubernetes.io/zh-cn/docs/tasks/run-application/configure-pdb/
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/disruptions/
https://medium.com/geekculture/kubernetes-pod-disruption-budgets-pdb-b74f3dade6c1
© 2018-2023 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)
K8s:通过 Pod 干扰预算(PDB)提高节点故障、维护期间 Pod 频繁调度时工作负载的可用性
https://liruilongs.github.io/2023/05/06/K8s/环境部署-运维/K8s:通过中断干扰预算-PDB-提高节点故障、维护期间Pod频繁调度时工作负载的可用性/
1.K8s 集群高可用master节点ETCD全部挂掉如何恢复?
2.K8s 集群高可用master节点故障如何恢复?
3.K8s 镜像缓存管理 kube-fledged 认知
4.K8s集群故障(The connection to the server <host>:<port> was refused - did you specify the right host or port)解决
5.关于 Kubernetes中Admission Controllers(准入控制器) 认知的一些笔记
6.K8s Pod 创建埋点处理(Mutating Admission Webhook)
7.关于AI(深度学习)相关项目 K8s 部署的一些思考
8.K8s Pod 安全认知:从openshift SCC 到 PSP 弃用以及现在的 PSA