节点内存吃紧时 Pod 被悄悄杀掉、执行维护时业务突然抖动,这类问题十有八九和驱逐机制有关。理解 Kubernetes 的驱逐原理,掌握规范的节点排水流程,是保障线上稳定性的基本功。

一、kubelet 驱逐机制是怎么工作的
Kubernetes 的驱逐分为两类:一类是节点资源压力触发的自动驱逐,由 kubelet 负责;另一类是通过 Eviction API 主动发起的驱逐,kubectl drain 走的就是这条路。自动驱逐依赖一组阈值配置,分为软驱逐和硬驱逐两种。硬驱逐没有宽限期,条件一满足 kubelet 立刻回收 Pod,典型配置如 memory.available<100Mi;软驱逐则要配合宽限期,条件持续满足超过设定时间才动手,比如 memory.available<500Mi 配合 1 分 30 秒的宽限期。
驱逐时 kubelet 会给 Pod 发送终止信号,并遵循 terminationGracePeriodSeconds 指定的优雅退出时间。如果超过宽限期 Pod 仍未退出,就会被强制杀死。这里有个容易踩的坑:使用 emptyDir 存储 Pod 内部数据的容器,被驱逐时本地数据会直接丢失。所以给关键 Pod 配置 PodDisruptionBudget 就显得尤为重要,它能保证驱逐过程中始终保持最小可用副本数,避免服务整体不可用。
还需要区分驱逐与抢占的不同。抢占发生在调度阶段,是高优先级 Pod 抢走低优先级 Pod 的节点位置;驱逐则发生在运行阶段,由节点压力或管理员主动触发。两者都会删除 Pod,但触发链路和影响范围完全不一样。
二、节点排水的标准操作流程
排水一般分三步走:先封锁节点阻止新 Pod 调度过来,再驱逐存量 Pod,最后维护完成解除封锁。第一步用 cordon 命令:
# 标记节点为不可调度 kubectl cordon node-worker-01 # 查看节点状态,会显示 SchedulingDisabled kubectl get nodes
这一步执行后,节点上已有的 Pod 不受影响,只是调度器不再往这个节点分配新的 Pod。它是排水的安全前置动作,即使维护计划临时取消,随时可以用 uncordon 恢复,成本几乎为零。
第二步是核心的 drain 命令。直接执行通常会报错,因为节点上往往存在 DaemonSet 管理的 Pod(比如日志采集、网络插件),这类 Pod 本来就应该跟着节点走,驱逐没有意义,需要显式忽略:
# 驱逐节点上所有可驱逐的 Pod kubectl drain node-worker-01 \ --ignore-daemonsets \ --delete-emptydir-data \ --grace-period=120 \ --force
几个关键参数要理解清楚。--ignore-daemonsets 跳过 DaemonSet Pod,几乎必加;--delete-emptydir-data 允许删除使用 emptyDir 的 Pod,注意数据会丢失,加之前务必确认;--force 用于驱逐不受控制器管理的裸 Pod,这类 Pod 被删后不会在其他节点重建,加不加要想清楚;--grace-period 设定优雅退出时间,给应用留够清理资源、完成收尾的时间。drain 执行成功后,节点会自动处于封锁状态,不需要重复 cordon。
第三步,维护结束后解除封锁:
# 解除节点封锁,恢复调度 kubectl uncordon node-worker-01
节点恢复调度能力后,新的 Pod 会正常分配过来。之前被驱逐的 Pod 已经在其他节点重建,不会自动迁回,这一点要心里有数。如果希望重新均衡负载,可以考虑用 Descheduler 之类的工具做二次调度。
三、排水过程中的常见报错与排查
最常见的报错是 Pod 驱逐被阻塞,命令卡住不动。除了前面提到的 DaemonSet 问题,还有两种情况。一是存在裸 Pod,报错信息会提示 use --force to delete;二是 PodDisruptionBudget 不满足条件,比如Deployment 只有 2 个副本,PDB 要求 minAvailable 为 2,drain 就无法驱逐任何一个 Pod。这时不要盲目加 --force,应该先评估业务容量,临时扩容副本数或调整 PDB 配置后再操作:
# 查看 PDB 状态和当前允许的驱逐数量 kubectl get pdb -n production # 查看 ALLOWED DISRUPTIONS 列,为 0 说明暂时不能驱逐 # 查看节点上还残留哪些 Pod kubectl get pods -A --field-selector spec.nodeName=node-worker-01
另一个高频问题是 Pod 卡在 Terminating 状态,drain 命令迟迟不返回。这通常是因为容器内的进程没有正确处理 SIGTERM 信号,或者清理逻辑耗时超过了宽限期。可以单独查看某个 Pod 的事件和状态确认原因,必要时用 kubectl delete pod xxx --grace-period=0 --force 强制删除,但这属于最后手段,强删可能导致数据不一致,生产环境要谨慎。
还有一种情况值得注意:drain 提示 cannot delete Pods with local storage。这是因为 Pod 使用了 emptyDir 或宿主机本地存储,默认策略禁止驱逐以保护数据。确认数据可丢后再加 --delete-emptydir-data,如果数据重要,就要先做数据迁移,而不是想办法绕过限制。
四、生产环境排水的实践建议
排水前先做容量检查。用 kubectl describe nodes 看一眼其他节点的可分配资源,确认被驱逐的 Pod 有地方接收。如果集群本身资源就很紧张,先扩容节点池再排水,否则 Pod 会进入 Pending 状态,反而制造故障。
给关键业务配好 PodDisruptionBudget 是排水安全的前提。建议所有多副本服务都设置 minAvailable 或 maxUnavailable,让驱逐过程天然受保护。对于单副本的有状态服务,比如数据库类应用,先评估是否支持自动迁移,很多时候更稳妥的做法是手动控制迁移节奏,而不是依赖 drain 一把梭。
维护多台节点时务必逐台进行,等上一台节点的 Pod 全部重新调度并就绪后再处理下一台。批量 cordon 再批量 drain 的做法看似高效,实际等于一次性抽走大片容量,很容易触发连锁故障。把 cordon、drain、uncordon 这套流程写成脚本或纳入自动化运维平台时,记得加上就绪探针校验和失败回滚逻辑,让节点维护真正做到业务无感。
Kubernetes Pod驱逐节点排水kubectl drain修改时间:2026-09-07 08:39:04