对 Kubernetes 集群进行节点维护时,最忌讳的就是直接关机或删除节点对象。节点上的 Pod 不会自动获得迁移机会,kubelet 停止响应后,控制平面通常要等待较长时间才会把这些 Pod 标记为未知,最终触发重建,业务会出现明显中断。为了安全地让节点退出调度并清空工作负载,Kubernetes 原生提供了 cordon 与 drain 两个操作。它们经常配合使用,但各自职责不同,参数选择也会直接影响维护期间的服务可用性。

cordon 与 drain 的核心区别
kubectl cordon 的作用只是把节点的 spec.unschedulable 字段设置为 true。一旦被标记,调度器在为新 Pod 选择节点时会跳过该节点,但已经运行在上面的 Pod 完全不受影响。可以通过 kubectl get node 查看节点状态,被 cordon 的节点会显示 SchedulingDisabled。这个操作非常轻量,适合在观察期先阻止新负载进入。
kubectl drain 则更进一步,它会先自动执行 cordon,然后逐个驱逐节点上现有的 Pod。驱逐并不是直接删除 Pod,而是通过 Eviction API 向 API Server 发起驱逐请求。对于由 Deployment、StatefulSet、ReplicaSet 等控制器管理的 Pod,控制器会在其他可用节点上重新创建副本;对于裸 Pod,驱逐后不会自动重建。理解这个区别能帮助判断哪些工作负载在 drain 后可能消失。
简单来说,cordon 是只进不出的状态切换,drain 是清空节点的完整流程。生产环境通常先手动 cordon,等待观察期确认没有异常后,再执行 drain。如果直接执行 drain,它会隐含 cordon 步骤,但最好显式操作,避免遗漏。
drain 的关键参数与驱逐行为
执行 kubectl drain 时,有几个参数直接决定操作是否顺畅。--ignore-daemonsets 是最常用的一个。DaemonSet 管理的 Pod 会在每个匹配节点上运行,即使驱逐它们,控制器也会立刻在同节点重新拉起,因此 drain 默认会因为无法驱逐 DaemonSet Pod 而失败。加上该参数后,drain 会跳过 DaemonSet Pod,允许节点继续清空其他工作负载。
另一个容易误用的参数是 --delete-emptydir-data。当 Pod 使用了 emptyDir 卷时,驱逐会导致临时数据被删除,drain 默认会拒绝驱逐这类 Pod,以提示用户确认。只有在明确知道这些数据可以丢弃时,才加上该参数。类似地,--force 并不会粗暴地强制删除 Pod,它只是在 Pod 不受 ReplicationController、ReplicaSet、Job、DaemonSet 或 StatefulSet 管理时,允许继续删除裸 Pod。真正的宽限期由 --grace-period 控制,默认 -1 表示使用 Pod 自身 terminationGracePeriodSeconds。
下面这条命令是一个比较稳妥的驱逐示例:
kubectl drain node-1 \ --ignore-daemonsets \ --delete-emptydir-data \ --grace-period=120 \ --timeout=5m
其中 --timeout 表示如果驱逐在指定时间内未完成,命令返回错误,但已经发出的驱逐请求仍然有效。此时需要通过 kubectl get pods -o wide 反复确认节点上是否还有残留 Pod。
节点维护完整流程与恢复调度
一次标准的节点维护应遵循以下步骤。先确认节点与 Pod 状态:
kubectl get nodes kubectl get pods -o wide | grep node-1
然后执行 cordon,让节点进入不可调度状态:
kubectl cordon node-1
观察一段时间,确认没有新 Pod 被调度到该节点,且现有 Pod 运行稳定后,执行 drain:
kubectl drain node-1 \ --ignore-daemonsets \ --delete-emptydir-data \ --grace-period=120 \ --timeout=5m
维护完成后,通过 kubectl uncordon node-1 恢复调度。此时节点会重新出现在调度器的候选列表中,但之前被驱逐的 Pod 不一定会自动调度回来,新 Pod 会按资源情况分布。对于有状态服务,建议等待控制器重新平衡或手动触发滚动更新。
实际操作中还要关注 PodDisruptionBudget。如果一个 Deployment 配置了 maxUnavailable: 1,而节点上有多个副本,drain 会尊重这一限制,确保同一时间被驱逐的副本数不超过允许值。如果 PDB 配置过于严格,drain 可能会长时间卡住。查看 PDB 的命令是 kubectl get pdb,必要时临时调整或先扩容副本。
常见卡住场景与排查方法
drain 卡住是最常见的运维问题。第一个原因是 DaemonSet Pod 没有被忽略。如果没有加 --ignore-daemonsets,drain 会报类似 DaemonSet-managed pods 的错误并停止。修复方法就是显式加上该参数,或者先评估是否可以在维护期间暂停 DaemonSet。
第二个原因是 Pod 使用本地存储或 emptyDir,drain 会提示需要 --delete-emptydir-data。如果直接加参数,会清空这些临时数据;对于使用 hostPath 或 local PV 的有状态服务,drain 无法迁移数据,需要先手动备份或调整工作负载存储类型。
第三个原因是 PDB 阻止驱逐。通过 kubectl get pdb -A 查看状态,如果 ALLOWED DISRUPTIONS 为 0,说明当前没有可中断预算。此时可以临时调高 minAvailable 或 maxUnavailable,或者扩容副本后再 drain。排查时可以使用 kubectl describe pdb 查看事件。
最后还可以使用 kubectl drain --dry-run=server 预演,它会模拟驱逐过程但不会真正删除 Pod。这在生产变更前非常有用,能提前暴露参数遗漏和 PDB 限制。
Kubernetescordondrain修改时间:2026-08-20 21:53:30