PDB 避坑指南:3 个配置让你的 K8s 服务在节点排空时也稳如狗
平时你随手一个 kubectl drain,自认为稳得一批,结果业务直接报警——因为同一个服务的所有 Pod 被同时干掉了。我当年第一次遇到这场景时,还在用 Kubernetes 1.15,连 PDB 叫啥都不清楚。后来被运维 leader 怼着脸问:“为啥不做中断预算?” 我才老实去把 PodDisruptionBudget 彻底搞明白。
今天这篇,就专门聊 PDB 里最容易踩坑,但社区里老被一笔带过的部分:自愿中断时,怎么保证最小可用 / 最大不可用的 Pod 数量。我会带上真实可跑的示例,也把我曾经犯的错、学乖了的做法都倒出来。读完你起码能避免半夜被排空节点搞醒的惨剧。
先搞清楚:PDB 挡不住什么
PodDisruptionBudget 只对自愿中断生效。你可能会问,啥叫自愿中断?就是集群管理员主动发起的操作,比如:
kubectl drain排空节点kubectl delete pod手工删 Pod- 集群自动缩放器(cluster-autoscaler)移除节点
- 节点原地升级、打补丁(比如 AWS EKS 的节点滚动更新)
非自愿中断,像节点突然宕机、OOMKill、底层硬件故障,PDB 完全无能为力。这点一定要印在脑子里,不然你会产生“我有 PDB 就万事大吉”的错觉,到时出事还得背锅。
(顺便说一句,早期的 PDB 只能用 minAvailable,后来才支持 maxUnavailable,咱们下面马上细聊。)
PDB 的两种表达方式,我推荐这么选
PDB 通过 .spec 里的两个互斥字段来定义中断预算:
|
字段 |
含义 |
适用场景 |
|
|
最少必须保持可用的 Pod 数 |
你知道服务至少需要几个实例才能抗住流量 |
|
|
最多允许同时不可用的 Pod 数 |
你更关心一次中断能容忍挂掉几个,而不确定总副本数 |
实战中我自己的偏好:能用 maxUnavailable 就别用 minAvailable。尤其当你的 Deployment/HPA 会动态伸缩时,minAvailable 经常让你调来调去。比如业务低峰期副本缩到 2,你写的 minAvailable: 2 直接就卡死,一个节点都排不空,坑爹的很。而 maxUnavailable: 1 无论副本数多少,总能保证至少干掉一个 Pod 后还不突破预算(只要副本 ≥ 2)。
当然,如果你的服务对中断极度敏感,比如 etcd 这种至少需要法定人数(quorum)的,那就老老实实用 minAvailable 算好法定数量。
动手:最小可用示例(minAvailable)
下面是一个完整的 PDB 示例,目标对象是 label 为 app: nginx-demo 的 Pod。这里我要求至少保持 2 个 Pod 可用。
# pdb-min-available.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb-min
spec:
minAvailable: 2 # 至少保持 2 个 Pod 处于 Ready 状态
selector:
matchLabels:
app: nginx-demo
对应的 Deployment,直接起 3 副本方便测试:
# deploy-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.25.4
ports:
- containerPort: 80
把它们扔进集群:
kubectl apply -f deploy-nginx.yaml -f pdb-min-available.yaml
验证 PDB 状态:
kubectl get pdb nginx-pdb-min
正常会输出类似:
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
nginx-pdb-min 2 N/A 1 10s
ALLOWED DISRUPTIONS 为 1,意思是当前最多允许 1 个 Pod 自愿中断。因为 3 个副本,最少要保持 2 个可用,所以中断预算 = 3 - 2 = 1。如果你把副本扩到 5,这个数字会变成 3。
再来:最大不可用示例(maxUnavailable)
我个人线上环境几乎全用这种写法:
# pdb-max-unavailable.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb-max
spec:
maxUnavailable: 1 # 最多 1 个 Pod 可以同时不可用
selector:
matchLabels:
app: nginx-demo
还是同一个 Deployment。看下效果:
kubectl get pdb nginx-pdb-max
输出:
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
nginx-pdb-max N/A 1 1 5s
无论副本是 3 还是 10,ALLOWED DISRUPTIONS 始终是 1。这就是我偏爱它的原因——不用因副本数变化而频繁调整 PDB。
怎么验证它真的生效了?来,直接排空节点试试
纸上谈兵没意思,咱直接模拟 kubectl drain。假设你的 Pod 跑在单节点上(比如 Minikube 只有一个 node),执行:
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
你会看到类似报错:
error when evicting pods/"nginx-demo-xxx" -n "default" (will retry after 5s):
Cannot evict pod as it would violate the pod's disruption budget.
这就是 PDB 在起作用——当驱逐会导致可用 Pod 数量低于 minAvailable,或者不可用数量超过 maxUnavailable 时,API 直接拒绝操作,节点排空被阻塞。
你可以顺便瞄一眼 PDB 事件:
kubectl describe pdb nginx-pdb-max
在 Events 段能看到“eviction was not allowed...”这类记录,很直白。
有个细节,只有通过 Eviction API(/api/v1/namespaces/{ns}/pods/{name}/eviction)发起的驱逐才会被 PDB 拦截,直接 kubectl delete pod 其实也会走 Eviction,除非你用了 --force --grace-period=0,那等于直接从 etcd 里硬删,PDB 完全不理。所以别觉得 PDB 能防住一切删 Pod 操作——它只管“体面的驱逐”。
两个我踩过的坑,你应该能躲过去
坑一:minAvailable 设成和副本数一样,直接堵死
刚用 PDB 时,我在生产环境为一个 3 副本的服务写了 minAvailable: 3。结果集群节点轮转时,kubectl drain 永远卡住,节点一直不可调度。原因很简单:想停掉任何一个 Pod,可用数就变 2,违反预算。这种配置等于禁止任何自愿中断。你想平滑更新节点?门都没有。
正确的姿势:如果你用 minAvailable,务必保证 replicas - minAvailable >= 1。HPA 环境下尤其要注意最小副本数,别在低谷时自动缩到比 minAvailable 还小,那时候 PDB 会把整个服务锁死。
坑二:PDB 没选对 Pod,形同虚设
selector 写错这种事情太低级了,但还真发生过——同事复制 YAML 时 label 没改干净,结果 PDB 对象虽然建好了,EXPECTED 和 HEALTHY 都是 0,完全没起作用。每次驱逐照样把所有 Pod 一波带走。
怎么检查 PDB 是不是真的在工作?很简单,kubectl get pdb 看 HEALTHY 列,如果是 0,赶紧查 selector 和 Pod label 是否匹配。
顺手再提一个进阶玩法:配合 HPA 和自定义驱逐策略
PDB 是静态预算,但有些场景你需要更细粒度的控制。比如有些 Pod 正在处理长连接,被驱逐影响极大。从 1.26 开始,Kubernetes 引入了 Pod 的 DisruptionTarget 条件和 Pod 级别的驱逐策略(比如通过 spec.disruptionBudget 中新的 unhealthyPodEvictionPolicy)。不过目前多数生产环境还保留在旧策略下,你得根据集群版本权衡。
我个人建议,在 1.25 及以前的版本,老老实实配好 PDB,再配合 podAntiAffinity 把 Pod 打散到不同节点,双重保险。毕竟单靠 PDB,没办法阻止整个可用区故障,但至少能让你睡个安稳觉。
收尾
PDB 配置简单到只有几个字段,但它起到的作用是四两拨千斤——用 20 行 YAML 换你半夜不用起来处理业务全挂。再说一次我的准则:动态副本服务,优先 maxUnavailable,强制测试一遍 drain 会不会卡死。 剩下的就是检查 selector、控制预算值别等于副本总数。
最后丢个问题给你:你那边是习惯用 minAvailable 还是 maxUnavailable?有没有被 PDB 摆过一道的经历?评论区聊聊,也算给后来者提个醒。
如果这文章让你省下了一次加班排障,欢迎顺手转发给团队里还在裸奔的小伙伴。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)