Kubernetes 基础体系 · 第 58/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
PodDisruptionBudget 与可用性:自愿中断、Eviction、滚动和误区
PodDisruptionBudget(PDB)是 Kubernetes 中用于约束自愿中断的策略对象。它回答的问题不是“这个应用是否高可用”,而是:
当 Kubernetes 或运维工具主动尝试删除某个 Pod 时,当前是否至少保留足够多的健康副本?
PDB 主要影响节点维护、集群扩缩容、Cluster Autoscaler 等通过 Eviction API 发起的 Pod 驱逐;它不会阻止所有类型的 Pod 消失,也不会替代 Deployment、Service、探针、跨可用区部署或应用自身的容错设计。
一、先区分三个概念:可用性、中断和删除
1. Pod 可用不等于 Pod 存在
对 PDB 而言,一个 Pod 是否“健康”主要由 Pod 的 Ready 状态决定。一个处于 Running 但尚未通过 readiness probe 的 Pod,通常不能作为 PDB 的健康副本计数。
因此下面几种状态不同:
| Pod 状态 | 是否通常计入 PDB 的 currentHealthy |
|---|---|
Running 且 Ready |
是 |
Running 但未 Ready |
否 |
| 正在启动、尚未 Ready | 否 |
| 已进入删除流程 | 通常不作为可安全中断的健康副本 |
| 容器进程仍在运行但 Ready 已变为 False | 否 |
PDB 保护的是 Kubernetes 观察到的健康副本数量,而不是业务真正能处理的请求数。例如,应用虽然通过 readiness probe,但数据库连接池已经耗尽,PDB 并不知道这一点。
2. 中断分为自愿中断和非自愿中断
**自愿中断(voluntary disruption)**是某个组件或操作主动尝试让 Pod 终止,例如:
kubectl drain为节点维护驱逐 Pod;- Cluster Autoscaler 为缩容节点驱逐 Pod;
- 集群管理员或自动化系统通过 Eviction API 删除 Pod;
- 某些集群管理组件主动迁移工作负载。
**非自愿中断(involuntary disruption)**是 Pod 因故障而消失,例如:
- 节点宕机;
- 节点失联或网络分区;
- 内核崩溃;
- 容器运行时异常;
- 节点资源压力导致 kubelet 驱逐;
- 底层虚拟机或云主机故障。
PDB 主要约束前者。它不能保证节点突然断电时仍有足够副本,也不能阻止 kubelet 因本地磁盘或内存压力采取回收动作。
可以把 PDB 理解成“主动维护时的中断预算”,而不是“副本永远不会减少”的保证。
二、PDB 的核心模型:可用副本、期望副本和预算
一个 PDB 通过 selector 找到一组 Pod,并声明其中至少要保留多少健康 Pod,或者最多允许多少 Pod 同时因自愿中断而不可用。
两种写法二选一:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
namespace: demo
spec:
minAvailable: 2
selector:
matchLabels:
app: web
或者:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
namespace: demo
spec:
maxUnavailable: 1
selector:
matchLabels:
app: web
minAvailable 和 maxUnavailable 不能同时设置。
设:
- :PDB 选择到的期望 Pod 数量;
- :当前被认为健康的 Pod 数量;
- :期望保留的健康 Pod 数量;
- :当前允许的自愿中断数量。
对于 minAvailable:
对于 maxUnavailable:
预算近似为:
直觉是:如果当前健康副本比最低要求多 1 个,就允许主动中断 1 个;如果刚好达到最低要求,就不允许再主动中断。
PDB 状态中常见的字段对应如下:
status:
currentHealthy: 3
desiredHealthy: 2
expectedPods: 3
disruptionsAllowed: 1
这里表示:
- 选中的 Pod 总数为 3;
- 当前健康 Pod 为 3;
- 至少需要保留 2 个健康 Pod;
- 当前还允许 1 个 Pod 进行自愿中断。
disruptionsAllowed 是控制 Eviction 是否可以继续的关键状态,但它由控制器异步计算,不应被当作一个实时事务锁来理解。Eviction 处理过程中还会使用状态更新和并发控制,避免多个驱逐请求简单地同时消费同一个预算。
三、完整算例:minAvailable 如何限制驱逐
假设 Deployment 有 3 个副本:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: demo
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
namespace: demo
spec:
minAvailable: 2
selector:
matchLabels:
app: web
初始状态:
| 变量 | 数值 |
|---|---|
expectedPods |
3 |
currentHealthy |
3 |
desiredHealthy |
2 |
disruptionsAllowed |
1 |
第一次驱逐
第一个 Eviction 请求到达时,PDB 允许它继续。假设 Pod web-abc 开始终止:
- 该 Pod 不再作为可用副本;
currentHealthy可能变为 2;desiredHealthy仍为 2;- 后续预算变为 0。
此时第二个驱逐请求会被拒绝,常见结果是 HTTP 429 Too Many Requests,客户端通常会看到类似“would violate the pod's disruption budget”的错误。
新 Pod 恢复后
如果 Deployment 创建的新 Pod 通过 readiness probe:
expectedPods回到 3;currentHealthy回到 3;disruptionsAllowed回到 1。
因此,PDB 不会“一次性批准所有要维护的 Pod”,而是随着健康副本恢复,逐步释放新的中断预算。
反例:已有一个 Pod 不健康
如果 3 个 Pod 中有一个尚未 Ready:
| 变量 | 数值 |
|---|---|
expectedPods |
3 |
currentHealthy |
2 |
desiredHealthy |
2 |
disruptionsAllowed |
0 |
即使实际上仍有两个 Pod 可以响应请求,PDB 也不会再批准自愿驱逐,因为它已经达到了最低健康数。这个行为是有意的:节点维护不应继续放大一个已经存在的可用性风险。
四、百分比配置的取整会改变结果
minAvailable 和 maxUnavailable 可以使用整数或百分比字符串:
spec:
minAvailable: 67%
对于百分比,当前 Kubernetes 文档定义的取整行为可能使小规模工作负载比直觉更严格或更宽松。以 3 个副本为例:
67% × 3 = 2.01
minAvailable 向上取整为 3,因此它实际上要求 3 个 Pod 都健康,等价于暂时不允许任何一个 Pod 被自愿中断。
同样:
spec:
maxUnavailable: 50%
对于 3 个副本:
50% × 3 = 1.5
按当前 API 语义向上取整为 2,这意味着最多允许 2 个 Pod 不可用。对小规模工作负载而言,这可能比作者预期的“允许一半”更宽松。
因此,对于 1~3 个副本的关键服务,整数配置通常更容易审计:
spec:
minAvailable: 2
比下面这种配置更明确:
spec:
minAvailable: 67%
百分比适合副本数变化范围较大的工作负载,但应先把不同副本数下的实际整数结果算出来。
五、Eviction 是什么:不是普通的 DELETE
Kubernetes 的 Eviction 是一个专门的 API 子资源,用于请求优雅驱逐 Pod。典型对象如下:
apiVersion: policy/v1
kind: Eviction
metadata:
name: web-abc
namespace: demo
可以使用 kubectl 创建它:
kubectl create -f eviction.yaml
也可以直接使用:
kubectl delete pod web-abc -n demo
但二者语义不同:
Eviction会经过 PDB 检查;- 普通删除请求并不是同一个 PDB 保护路径;
- Eviction 成功通常表示“驱逐请求被接受”,不一定表示 Pod 已经立刻消失;
- Pod 仍会遵守终止流程和
terminationGracePeriodSeconds。
一个完整的 Eviction 路径可以概括为:
sequenceDiagram
participant O as 操作员或控制器
participant A as API Server
participant P as PDB/准入逻辑
participant K as Kubelet
participant C as 容器进程
O->>A: 创建 Pod Eviction
A->>P: 检查匹配的 PDB 与当前预算
alt disruptionsAllowed > 0
P-->>A: 允许并记录中断意图
A-->>O: 接受请求
A->>K: Pod 进入删除流程
K->>C: 发送终止信号
C-->>K: 退出或等待宽限期
K-->>A: Pod 状态更新
else 预算不足
P-->>A: 拒绝,通常返回 429
A-->>O: 驱逐失败
end
实际实现涉及 API Server、PDB 控制器、Pod 状态更新和并发协调,不能把图理解为一个完全同步的单线程事务。多个请求可能同时到达,控制面会通过资源版本、状态更新和驱逐记录尽量避免超额批准;客户端仍应正确处理 429、冲突、超时和 Pod 已经被删除等情况。
六、kubectl drain 如何使用 PDB
节点维护通常先标记节点不可调度,再驱逐已有 Pod:
kubectl cordon worker-1
kubectl drain worker-1 \
--ignore-daemonsets \
--delete-emptydir-data
1. cordon 做什么
kubectl cordon worker-1
会把节点设置为不可调度。新的普通 Pod 不会再被调度到该节点,但它不会自动迁移已经运行的 Pod,也不会自动执行驱逐。
因此:
cordon ≠ drain
cordon 是阻止新增调度,drain 才是尝试清空节点上的可驱逐工作负载。
2. drain 做什么
kubectl drain 会:
- 标记或确认节点不可调度;
- 找出节点上的 Pod;
- 跳过或处理 DaemonSet、镜像 Pod、裸 Pod 等特殊情况;
- 对可驱逐 Pod 使用 Eviction API;
- 等待 Pod 终止;
- 如果 PDB 不允许,等待或报错,而不是强行忽略预算。
如果命令因为 PDB 失败,常见现象包括:
error when evicting pods/...:
Cannot evict pod as it would violate the pod's disruption budget.
这不表示节点不可维护,而是表示当前维护速度超过了应用声明的中断预算。
3. 常见参数的含义和风险
kubectl drain worker-1 \
--ignore-daemonsets \
--delete-emptydir-data
--ignore-daemonsets:忽略由 DaemonSet 管理的 Pod。DaemonSet 通常每个节点都需要一个副本,不能按普通工作负载迁移。--delete-emptydir-data:允许删除使用emptyDir的 Pod。emptyDir中的数据随 Pod 删除而丢失。--force:允许处理没有控制器管理的裸 Pod,可能导致这些 Pod 不会自动恢复。--disable-eviction:使用普通删除而不是 Eviction,从而绕过 PDB。该选项具有明显的可用性风险,不应作为“让 drain 通过”的默认手段。
kubectl drain --dry-run=client 只能检查命令参数和本地客户端逻辑,不能模拟真实控制面中的 PDB 竞争、Pod Ready 状态和节点上的全部对象。生产维护前仍要观察实际 PDB 状态和工作负载状态。
七、PDB 与 Deployment 滚动更新不是同一个控制器
这是最容易混淆的部分之一。
Deployment 的滚动更新由 Deployment 控制器根据以下参数协调:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
maxSurge:更新期间允许超过期望副本数的额外 Pod 数量;maxUnavailable:滚动更新期间允许不可用的 Pod 数量。
例如,期望副本数为 3,配置:
maxSurge: 1
maxUnavailable: 0
典型过程是:
旧版本 3,新版本 0
旧版本 3,新版本 1,暂时总数 4
新版本 Pod Ready
旧版本 2,新版本 1
新版本 Pod Ready
旧版本 1,新版本 2
新版本 Pod Ready
旧版本 0,新版本 3
Deployment 的 maxUnavailable 控制的是滚动更新算法,PDB 的 minAvailable 控制的是通过 Eviction API 发起的自愿中断。它们名称相似,但作用域不同。
PDB 不会自动阻止普通滚动更新
Deployment 控制器在滚动更新时会调整 ReplicaSet 的副本数,并删除旧版本 Pod。这个流程不是把每个 Pod 都包装成 Eviction 请求,因此 PDB 不是滚动更新的主保护机制。
例如:
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
这允许 Deployment 在更新过程中最多有 1 个副本不可用。即使 PDB 写成:
spec:
minAvailable: 3
也不能把 PDB 当成 Deployment 更新的替代控制器。要控制滚动期间的可用副本,应配置 Deployment 的滚动策略,并通过 readiness probe 确保“新 Pod 真正可接流量”后再继续。
反过来,maxUnavailable: 0 也不意味着更新永远不会失败。它可能因以下原因卡住:
- 新版本 Pod 无法通过 readiness probe;
- 节点资源不足,无法满足
maxSurge; - 镜像拉取失败;
- 配置或 Secret 错误;
- 新版本启动后立即崩溃。
滚动更新策略约束“更新控制器如何推进”,并不替应用修复启动错误。
八、PDB 与 Deployment 副本数之间的可行性
PDB 依赖工作负载实际创建出来的 Pod。如果 Deployment 的副本数为 3,但由于调度失败实际只有 2 个 Pod,PDB 不会凭空创建第 3 个 Pod。
假设:
spec:
replicas: 3
PDB:
spec:
minAvailable: 3
当 3 个 Pod 都 Ready 时,自愿驱逐预算为 0。这意味着任何一个节点维护操作都不能安全地驱逐这些 Pod,除非其他副本先增加并变为健康。
如果配置:
spec:
replicas: 3
minAvailable: 2
则在 3 个 Pod 健康时可驱逐 1 个;但在其中一个 Pod 已经故障时,预算为 0。
单副本工作负载的典型误区
spec:
replicas: 1
---
spec:
minAvailable: 1
这不是“保证单副本服务高可用”,而是声明:
这个唯一副本不能被自愿中断。
节点 drain 时,PDB 可能一直阻止驱逐;但节点突然宕机时,PDB 仍然无法保护该 Pod。对于单副本服务,PDB 只能防止主动维护造成停机,不能解决单副本本身没有冗余的问题。
九、PDB 选择器必须准确,重叠选择器会制造意外约束
PDB 通过 spec.selector 选择 Pod:
selector:
matchLabels:
app: web
它只能选择同一命名空间内的 Pod。实际使用时,选择器应尽量只对应一个工作负载的 Pod 集合。
1. 标签选择器写错会导致 PDB 失效
例如 Deployment 使用:
template:
metadata:
labels:
app: web
component: frontend
但 PDB 写成:
selector:
matchLabels:
app: api
那么 PDB 不会匹配目标 Pod。表面上 PDB 对象存在,实际上它没有保护 Web 工作负载。
应检查:
kubectl get pods -n demo -l app=web --show-labels
kubectl describe pdb web-pdb -n demo
重点观察:
kubectl get pdb web-pdb -n demo -o yaml
其中的:
status.expectedPodsstatus.currentHealthystatus.desiredHealthystatus.disruptionsAllowedstatus.conditions
能帮助判断 PDB 是否匹配到了预期对象。
2. 不要让多个 PDB 重叠保护同一组 Pod
假设同一批 Pod 同时匹配两个 PDB:
pdb-a: minAvailable: 2
pdb-b: minAvailable: 3
一个 Eviction 必须同时满足所有相关 PDB 的约束。只要其中一个 PDB 不允许,驱逐就可能失败。
这会产生一种常见故障路径:
- 运维看到某个 PDB 还有预算;
- 另一个未注意到的 PDB 没有预算;
kubectl drain仍然失败;- 操作者误以为 PDB 检查“不稳定”。
PDB 的选择器应避免交叉覆盖,尤其不要让一个“全局标签”选择器和一个“应用标签”选择器同时匹配同一工作负载。
3. 空选择器的版本差异
使用 policy/v1 时,空的 matchLabels 选择器具有选择命名空间内全部 Pod 的语义;旧的 policy/v1beta1 曾存在不同的空选择器行为。由于 policy/v1beta1 已被移除,不应依赖旧版本的解释。
因此生产配置不要使用含义不明显的空选择器:
selector: {}
应明确写出业务标签,并在升级 Kubernetes 或迁移 API 版本时重新检查选择范围。
十、Pod 终止宽限期不会被 PDB 取消
PDB 决定“现在是否允许开始驱逐”,不决定 Pod 被批准后多久退出。
例如:
spec:
terminationGracePeriodSeconds: 60
当 Eviction 被接受后,Pod 仍可能经历:
- Pod 被设置删除时间;
- EndpointSlice 或 Service 流量转发逐步更新;
- kubelet 向容器发送
SIGTERM; - 应用执行连接排空;
- 等待宽限期;
- 若仍未退出,才可能收到
SIGKILL。
如果应用忽略 SIGTERM,或者连接排空时间超过宽限期,即使 PDB 正确生效,也可能出现请求中断。
此外,PDB 只关心 Pod 数量和健康状态,不检查:
- 剩余 Pod 是否分布在不同节点;
- 剩余 Pod 是否位于不同可用区;
- Service 是否有正确的 Endpoint;
- 应用是否具备多副本并发能力;
- 数据库是否允许故障转移;
- 负载是否已经超过剩余副本容量。
因此,PDB 与优雅终止、readiness probe、拓扑分布约束是互补关系,而不是替代关系。
十一、DaemonSet、StatefulSet 和其他控制器的边界
1. DaemonSet
DaemonSet 的目标通常是“每个节点运行一个 Pod”,例如日志采集、网络插件或存储插件。kubectl drain 默认不会删除 DaemonSet Pod,因为即使删除,它也可能立即在同一节点上被重新创建。
通常需要:
kubectl drain worker-1 --ignore-daemonsets
但这只是告诉 drain 忽略 DaemonSet Pod,不表示这些 Pod 已经被迁移。节点维护流程还必须确认:
- DaemonSet 是否在目标节点之外健康运行;
- 网络、存储、日志等节点级组件是否允许该节点暂时缺失;
- 节点恢复后 DaemonSet Pod 是否重新就绪。
2. StatefulSet
StatefulSet 具有稳定身份、持久卷和有序更新特征。PDB 可以约束通过 Eviction API 发起的 Pod 驱逐,但不会自动理解数据库复制协议或主从角色。
例如,一个有 3 个数据库 Pod 的 StatefulSet 即使满足:
minAvailable: 2
也不代表随便驱逐一个 Pod 都安全。被驱逐的可能是当前主节点,业务能否继续取决于数据库本身是否完成故障转移。
3. Job、裸 Pod 和镜像 Pod
- Job 创建的 Pod 是否可重新运行,取决于 Job 的重试和完成语义;
- 裸 Pod 没有控制器负责重建,
drain --force可能导致它永久消失; - 静态 Pod 或镜像 Pod 由 kubelet 管理,不能按普通 Deployment Pod 处理。
PDB 不是所有 Pod 生命周期的统一抽象。执行 drain 前,必须先识别节点上的 Pod 来源:
kubectl get pods -A \
--field-selector spec.nodeName=worker-1 \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,OWNER:.metadata.ownerReferences[0].kind'
十二、unhealthyPodEvictionPolicy:不健康 Pod 是否允许驱逐
当前 policy/v1 PDB 支持 unhealthyPodEvictionPolicy,用于决定不健康 Pod 的 Eviction 行为。常见取值包括:
IfHealthyBudget:默认行为,只有在当前应用健康副本满足预算条件时,才允许驱逐不健康 Pod;AlwaysAllow:允许驱逐不健康 Pod。
示例:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
namespace: demo
spec:
minAvailable: 2
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
app: web
这个选项解决的是一种实际矛盾:
- 一个 Pod 已经不 Ready;
- 由于 PDB 的健康统计,驱逐预算为 0;
- 节点 drain 无法移除这个本来就不能提供服务的 Pod;
- 它又可能阻碍节点维护。
AlwaysAllow 允许在这类场景下驱逐不健康 Pod,但它不是无条件安全开关。若 readiness probe 配置错误,Pod 可能被错误判定为不健康;如果应用本身正在短暂恢复,驱逐也可能进一步放大故障。
该字段存在版本可用性差异,使用前应检查目标集群的 Kubernetes 版本和 policy/v1 API 文档。不能因为控制面支持该字段,就假设所有云厂商托管节点组件的行为完全相同。
十三、滚动更新失败与 PDB 阻塞的诊断顺序
当更新或节点维护卡住时,先判断是哪一种控制路径。
场景一:Deployment 滚动更新卡住
查看 Deployment 和 ReplicaSet:
kubectl rollout status deployment/web -n demo
kubectl describe deployment web -n demo
kubectl get rs -n demo
kubectl get pods -n demo -l app=web -o wide
重点检查:
- 新 ReplicaSet 是否创建;
- 新 Pod 是否
Ready; - 是否有镜像拉取错误;
- 是否有资源不足或调度失败;
- Deployment 的
maxSurge和maxUnavailable; - 是否触发了
progressDeadlineSeconds。
PDB 通常不是 Deployment 滚动更新卡住的第一诊断对象,因为 Deployment 更新不以 Eviction 作为主要推进机制。
场景二:kubectl drain 被拒绝
查看 PDB:
kubectl get pdb -A
kubectl describe pdb web-pdb -n demo
再查看健康状态:
kubectl get pods -n demo -l app=web \
-o custom-columns='NAME:.metadata.name,READY:.status.containerStatuses[*].ready,PHASE:.status.phase,NODE:.spec.nodeName'
典型推导过程:
PDB minAvailable = 2
匹配 Pod = 3
Ready Pod = 2
disruptionsAllowed = 0
此时继续重试 drain 不会解决问题。应先判断:
- 是否有 Pod 因启动失败而不 Ready;
- 是否有 Pod 正在被删除;
- Deployment 是否正在扩容或滚动更新;
- 资源配额、节点容量或拓扑约束是否阻止新 Pod 创建;
- PDB 选择器是否匹配了错误的 Pod;
- 是否存在多个重叠 PDB。
场景三:预算看起来足够,但 Eviction 仍失败
可能原因包括:
- 另一个 PDB 同时匹配该 Pod;
- 预算状态尚未被控制器更新;
- Pod 已经进入删除流程;
- API Server 返回资源版本冲突;
- Pod 所属控制器和选择器状态正在变化;
- 客户端遇到临时的
429或超时。
因此不能只看一个 PDB 的 disruptionsAllowed,还要确认目标 Pod 匹配到的全部 PDB 和工作负载状态。
十四、回滚和暂停不会绕过 PDB 的语义边界
Deployment 支持暂停、继续和回滚:
kubectl rollout pause deployment/web -n demo
kubectl rollout resume deployment/web -n demo
kubectl rollout undo deployment/web -n demo
kubectl rollout history deployment/web -n demo
这些操作改变的是 Deployment 的 ReplicaSet 协调过程和版本状态,不会把 PDB 变成全局删除拦截器。
例如,回滚过程中:
- Deployment 指向旧版本 ReplicaSet;
- 控制器调整新旧 ReplicaSet 的副本数;
- 旧版本 Pod 创建并等待 Ready;
- 新版本 Pod 逐步缩减;
- Deployment 根据滚动更新参数判断是否继续。
这里的安全边界由 Deployment 更新策略、Pod 就绪状态和副本数共同决定。PDB 主要在节点 drain 或其他 Eviction 路径中发挥作用。
暂停更新也不会冻结所有 Pod 状态变化。已有 Pod 仍可能崩溃、被节点故障影响或被其他操作删除;暂停只是暂停 Deployment 控制器推进更新。
十五、几个常见误区
误区一:PDB 能保证高可用
错误。PDB 只能约束特定的自愿中断路径。它不能防止:
- 节点断电;
- 整个可用区故障;
- 应用自身崩溃;
- Service 配置错误;
- 所有副本错误地调度到同一节点;
- 数据库或外部依赖不可用。
高可用还需要多个副本、合理的拓扑分布、正确的探针、容量余量和应用级故障转移。
误区二:PDB 会保护普通 kubectl delete pod
不能这样假设。直接删除 Pod 与通过 Eviction API 驱逐 Pod 是不同请求路径。测试 PDB 时应使用 Eviction 或 kubectl drain,而不是用普通删除命令得出结论。
误区三:PDB 的 maxUnavailable 等于 Deployment 的 maxUnavailable
名称相同但控制器和语义不同:
- PDB:限制自愿中断;
- Deployment:限制滚动更新过程中的不可用副本。
两者必须分别设计。
误区四:minAvailable: 100% 表示更安全
它表示任何自愿中断都不被允许。在节点维护、集群缩容和故障修复场景中,这可能使工作负载无法迁移,形成运维阻塞。
误区五:PDB 能让单副本应用高可用
单副本加 PDB 只能阻止主动维护删除唯一副本,不能抵抗节点故障,也不能让应用在重启期间继续服务。
误区六:PDB 数字越严格越好
PDB 过于严格时,节点无法 drain,安全补丁和硬件维护可能被长期阻塞。PDB 的约束应与副本数、业务容量、故障域和维护方式共同计算,而不是单独追求更大的 minAvailable。
十六、如何验证 PDB 确实按预期工作
下面给出一个最小验证流程。假设命名空间为 demo,Deployment 为 web,PDB 为 web-pdb。
1. 检查匹配范围
kubectl get pods -n demo -l app=web -o wide
kubectl describe pdb web-pdb -n demo
确认:
- PDB 选择器与 Pod 标签一致;
expectedPods等于预期副本数;- 健康 Pod 数量符合实际 Ready 状态;
disruptionsAllowed与公式推导一致。
2. 检查控制器状态
kubectl get deployment web -n demo
kubectl get rs -n demo -l app=web
kubectl rollout status deployment/web -n demo
如果 expectedPods 不符合预期,不要先调 PDB,先确认 Deployment 是否真正创建了足够副本。
3. 在非生产环境执行驱逐测试
kubectl get pod -n demo -l app=web -o name
kubectl create -f eviction.yaml
如果 PDB 允许:
- Eviction 请求被接受;
- 目标 Pod 进入 Terminating;
- Deployment 创建替代 Pod;
- 新 Pod 通过 readiness probe 后,预算逐渐恢复。
如果 PDB 不允许:
- 请求通常返回
429; - Pod 不应因为这个 Eviction 请求立即被删除;
- PDB 的
disruptionsAllowed通常为 0。
4. 验证恢复,而不是只验证删除
kubectl wait \
--for=condition=Available \
deployment/web \
-n demo \
--timeout=120s
kubectl get pdb web-pdb -n demo
kubectl get endpointslice -n demo -l kubernetes.io/service-name=web
要同时确认:
- Deployment 达到期望可用副本;
- PDB 状态恢复;
- Service 的 EndpointSlice 中存在预期后端;
- 应用实际请求成功。
仅看到 Pod 从 Terminating 消失,不能证明业务已经恢复。
十七、生产设计中的取舍
对于副本数为 的无状态服务,常见的最小约束是:
这允许一次维护一个 Pod,同时要求其余副本健康。但这个推导有前提:
- 单个剩余 Pod 能承受增加的流量;
- 副本分布不集中在同一个故障域;
- 新 Pod 能在可接受时间内启动;
- readiness probe 能准确反映可接流量状态;
- 应用支持优雅终止;
- 维护过程中不会同时发生其他独立故障。
如果副本为 3,设置 minAvailable: 2 并不能抵抗两个节点同时故障;如果三个副本都在同一节点上,也不能抵抗该节点故障。
对于跨节点或跨可用区的高可用,还需要配合:
topologySpreadConstraints;podAntiAffinity;- 节点池和可用区容量;
- 合理的 Deployment
maxSurge; - 资源请求与集群余量;
- Service 和 EndpointSlice 验证;
- 应用级重试、超时与连接排空。
PDB 的价值在于把“维护时最多可以主动损失多少健康副本”明确表达给 Kubernetes,而不是替应用完成整个高可用设计。
结语
PDB 的核心不是“禁止删除 Pod”,而是约束通过 Eviction API 发起的自愿中断。理解它至少需要同时区分四条路径:
PDB:约束自愿驱逐
Eviction:带 PDB 检查的优雅驱逐请求
Deployment:控制 ReplicaSet 和滚动更新
节点故障或压力:可能绕过 PDB 的非自愿中断
使用 PDB 时,应先根据副本数和健康状态计算 desiredHealthy 与 disruptionsAllowed,再检查选择器、Eviction 路径、Deployment 更新策略和节点维护命令。只有把这些机制分开,才能解释为什么一次 drain 会被阻塞、为什么滚动更新仍可能继续、以及为什么 PDB 存在时应用仍可能因节点故障而不可用。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 发布策略:Rolling、Canary、Blue-Green、流量和回滚
- 下一篇:Kubernetes HPA 与 VPA:指标、算法、稳定窗口、冲突和容量
- 延伸:Deployment 与 ReplicaSet:滚动更新、Revision、暂停、回滚和失败
- 延伸:Kubernetes 节点维护:Cordon、Drain、Eviction、DaemonSet 和恢复验证
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论