3. Node Drain 原理深度解析:kubectl drain 工作流程、PodDisruptionBudget (PDB) 机制、Eviction API
Node Drain(节点腾空)是 Kubernetes 集群运维中最关键的操作之一,尤其在节点升级、缩容或故障修复场景下。理解其底层原理,是避免服务中断、保障集群高可用的前提。本节将深入剖析 kubectl drain 的完整工作流程、PodDisruptionBudget(PDB)的防护机制以及 Eviction API 的调用链路。
3.1 kubectl drain 工作流程
kubectl drain 并非一个原子操作,而是一个由多个步骤组成的协调过程。其核心目标是:将节点上的所有 Pod 安全地迁移到其他节点,同时确保应用的高可用性不受影响。
核心原则:Drain 操作遵循“先驱逐,后封锁”的策略。先尝试优雅地驱逐 Pod,如果驱逐失败(例如 PDB 阻止),则操作会阻塞或失败,而不是强制删除。
3.1.1 完整执行步骤
- 标记节点为不可调度(Cordon): 首先将节点标记为
SchedulingDisabled,阻止新的 Pod 调度到该节点上。 - 驱逐 Pod(Eviction): 遍历节点上的所有 Pod(排除 DaemonSet 管理的 Pod 和镜像 Pod),对每个 Pod 发起 Eviction 请求。
- 等待 Pod 终止: 等待被驱逐的 Pod 达到
Terminating状态并最终被删除。如果 Pod 有terminationGracePeriodSeconds,则会等待该时长。 - 处理 DaemonSet Pod: 默认情况下,
kubectl drain会忽略 DaemonSet Pod。如果使用--ignore-daemonsets=false,则会尝试驱逐它们。 - 完成腾空: 当节点上所有可驱逐的 Pod 都被成功删除后,节点处于“已腾空”状态,此时可以进行升级或下线操作。
实践建议: 在生产环境中,始终使用
kubectl drain --ignore-daemonsets --delete-emptydir-data --force 并配合 PDB 使用。其中 --delete-emptydir-data 用于处理使用 emptyDir 的 Pod,--force 用于强制驱逐不受 PDB 保护的 Pod。
3.1.2 工作流程时序图
下图展示了 kubectl drain 从发起命令到节点腾空完成的完整时序流程:
3.2 PodDisruptionBudget (PDB) 机制
PDB 是 Kubernetes 提供的用于保护应用在自愿中断(如 Drain、缩容)期间高可用性的关键资源。它定义了在任意时刻,一个应用集合中最多可以有多少个 Pod 同时不可用。
3.2.1 PDB 核心概念
| 概念 | 说明 | 示例 |
|---|---|---|
| minAvailable | 指定在中断期间必须保持可用的 Pod 最小数量或百分比 | minAvailable: 3 或 minAvailable: 80% |
| maxUnavailable | 指定在中断期间允许同时不可用的 Pod 最大数量或百分比 | maxUnavailable: 1 或 maxUnavailable: 25% |
| selector | 通过标签选择器关联到一组 Pod | matchLabels: { app: nginx } |
| 自愿中断 | 由集群管理员主动发起的操作,如 Drain、缩容、更新 | kubectl drain、kubectl scale |
| 非自愿中断 | 由硬件故障、内核崩溃等不可控因素导致的中断 | 节点宕机、磁盘损坏 |
重要提示: PDB 仅对自愿中断生效。对于非自愿中断(如节点宕机),PDB 无法阻止 Pod 的丢失。因此,PDB 应与其他高可用机制(如多副本、反亲和性)配合使用。
3.2.2 PDB 工作流程
当 Eviction API 请求到达 API Server 时,API Server 会执行以下 PDB 校验逻辑:
- 查找匹配的 PDB: 根据 Pod 的标签,查找所有匹配的 PDB 对象。
- 计算当前可用 Pod 数: 统计当前处于
Ready状态的 Pod 数量。 - 模拟驱逐后的状态: 假设当前 Pod 被驱逐,重新计算可用 Pod 数。
- 校验约束:
- 如果使用
minAvailable:驱逐后可用 Pod 数必须 ≥minAvailable。 - 如果使用
maxUnavailable:驱逐后不可用 Pod 数必须 ≤maxUnavailable。
- 如果使用
- 返回结果: 如果校验通过,允许驱逐;否则拒绝请求并返回错误。
3.2.3 PDB 配置示例
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: nginx-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: nginx
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: web-server
3.3 Eviction API
Eviction API 是 Kubernetes 提供的用于优雅驱逐 Pod 的标准化接口。它是 kubectl drain 和 PDB 之间的桥梁,也是实现精细化 Pod 生命周期管理的关键。
3.3.1 Eviction API 调用链路
下图展示了从发起 Eviction 请求到 Pod 被删除的完整调用链路:
3.3.2 Eviction API 与直接删除 Pod 的对比
| 特性 | Eviction API | 直接删除 Pod (kubectl delete pod) |
|---|---|---|
| PDB 校验 | 强制校验,违反 PDB 则拒绝 | 不校验,直接删除 |
| 优雅终止 | 支持,遵循 terminationGracePeriodSeconds |
支持,遵循 terminationGracePeriodSeconds |
| 触发方式 | 通过 API 创建 Eviction 子资源 | 直接删除 Pod 对象 |
| 适用场景 | 集群运维、节点升级、缩容 | 故障恢复、强制清理 |
| 安全性 | 高,受 PDB 保护 | 低,可能造成服务中断 |
3.3.3 手动调用 Eviction API
除了 kubectl drain,你也可以通过编程方式直接调用 Eviction API:
# 使用 kubectl 手动触发 Eviction
kubectl evict pod/nginx-pod-xxxxx
# 使用 curl 直接调用 API
curl -X POST \
-H "Content-Type: application/json" \
-d '{"apiVersion":"policy/v1","kind":"Eviction","metadata":{"name":"nginx-pod-xxxxx","namespace":"default"}}' \
https://<api-server>/api/v1/namespaces/default/pods/nginx-pod-xxxxx/eviction
最佳实践: 在自动化运维脚本中,优先使用 Eviction API 而非直接删除 Pod。这样可以确保 PDB 约束得到遵守,避免因批量驱逐导致应用不可用。
3.4 常见问题与排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
Drain 操作卡住,Pod 一直处于 Terminating 状态 |
Pod 的 preStop 钩子执行时间过长或卡死 |
检查 Pod 日志,优化 preStop 逻辑,或使用 --force 强制删除 |
| Drain 失败,提示 PDB 阻止 | PDB 配置过于严格,或集群中可用 Pod 数不足 | 临时调整 PDB 的 minAvailable 或 maxUnavailable,或扩容 Pod 副本数 |
| Eviction API 返回 429 Too Many Requests | 短时间内发起大量 Eviction 请求,触发 API Server 限流 | 增加请求间隔,或使用 --rate-limit 参数控制速率 |
| DaemonSet Pod 未被驱逐 | 默认情况下 kubectl drain 忽略 DaemonSet Pod |
使用 --ignore-daemonsets=false 参数,但需谨慎操作 |
生产环境警告: 切勿在未配置 PDB 的情况下对关键业务 Pod 所在节点执行 Drain 操作。一次失败的 Drain 可能导致整个服务不可用。始终遵循“先配置 PDB,再执行 Drain”的原则。