首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >kubectl drain 到底做了什么?为什么它总是在升级时为难你?

kubectl drain 到底做了什么?为什么它总是在升级时为难你?

作者头像
一根头发丝的宽度
发布2026-06-29 13:08:07
发布2026-06-29 13:08:07
900
举报

你以为 drain 只是删除 Pod?它其实是在“安全地清空一台节点”。

这两天升级 Kubernetes 集群时,在升级 Worker 节点阶段执行了下面这条命令:

代码语言:javascript
复制
kubectl drain worker02 --ignore-daemonsets --delete-emptydir-data

结果直接报错:

代码语言:javascript
复制
cannot delete Pods that declare no controller

default/recovery-nginx

可能不少人第一次看到这个报错都和我一样懵:

  • drain 为什么不直接删 Pod?
  • 为什么 Deployment 的 Pod 能删,裸 Pod 就不行?
  • 升级节点为什么必须先 drain?

本篇文章,理一理 kubectl drain的来龙去脉。


为什么需要 drain?

假设你的集群里有一台 worker01,上面跑着这些 Pod:

代码语言:javascript
复制
worker01
├── nginx
├── redis
└── mysql

如果这时候你图省事,直接执行:

代码语言:javascript
复制
systemctl stop kubelet
# 或者直接 reboot

会发生什么?

  • Pod 突然消失
  • 业务中断
  • Service 转发失败
  • 用户请求报 500

这就是典型的“暴力下线”。

kubectl drain就是用来解决这个问题的。它的目标很明确:让节点上的工作负载平滑迁移,尽量减少对业务的影响。


drain 的执行流程

kubectl drain其实干了四件事:

代码语言:javascript
复制
1. 禁止调度(Cordon)
        ↓
2. 驱逐 Pod(Eviction)
        ↓
3. 等待新 Pod 启动
        ↓
4. 节点清空完成

我们一步步拆解。


第一步:Cordon——先关门

执行 drain 时,它会先调用:

代码语言:javascript
复制
kubectl cordon worker02

节点状态会变成:

代码语言:javascript
复制
worker02   Ready,SchedulingDisabled

效果是:

  • 新的 Pod 不会再调度到这台节点上
  • 已有的 Pod 继续运行,不受影响

你可以理解为:先把门关上,不再接待新客人。


第二步:Eviction——不是删除,是“请离开”

它调用的是 Kubernetes 的 Eviction API(驱逐 API),而不是直接 DELETE Pod。

两者的区别在于:

  • DELETE Pod:强行删除,不管业务死活
  • Eviction API:向 API Server 发起驱逐请求,由系统判断是否可以安全删除

Eviction 会检查:

  • Pod 是否受控制器管理(Deployment / StatefulSet 等)
  • 是否满足 PDB(PodDisruptionBudget)
  • 优雅终止时间(terminationGracePeriodSeconds)

只有条件满足,才会真正删除 Pod,并让控制器在其他节点重建。


不同 Pod 类型,drain 的处理方式不同

✅ 受控制器管理的 Pod(Deployment / StatefulSet)

例如:

代码语言:javascript
复制
replicas: 3

驱逐前:

代码语言:javascript
复制
worker01 ─── nginx-abc
worker02 ─── nginx-def
worker03 ─── nginx-ghi

驱逐 worker01 后:

代码语言:javascript
复制
worker02 ─── nginx-def
worker03 ─── nginx-ghi
worker02 ─── nginx-new   # 自动重建

业务几乎无感知。


⚠️ DaemonSet(Calico / kube-proxy / node-exporter)

这些 Pod 的设计就是“每个节点必须运行一个”。

drain 默认会报错:

代码语言:javascript
复制
cannot delete DaemonSet-managed Pods

因此需要加上:

代码语言:javascript
复制
--ignore-daemonsets

表示:这些我确认保留,不用管。


⚠️ 挂载 emptyDir 的 Pod

如果 Pod 使用了 emptyDir卷:

代码语言:javascript
复制
volumes:
- emptyDir: {}

drain 会阻止驱逐,因为:

代码语言:javascript
复制
节点删除 → emptyDir 数据永久丢失

需要加上:

代码语言:javascript
复制
--delete-emptydir-data

表示:这些数据我可以接受丢失。


❌ 裸 Pod(没有控制器的 Pod)

这就是我遇到的那个坑。

代码语言:javascript
复制
kubectl run nginx --image=nginx

这个 Pod 没有 Deployment / ReplicaSet / StatefulSet 管理。

drain 的报错是:

代码语言:javascript
复制
cannot delete Pods that declare no controller

原因很简单:删了就真的没了,没有人会帮它重建。

Kubernetes 拒绝帮你做这个危险操作。


裸 Pod 的解决方案

两种处理方式:

方案一:先删再 drain

代码语言:javascript
复制
kubectl delete pod nginx
kubectl drain worker02 ...

方案二:强制驱逐

代码语言:javascript
复制
kubectl drain worker02 --ignore-daemonsets --delete-emptydir-data --force

⚠️ 注意:--force可能导致业务中断,生产环境需要谨慎评估。


drain 会等待什么?

drain 不是一刀切,它会等待:

  • Pod 进入 Terminating 状态
  • 应用收到 SIGTERM 信号
  • 优雅退出(默认 30 秒)
  • 新 Pod 在其它节点变成 Running
  • ReadinessProbe 探测成功

例如:

代码语言:javascript
复制
terminationGracePeriodSeconds: 30

drain 会等待最多 30 秒让应用完成收尾工作,而不是直接杀死。


PDB 为什么会导致 drain 卡住?

PDB(PodDisruptionBudget)是用来保护业务可用性的。

例如:

代码语言:javascript
复制
minAvailable: 2

如果某个应用当前只有 3 个 Pod:

  • 驱逐第 1 个 → 剩 2 个 ✅
  • 驱逐第 2 个 → 剩 1 个 ❌(违反 PDB)

此时 drain 会卡住,等待新 Pod 启动后再继续。

有的人以为 drain 出 bug 了,实际上是 PDB 在保护你的业务。


drain vs delete:一张表看懂

功能

drain

delete 节点

禁止新 Pod 调度

优雅驱逐 Pod

等待 Pod 迁移

考虑 PDB

保护业务

所以记住:drain ≠ 删除节点,而是安全下线节点。


完整的节点维护流程

代码语言:javascript
复制
cordon
   ↓
drain
   ↓
升级节点 / 维修硬件 / 替换系统
   ↓
重启 kubelet
   ↓
检查节点状态
   ↓
uncordon

命令示例:

代码语言:javascript
复制
# 1. 驱逐负载
kubectl drain worker01 \
    --ignore-daemonsets \
    --delete-emptydir-data

# 2. 执行升级操作
apt install kubelet kubeadm kubectl
systemctl restart kubelet

# 3. 恢复调度
kubectl uncordon worker01

uncordon 做了什么?

uncordoncordon的逆操作:

代码语言:javascript
复制
SchedulingDisabled
        ↓
Ready

节点恢复调度能力,新 Pod 可以再次调度上来。


一张图看懂 drain


总结

实际上:

代码语言:javascript
复制
kubectl drain = 安全下线节点

它做的事情包括:

  • 禁止调度(cordon)
  • 驱逐 Pod(Eviction)
  • 等待迁移完成
  • 检查 PDB 约束
  • 保护业务连续性

所以,在以下场景中:

  • 节点维护
  • 集群升级
  • 节点替换
  • 硬件维修
  • 操作系统更新

drain都是必不可少的一步。

因为 Kubernetes 的设计理念从来不是简单地“关掉一台机器”,而是:

在保证业务连续运行的前提下,让节点安全退出集群。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一根头发丝的宽度 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么需要 drain?
  • drain 的执行流程
  • 第一步:Cordon——先关门
  • 第二步:Eviction——不是删除,是“请离开”
  • 不同 Pod 类型,drain 的处理方式不同
    • ✅ 受控制器管理的 Pod(Deployment / StatefulSet)
    • ⚠️ DaemonSet(Calico / kube-proxy / node-exporter)
    • ⚠️ 挂载 emptyDir 的 Pod
    • ❌ 裸 Pod(没有控制器的 Pod)
  • 裸 Pod 的解决方案
    • 方案一:先删再 drain
    • 方案二:强制驱逐
  • drain 会等待什么?
  • PDB 为什么会导致 drain 卡住?
  • drain vs delete:一张表看懂
  • 完整的节点维护流程
  • uncordon 做了什么?
  • 一张图看懂 drain
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档