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 完整执行步骤

  1. 标记节点为不可调度(Cordon): 首先将节点标记为 SchedulingDisabled,阻止新的 Pod 调度到该节点上。
  2. 驱逐 Pod(Eviction): 遍历节点上的所有 Pod(排除 DaemonSet 管理的 Pod 和镜像 Pod),对每个 Pod 发起 Eviction 请求。
  3. 等待 Pod 终止: 等待被驱逐的 Pod 达到 Terminating 状态并最终被删除。如果 Pod 有 terminationGracePeriodSeconds,则会等待该时长。
  4. 处理 DaemonSet Pod: 默认情况下,kubectl drain 会忽略 DaemonSet Pod。如果使用 --ignore-daemonsets=false,则会尝试驱逐它们。
  5. 完成腾空: 当节点上所有可驱逐的 Pod 都被成功删除后,节点处于“已腾空”状态,此时可以进行升级或下线操作。
实践建议: 在生产环境中,始终使用 kubectl drain --ignore-daemonsets --delete-emptydir-data --force 并配合 PDB 使用。其中 --delete-emptydir-data 用于处理使用 emptyDir 的 Pod,--force 用于强制驱逐不受 PDB 保护的 Pod。

3.1.2 工作流程时序图

下图展示了 kubectl drain 从发起命令到节点腾空完成的完整时序流程:

kubectl drain 工作流程时序图 用户 API Server Kubelet 目标 Pod kubectl drain Cordon 节点 标记不可调度 遍历 Pod 列表 创建 Eviction 检查 PDB 约束 PDB 校验通过 发送 SIGTERM 优雅终止 Pod 删除 确认删除 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 校验逻辑:

  1. 查找匹配的 PDB: 根据 Pod 的标签,查找所有匹配的 PDB 对象。
  2. 计算当前可用 Pod 数: 统计当前处于 Ready 状态的 Pod 数量。
  3. 模拟驱逐后的状态: 假设当前 Pod 被驱逐,重新计算可用 Pod 数。
  4. 校验约束:
    • 如果使用 minAvailable:驱逐后可用 Pod 数必须 ≥ minAvailable。
    • 如果使用 maxUnavailable:驱逐后不可用 Pod 数必须 ≤ maxUnavailable。
  5. 返回结果: 如果校验通过,允许驱逐;否则拒绝请求并返回错误。

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 被删除的完整调用链路:

Eviction API 调用链路 客户端 kubectl / 控制器 POST /eviction API Server 认证/授权/准入 PDB 校验 PDB 校验 minAvailable/maxUnavailable 校验通过 创建 Eviction 对象 写入 etcd Kubelet Watch Eviction 事件 Pod 终止 SIGTERM → 删除

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”的原则。