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 三阶段流程与状态转换
下图展示了单个节点在滚动升级过程中的状态变化与命令对应关系:
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 和调度策略 |