你以为 drain 只是删除 Pod?它其实是在“安全地清空一台节点”。
这两天升级 Kubernetes 集群时,在升级 Worker 节点阶段执行了下面这条命令:
kubectl drain worker02 --ignore-daemonsets --delete-emptydir-data
结果直接报错:
cannot delete Pods that declare no controller
default/recovery-nginx

可能不少人第一次看到这个报错都和我一样懵:
本篇文章,理一理 kubectl drain的来龙去脉。
假设你的集群里有一台 worker01,上面跑着这些 Pod:
worker01
├── nginx
├── redis
└── mysql
如果这时候你图省事,直接执行:
systemctl stop kubelet
# 或者直接 reboot
会发生什么?
这就是典型的“暴力下线”。
而 kubectl drain就是用来解决这个问题的。它的目标很明确:让节点上的工作负载平滑迁移,尽量减少对业务的影响。
kubectl drain其实干了四件事:
1. 禁止调度(Cordon)
↓
2. 驱逐 Pod(Eviction)
↓
3. 等待新 Pod 启动
↓
4. 节点清空完成
我们一步步拆解。
执行 drain 时,它会先调用:
kubectl cordon worker02
节点状态会变成:
worker02 Ready,SchedulingDisabled

效果是:
你可以理解为:先把门关上,不再接待新客人。
它调用的是 Kubernetes 的 Eviction API(驱逐 API),而不是直接 DELETE Pod。
两者的区别在于:
Eviction 会检查:
只有条件满足,才会真正删除 Pod,并让控制器在其他节点重建。
例如:
replicas: 3
驱逐前:
worker01 ─── nginx-abc
worker02 ─── nginx-def
worker03 ─── nginx-ghi
驱逐 worker01 后:
worker02 ─── nginx-def
worker03 ─── nginx-ghi
worker02 ─── nginx-new # 自动重建
业务几乎无感知。
这些 Pod 的设计就是“每个节点必须运行一个”。
drain 默认会报错:
cannot delete DaemonSet-managed Pods
因此需要加上:
--ignore-daemonsets
表示:这些我确认保留,不用管。
如果 Pod 使用了 emptyDir卷:
volumes:
- emptyDir: {}
drain 会阻止驱逐,因为:
节点删除 → emptyDir 数据永久丢失
需要加上:
--delete-emptydir-data
表示:这些数据我可以接受丢失。
这就是我遇到的那个坑。
kubectl run nginx --image=nginx
这个 Pod 没有 Deployment / ReplicaSet / StatefulSet 管理。
drain 的报错是:
cannot delete Pods that declare no controller

原因很简单:删了就真的没了,没有人会帮它重建。
Kubernetes 拒绝帮你做这个危险操作。
两种处理方式:
kubectl delete pod nginx
kubectl drain worker02 ...
kubectl drain worker02 --ignore-daemonsets --delete-emptydir-data --force
⚠️ 注意:
--force可能导致业务中断,生产环境需要谨慎评估。
drain 不是一刀切,它会等待:
例如:
terminationGracePeriodSeconds: 30
drain 会等待最多 30 秒让应用完成收尾工作,而不是直接杀死。
PDB(PodDisruptionBudget)是用来保护业务可用性的。
例如:
minAvailable: 2
如果某个应用当前只有 3 个 Pod:
此时 drain 会卡住,等待新 Pod 启动后再继续。
有的人以为 drain 出 bug 了,实际上是 PDB 在保护你的业务。
功能 | drain | delete 节点 |
|---|---|---|
禁止新 Pod 调度 | ✅ | ❌ |
优雅驱逐 Pod | ✅ | ❌ |
等待 Pod 迁移 | ✅ | ❌ |
考虑 PDB | ✅ | ❌ |
保护业务 | ✅ | ❌ |
所以记住:drain ≠ 删除节点,而是安全下线节点。
cordon
↓
drain
↓
升级节点 / 维修硬件 / 替换系统
↓
重启 kubelet
↓
检查节点状态
↓
uncordon
命令示例:
# 1. 驱逐负载
kubectl drain worker01 \
--ignore-daemonsets \
--delete-emptydir-data
# 2. 执行升级操作
apt install kubelet kubeadm kubectl
systemctl restart kubelet
# 3. 恢复调度
kubectl uncordon worker01
uncordon是 cordon的逆操作:
SchedulingDisabled
↓
Ready
节点恢复调度能力,新 Pod 可以再次调度上来。

实际上:
kubectl drain = 安全下线节点
它做的事情包括:
所以,在以下场景中:
drain都是必不可少的一步。
因为 Kubernetes 的设计理念从来不是简单地“关掉一台机器”,而是:
在保证业务连续运行的前提下,让节点安全退出集群。