Cordon 改变调度资格,drain 协调驱逐并等待 Pod 终止。两条命令都不保证维护期间工作一定保留;应用关闭逻辑、替代容量、存储和中断预算共同决定结果。应把排空视为需要观察的状态,并记录明确例外,而不是从命令名称推导节点已经可以安全关机。

Cordon 存在调度例外。Drain 包含 cordon,并涉及删除过滤、服务端支持的驱逐与终止等待。部分完成结果、中断预算、剩余负载、替代容量和存储都需要检查。
Cordon 是放置限制,不证明节点已清空。Drain 不是原子事务,也不证明写入或替代副本成功。本轮没有执行原文保留的诊断命令。

Cordon 改变未来放置

Cordon 设置节点的不可调度字段,已有 Pod 继续运行,普通的新调度会避开它。Drain 自己也会执行 cordon,因此单独先做一次可以留下观察窗口,但不是必需步骤。Uncordon 重新允许调度,不保证立即出现新副本,也不会修好不健康的 kubelet;恢复前仍应核对节点状态与应用准备情况。

这项调度限制存在例外。DaemonSet Pod 容忍不可调度条件,直接填写 nodeName 的 Pod 则绕过普通调度选择。具有相应容忍度的其他 Pod 也可能符合放置条件。因此,需要检查真实剩余负载,不能把 cordon 说成阻止所有 Pod 到来的防火墙,更不能只凭不可调度字段就判断节点已经清空。

Drain 具有过滤检查和独立操作

Kubectl 先列出 Pod 并检查删除条件。独立 Pod 或管理控制器已经不存在的 Pod 可能需要 force;emptyDir 需要确认丢弃内容;DaemonSet 需要 ignore 选项。镜像 Pod 会被跳过,因为删除 API 表示不能停止 kubelet 根据静态清单管理的底层工作负载,静态程序需要单独规划维护。

预检查不等于原子事务。真正开始驱逐后,一部分可以成功,另一部分仍在等待或失败;失败时节点也可能已经被 cordon。再次尝试前应重新读取节点与 Pod 状态,不能认为命令非零退出就表示所有东西都保持原样。维护记录要区分哪些请求被接受、哪些进程已经退出,以及哪些副本仍未恢复。

Ignore-daemonsets 会留下 DaemonSet Pod。Delete-emptydir-data 允许丢失临时卷内容,不负责迁移。Force 允许处理无管理控制器的 Pod,却不会绕过 PodDisruptionBudget。选择参数前要核对归属、数据用途与重建方式;即使一个进程无状态,删除唯一实例也可能影响服务,因此无状态不是足够的安全证明。

预算约束主动驱逐

服务端支持时,drain 使用 Eviction API,并检查适用的中断预算。Disable-eviction 则改为直接删除,绕过这种保护。这个区别非常重要:添加 force 不能解决预算拒绝,而绕过驱逐可能删除维持法定多数或业务可用性所需的副本。不能把几个名字带有强制含义的参数视为同一个开关。

健康 Pod 可能因为预算没有可中断数量而被阻塞。默认 IfHealthyBudget 还限制已经运行但不健康的 Pod:当所保护应用已经低于健康要求时,驱逐可能等待;这并不表示每个不健康 Pod 都必须先变 Ready。AlwaysAllow 改变这类不健康 Pod 的判定,应结合应用恢复行为选择,而不是为了让终端尽快返回随意启用。

Drain 会等待终止,并重试部分被拒绝的驱逐。超时默认无限,但认证失败、服务端错误等仍可能结束命令。优雅终止只给应用一个停止窗口,不能证明写入完成或替代副本可用。设置合理的操作期限,有助于把受阻维护暴露出来,而不是让值班人员无限等待没有说明的进度。

坏掉的样子

可中断数量为零是重要线索,不是立即削弱预算的许可。应检查选择到的 Pod、当前和期望健康数、替代容量与存储拓扑。先恢复不健康副本或增加安全容量,可能比降低保护更合理。多节点并行排空会竞争同一预算,还可能加重恢复压力;增加并发不能凭空获得更多可用副本。

维护不能继续时,应找出具体剩余 Pod 和原因,不要为了看到成功退出而直接换成 disable-eviction。关闭节点之前,仍需验证应用健康,以及静态 Pod、DaemonSet 等剩余类别。普通 drain 成功本来就会留下部分工作负载,操作手册必须解释这些例外如何处理,而不是把它们当作遗漏。

快速验证

以下保留命令本轮没有执行。应明确选择临时集群与目标命名空间。客户端干跑仍读取集群,但不真正提交驱逐,也不验证服务端是否准许驱逐。示例中的 ignore 与 emptyDir 参数会压下对应预检查拒绝,所以不能把这份输出当成未忽略任何问题的完整安全报告。

kubectl get node node-1 -o jsonpath='{.spec.unschedulable}{"\n"}'
kubectl drain node-1 --dry-run=client --ignore-daemonsets --delete-emptydir-data
kubectl get pods --field-selector spec.nodeName=node-1 -o custom-columns=NAME:.metadata.name,OWNER:.metadata.ownerReferences[0].kind
kubectl get pdb -A -o custom-columns=NAME:.metadata.name,MIN:.spec.minAvailable,MAX:.spec.maxUnavailable,ALLOWED:.status.disruptionsAllowed

节点字段显示 cordon 状态。Pod 列表只涵盖当前命名空间,而且只显示第一个 owner reference;空列或非空列都不能证明完整控制器关系。实际操作前应比较所有命名空间和控制引用。预算列表虽然跨命名空间,却未显示命名空间与选择器,需要逐个匹配实际负载。真实排空仍须在获准环境单独验证,这些检查不能认证其成功。

来源

  • 安全地排空节点,drain 流程及其与 PodDisruptionBudget 的交互,已核验 2026-10-15。

  • kubectl drain 参考,精确的拒绝条件与参数说明,已核验 2026-10-15。

  • Pod 中断,主动与被动中断的区别以及驱逐重试,已核验 2026-10-15。

  • Primary reference,不健康 Pod 驱逐策略,已核验 2026-10-15。