Kubernetes Tips:用 PDB + Surge/Unavailable 做更稳的发布
把 Deployment 滚动升级参数与 PodDisruptionBudget 配合起来,降低发布与节点维护带来的可用性风险。
很多“例行发布变事故”的根因并不是某个单点 bug,而是以下几个参数之间的假设不一致:
- 发布时允许同时下线多少 Pod(Deployment rollingUpdate)
- 维护/驱逐时允许同时被赶走多少 Pod(PDB)
- 实际副本数与容量是否足够(请求资源、节点池 headroom)
- readiness 与优雅下线是否可靠(摘流量是否及时)
这篇 tips 给你一个可落地的组合策略。
Deployment rollingUpdate:最常用的两个旋钮
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
含义:
maxSurge:升级时允许额外创建多少新 Pod(加速替换,但需要容量)maxUnavailable:升级时允许不可用的 Pod 数量(越小越稳,但可能更慢)
对于必须保持可用的服务,maxUnavailable: 0 常常是一个强默认值。
PDB(PodDisruptionBudget):保护“自愿”驱逐
PDB 主要保护:
kubectl drain节点- 平台升级/维护时的 cordon + drain
- 一些自动化维护流程
示例:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: api
注意:PDB 只对 voluntary disruptions 生效,不对节点崩溃、网络分区、内核 OOM 这类“非自愿”故障兜底。
选 minAvailable 还是 maxUnavailable
minAvailable:副本数固定时更直观maxUnavailable:比例型预算更方便(大规模服务)
经典坑:副本数太少 + PDB 太严格
如果你只有 2 个副本,同时 minAvailable: 2:
- drain 节点可能被卡住(一个都不让走)
- 维护会很痛苦
这不是“PDB 不好”,而是你的 SLA、成本、副本数与维护策略需要对齐。
让数字变得清晰:rollout 的“数学题”
例子:8 副本,maxSurge: 25%,maxUnavailable: 0
maxSurge允许额外最多 2 个 Pod(25% of 8)- 理论上可用 Pod 维持 8 个不掉(更稳)
但这依赖两个前提:
- 集群有容量调度出 surge Pod(requests 合理,节点池有余量)
- readiness 真的代表“可接流量”(否则 surge 再多也没用)
如果 surge Pod 因容量不足一直 Pending,rollout 会卡住。
把副本“铺开”:否则 PDB 也救不了你
如果所有副本都落在同一个节点/同一个可用区:
- 一个节点 drain/故障就会把你打到不可用
建议使用 topologySpreadConstraints(现代方式)或 pod anti-affinity。
例如跨可用区分布:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api
“可用性”的另一半:优雅下线
即使 maxUnavailable: 0,你仍可能在发布时看到错误率升高,原因往往是旧 Pod 下线过快:
- readiness 未及时失败,仍在 endpoints 里
- 连接被强行断开(长连接/gRPC/队列 worker)
terminationGracePeriodSeconds太短
实用模式:
- 收到 SIGTERM,服务立刻让 readiness 失败(快速摘流量)
- 等待 in-flight 请求完成(grace period)
- 再退出
和 HPA / Cluster Autoscaler 的交互
rollout 时 surge 会让 Pod 数暂时增多:
- 如果节点有余量,发布会很顺
- 如果没余量,Cluster Autoscaler 可能会扩节点,但节点启动需要时间
同时如果 HPA 正在因流量扩容,也会叠加 surge,造成更大的容量需求。
建议:
- 高峰期减少发布频率或采用 canary
- 让节点池保留一定 headroom
- 确保 requests 准确(否则你以为有余量,实际上没有)
kubectl drain 被卡住怎么办(PDB 视角)
当 drain 时 PDB 阻止驱逐,你通常会看到:
- drain 命令一直等待
- 事件里提示 disruption budget 不允许
这时应检查:
- 副本数是否足够、是否能在其他节点快速重建
- 是否因亲和性/节点选择器太严格导致无法调度
- readiness 是否一直不通过导致可用副本不足
进阶:当 rollingUpdate 不够稳(canary/blue-green)
某些变更风险更高:
- 大版本升级
- 配置大改
- 数据库迁移
可考虑渐进交付:
- canary:先 1% 再 10% 再 50%
- blue/green:两套环境切流量
即使没有完整平台,也可以通过“第二个 Deployment + 流量规则”近似 canary,关键是 把爆炸半径变小。
推荐组合(可直接抄的经验值)
3 副本的典型无状态 API
- Deployment:
maxUnavailable: 0,maxSurge: 1 - PDB:
minAvailable: 2 - 跨 zone 分布(spread constraints)
10+ 副本的大流量服务
- Deployment:
maxUnavailable: 10%,maxSurge: 10% - PDB:用比例预算对齐 rollout(减少“维护卡死”)
- 配合监控与自动回滚策略
结论
PDB + rollout 参数不是“开了就万事大吉”,它们在以下条件成立时效果最好:
- 副本数足够
- 副本分布在不同故障域
- readiness/liveness/优雅下线靠谱
- 节点容量足够(或 autoscaler 配置合理)
把这些前提补齐,发布会明显更稳。
参考链接
FAQ
Q: PDB 保护什么? A: 只保护“自愿中断”(如 drain/升级),不防节点宕机。
Q: 发布卡住怎么办?
A: PDB 太严或 readiness 未就绪会阻塞发布。检查事件并调整 maxUnavailable/minAvailable。
Q: surge/unavailable 怎么配? A: 保证有足够的 surge 来创建新 Pod,同时满足 PDB 的可用性约束。