3、滚动升级基础:kubectl drain、kubectl uncordon、kubectl cordon 命令详解

在 Kubernetes 节点滚动升级流程中,kubectl cordon、kubectl drain 和 kubectl uncordon 是三个最核心的节点管理命令。它们共同构成了节点调度的“开关-排空-恢复”三阶段模型,是后续实现 Policy as Code 窗口控制的基础操作单元。

3.1 命令功能对比

命令 核心动作 调度影响 Pod 处理 典型场景
kubectl cordon 标记节点为不可调度 新 Pod 不再调度至此节点 不驱逐已有 Pod 节点维护前准备,防止新负载进入
kubectl drain 排空节点上的 Pod 自动 cordon + 驱逐 Pod 优雅驱逐所有可迁移 Pod 节点升级、下线、重启
kubectl uncordon 恢复节点为可调度 新 Pod 可重新调度至此节点 不恢复已驱逐的 Pod 节点升级完成后恢复服务
关键理解: cordon 只做“禁止新来”,drain 做“禁止新来 + 清空现有”,uncordon 做“重新开放”。滚动升级中,三者的顺序是固定的:cordon → drain → 升级操作 → uncordon。

3.2 kubectl cordon —— 节点“隔离”

cordon 命令在节点上设置 node.kubernetes.io/unschedulable: NoSchedule 污点,阻止新 Pod 调度到该节点。已运行的 Pod 不受影响。

# 标记节点 node-1 为不可调度
kubectl cordon node-1

# 验证节点状态
kubectl get nodes
# 输出中 node-1 的 STATUS 列会显示 "Ready,SchedulingDisabled"

使用要点:

  • cordon 是 非破坏性 操作,不会影响节点上正在运行的业务。
  • 适用于需要逐步减少节点负载的场景,如灰度升级前的准备阶段。
  • 可以配合 kubectl describe node 查看污点详情。

3.3 kubectl drain —— 节点“排空”

drain 是滚动升级中最关键的命令。它自动执行 cordon,然后驱逐节点上所有可迁移的 Pod(DaemonSet 管理的 Pod 除外)。

# 排空节点 node-1,忽略 DaemonSet,设置优雅终止期限为 60 秒
kubectl drain node-1 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=60 \
  --force

参数说明:

参数 作用 是否必须
--ignore-daemonsets 忽略 DaemonSet 管理的 Pod(不会被驱逐) 通常必须
--delete-emptydir-data 允许驱逐使用 emptyDir 卷的 Pod 视情况
--grace-period Pod 优雅终止等待时间(秒) 推荐设置
--force 强制驱逐不受 controller 管理的 Pod 谨慎使用
--pod-selector 只驱逐匹配标签的 Pod 可选
⚠️ 排空失败常见原因: PodDisruptionBudget(PDB)限制、未设置 --ignore-daemonsets、存在不受 controller 管理的裸 Pod、节点上有 mirror Pod(静态 Pod)。排空失败时,drain 会阻塞并输出具体原因,不会自动回滚。

3.4 kubectl uncordon —— 节点“恢复”

升级或维护完成后,使用 uncordon 移除节点的 NoSchedule 污点,使其重新加入调度池。

# 恢复节点 node-1 为可调度
kubectl uncordon node-1

# 验证节点状态
kubectl get nodes
# node-1 的 STATUS 恢复为 "Ready"

注意事项:

  • uncordon 不会自动将之前被驱逐的 Pod 调度回来,需要依赖 Deployment/StatefulSet 的控制器重新创建 Pod。
  • 恢复后,新 Pod 会逐渐调度到该节点,但不会立即填满。
  • 在滚动升级脚本中,uncordon 通常是最后一步,标志着该节点升级完成。

3.5 三阶段流程与状态转换

下图展示了单个节点在滚动升级过程中的状态变化与命令对应关系:

节点滚动升级三阶段状态转换 阶段 1:正常运行 可调度,Pod 运行中 阶段 2:排空 不可调度,驱逐 Pod 阶段 3:升级 节点维护/升级 cordon drain uncordon(恢复可调度) 📌 流程说明: cordon 先阻止新 Pod 进入 → drain 优雅驱逐已有 Pod → 执行升级操作 → uncordon 恢复调度。

3.6 滚动升级中的典型命令序列

以下是一个完整的节点滚动升级命令序列,展示了三个命令的配合使用:

# 步骤 1:标记节点不可调度
kubectl cordon node-1

# 步骤 2:排空节点上的 Pod
kubectl drain node-1 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=120

# 步骤 3:执行节点升级操作(如 kubelet 版本更新、内核升级等)
# 此处为示例,实际升级命令因环境而异
# ssh node-1 "sudo apt update && sudo apt upgrade -y kubelet"

# 步骤 4:恢复节点可调度
kubectl uncordon node-1

# 步骤 5:验证节点状态
kubectl get nodes -o wide | grep node-1
💡 生产环境建议: 在自动化滚动升级脚本中,建议在 drain 和 uncordon 之间加入健康检查逻辑(如等待节点 Ready、等待系统 Pod 恢复),而不是直接执行 uncordon。这可以避免在节点尚未完全恢复时错误地开放调度。

3.7 常见错误与排查

错误现象 可能原因 解决方法
drain 卡住不执行 PDB 阻止驱逐,或存在未受 controller 管理的 Pod 检查 PDB 配置;使用 --force 强制驱逐裸 Pod
drain 报错 "DaemonSet-managed Pods" 未添加 --ignore-daemonsets 参数 添加 --ignore-daemonsets 重试
uncordon 后节点仍不可调度 节点上存在其他污点(如 node.kubernetes.io/out-of-disk) 使用 kubectl describe node 查看所有污点并逐一处理
Pod 在 drain 后未重新调度 Deployment 副本数已满足,或节点亲和性限制 检查 Deployment 的 replicas 和调度策略