Kubernetes 节点维护实战:cordon、drain 与污点容忍的完整手册
给 Kubernetes 节点打补丁、升级 kubelet、换硬件,都绕不开一个问题:节点上正在跑的 Pod 怎么办。直接关机是最差的做法——Pod 被硬杀、服务抖动、状态型工作负载还可能丢数据。Kubernetes 为此准备了三套机制:cordon 标记节点不可调度,drain 用驱逐 API 优雅地把 Pod 迁走,污点(taint)与容忍(toleration)则负责长期的“准入控制”。本文以一个完整的节点维护流程为主线,把三者的参数、代价和常见僵局一次讲清。
前置条件
- 一个可用的 Kubernetes 集群(1.26+),kubectl 已配置好 kubeconfig 且有节点管理权限。
- 待维护节点的 Pod 清单一目了然:知道上面跑了哪些关键服务、哪些带 PDB、哪些用了 emptyDir 或本地存储。
- 已确认集群其余节点的剩余容量能接得住被驱逐的 Pod,否则 drain 成功也会变成 Pending 堆积。
- 维护窗口与回滚预案明确:维护失败时是 uncordon 回退,还是直接更换节点。
维护的标准流程
一次规范的节点维护分五步:确认节点与负载现状 → cordon 止血 → drain 迁走 Pod → 执行维护 → uncordon 恢复并观察调度。每一步都有对应的确认动作,跳步是大多数维护事故的来源。
# 1. 先看清节点上有什么
kubectl get node node-01
kubectl get pods -A --field-selector spec.nodeName=node-01 -o wide
# 2. 标记不可调度(新 Pod 不再往这台落)
kubectl cordon node-01
# 3. 驱逐已有 Pod
kubectl drain node-01 --ignore-daemonsets --delete-emptydir-data --grace-period=120 --timeout=10m
# 4. 维护完成后恢复调度
kubectl uncordon node-01
kubectl get pods -A --field-selector spec.nodeName=node-01
cordon 后节点显示 SchedulingDisabled,只影响新调度,不碰在跑的 Pod。drain 则逐个调用驱逐(Eviction)API 删除 Pod,由控制器在其他节点重建——是“删除并重建”不是“迁移”,Pod 会换 IP、经历冷启动,维护窗口要按业务可中断程度来选。
drain 的参数与注意事项
--ignore-daemonsets:几乎必加。DaemonSet 的 Pod(如日志采集、监控 agent、CNI 组件)绑定在节点上无法迁走,不加这个参数 drain 会直接报错中止。它们会随节点关机而停止、开机后自动回来,不用操心。--delete-emptydir-data:允许删除使用 emptyDir 的 Pod。代价写在明面上:emptyDir 里的临时数据(缓存、会话文件、本地队列)会随 Pod 删除而丢失。不加这个参数时,存在这类 Pod 就会阻塞 drain。执行前先确认这些 Pod 的 emptyDir 里没有不能丢的数据。--force:允许删除不受控制器管理的裸 Pod。这类 Pod 删掉不会重建,属一次性工作负载,非万不得已不要用。--grace-period与--timeout:前者是单个 Pod 的优雅终止时间(还受 Pod 自身 terminationGracePeriodSeconds 影响),后者是整个 drain 的总超时。给数据库类 Pod 留足时间,超时中止后先排障再重试,不要反复硬跑。--disable-eviction:最后手段。它绕过 Eviction API 直接 DELETE Pod,代价是不再尊重 PodDisruptionBudget,可能一次性打掉一个服务的全部副本。只在与业务方确认影响后使用。
PodDisruptionBudget:保护与僵局
PDB 是服务可用性的保险丝:它通过 minAvailable(至少保留几个可用)或 maxUnavailable(最多允许几个不可用)声明一个服务能承受的自愿中断上限。drain 走 Eviction API 时,API Server 会逐个校验 PDB,违反预算的驱逐会被拒绝并返回错误 Cannot evict pod as it would violate the pod's disruption budget。
僵局通常有两种成因:一是 PDB 的 minAvailable 写得等于副本数(如 2 副本 minAvailable=2),任何自愿中断都被禁止,多半是笔误或误解语义;二是可用副本本身已经低于预算(有副本不健康),drain 一个都迁不走。处理顺序:先 kubectl get pdb -A 找出卡住的 PDB 及其 ALLOWED DISRUPTIONS;再优先修好不健康副本或临时扩容一个;实在需要窗口时,与业务方确认后临时调整 PDB。不要一卡就 --disable-eviction,那等于每次维护都把保险丝拆掉。
kubectl get pdb -A
kubectl describe pdb -n prod checkout-api
# 临时扩容一个副本,通常就能让 drain 继续
kubectl scale deployment -n prod checkout-api --replicas=3
污点与容忍:长期的准入控制
cordon 是一次性的手动标记,污点(taint)则是贴在节点上的持久“排斥标签”:没有对应容忍(toleration)的 Pod 不会被调度上来。每个污点由 key、value 和效应(effect)组成,效应有三种:
NoSchedule:硬排斥。新 Pod 没有容忍就不能调度上来;已经在跑的 Pod 不受影响。适合给 GPU 节点、专用中间件节点做隔离。PreferNoSchedule:软排斥。调度器尽量不往这儿放,实在没地方时仍可落。适合给即将退役的节点池“只出不进”的渐进下线。NoExecute:最强硬。不仅不收新 Pod,连正在跑且没有容忍的 Pod 也会被驱逐;配合容忍里的tolerationSeconds可以实现“节点故障后宽限 N 秒再驱逐”。节点出现硬件告警时打 NoExecute 污点,是一种快速疏散手段。
# 给节点打专用污点:只有容忍它的 Pod 才能上来
kubectl taint nodes node-01 dedicated=monitoring:NoSchedule
# 查看与删除污点(末尾加减号即删除)
kubectl describe node node-01 | grep -A3 Taints
kubectl taint nodes node-01 dedicated-
运维中最常见的组合是“标签 + 污点 + 容忍”三件套:节点打标签标明用途(如 role=gpu),再打污点防误入,目标负载在 Pod 里写 nodeSelector 选标签、写 toleration 过准入。只打标签不打污点,隔离就不成立。系统还有一组内置的 node.kubernetes.io/* 污点(not-ready、unreachable、memory-pressure 等),由节点控制器自动维护、NoExecute 语义,看到 Pod 被它们驱逐要顺着节点健康度查根因。
节点彻底下线的收尾
永久下线还要在 drain 之后补三步:删除节点对象(kubectl delete node node-01);在本机清理(kubeadm 部署的执行 kubeadm reset,并清 /etc/kubernetes、/var/lib/kubelet 等残留);确认节点已从负载均衡/Ingress 后端列表摘除。跳过删节点对象,监控里会长期挂着一个 NotReady 的幽灵节点。
验证
- drain 命令以
node/node-01 drained正常结束,没有报错与超时中止。 kubectl get pods -A --field-selector spec.nodeName=node-01只剩 DaemonSet 的 Pod,其余已在其他节点 Running。- uncordon 后
kubectl get node node-01状态回到 Ready,新建测试 Pod 能正常调度上去。 - 维护涉及的服务在整个窗口内无告警,副本数回到期望值。
常见问题
drain 卡住不动。输出停在某个 Pod 上,通常是其 PDB 预算为 0、Pod 带 finalizer,或镜面 Pod(mirror pod)误入视野。依次查 kubectl get pdb -A 的 ALLOWED DISRUPTIONS、kubectl describe pod 的事件、该 Pod 是否由静态清单托管。找到原因再决定扩容、修 PDB 还是跳过,不要直接上 --force 全家桶。
驱逐后服务明显抖动。多半是优雅终止没做好:readiness 摘流太慢、进程不处理 SIGTERM、terminationGracePeriodSeconds 太短。维护前先补齐关键服务的 preStop 钩子与探针参数;维护时一个节点一个节点来,给重建留时间。
uncordon 之后新 Pod 分布很不均。这是预期行为:Kubernetes 只调度新建的 Pod,不会主动把已在别处安家的 Pod 搬回来做均衡。在意分布时要么滚动重启相关工作负载,要么引入 Descheduler 周期性做再均衡。不要指望 uncordon 自带“回流”效果。
小结
cordon 管当下,drain 管迁移,污点管长期准入,三者配合才是一次完整的节点维护。drain 的每个参数都有明确代价:emptyDir 数据会丢、裸 Pod 不重建、绕过驱逐等于拆掉 PDB 保险丝。把 PDB 当作需要提前沟通的契约而不是拦路石,把“标签 + 污点 + 容忍”当作节点隔离的标准件,节点维护就会从每次提心吊胆变成例行操作。







