服务器与大带宽专家 · 持牌IDC/CDN/ISP服务商
简米科技官网JIANMI TECH
资讯 2026-08-30 更新于 2026-08-30 简米科技 4,913 字 12 分钟阅读

节点维护窗口内如何平滑驱逐Pod?节点维护,Pod驱逐最佳实践

导读节点维护窗口内实现Pod平滑驱逐的核心方法,是在drain节点前配置合理的PodDisruptionBudget(PDB)策略,并配合优先级类和拓扑分布约束,让Pod在维护窗口内有序迁移而非被强制清理,很多人第一次接触节点维护时,脑子里想的都是“直接kubectl drain节点完事”,但真到了生产环境,你这么……

节点维护窗口内实现Pod平滑驱逐的核心方法,是在drain节点前配置合理的PodDisruptionBudget(PDB)策略,并配合优先级类和拓扑分布约束,让Pod在维护窗口内有序迁移而非被强制清理。

很多人第一次接触节点维护时,脑子里想的都是“直接kubectl drain节点完事”,但真到了生产环境,你这么干大概率会被线上告警炸醒,因为drain命令默认行为是驱逐Pod,而驱逐不等于杀死Pod,它需要遵循PodDisruptionBudget的约束,如果不了解PDB的工作原理,节点维护窗口就会变成服务的“渡劫时刻”。

节点维护时Pod驱逐失败怎么办

先说一个最常见的场景:你收到云平台通知,说某台节点需要重启更新内核,维护窗口只有半小时,你打开终端执行kubectl drain node-xxx,然后发现命令卡住了,一堆Pod卡在Terminating状态,节点上还挂着几个“死活不走”的Pod,这就是驱逐失败的典型症状。

驱逐失败的核心原因:PDB阻止了驱逐操作

PDB(PodDisruptionBudget)是一个Kubernetes资源对象,它规定了在自愿中断(如节点维护)时,一个应用最多能容忍多少个副本同时不可用,你如果给某个应用设置了maxUnavailable: 1,那么当节点维护触发驱逐时,kubelet会检查这个约束,如果此时已经有1个副本处于不可用状态,剩余Pod就不会被允许驱逐,drain命令只能干等着。

这其实是PDB在保护你的应用,但如果你不了解机制,就会觉得“哇,这什么鬼系统”。

解决思路分两步:

  • 查询当前PDB状态:kubectl get pdb -n <namespace>,看ALLOWED DISRUPTIONS这一列是否为0
  • 如果确实为0,要么等Pod恢复可用状态再继续drain,要么临时调整PDB策略(需要审批流程)

Pod卡在Terminating状态的补救操作

如果PDB允许但Pod还是卡住,大概率是容器停止钩子(preStop)卡住了,或者存储卷卸载有问题,这时候别慌,按顺序排查:

kubectl describe pod <pod-name> -n <namespace>看Events里的最后几条日志,重点看FailedKillPod或者FailedDetachVolume,如果确认是preStop脚本问题,可以直接用kubectl delete pod <pod-name> -n <namespace> --force --grace-period=0强制执行删除,先把Pod清掉,让节点维护能继续,后续再排查脚本逻辑。

k8s节点drain驱逐Pod和直接删除节点有什么区别

很多人分不清drain、delete和cordon三个操作的区别,尤其是刚接触Kubernetes的运维人员,简单说:

  • cordon是给节点打上“禁止调度”的标记,已有Pod不受影响
  • 节点维护窗口内如何平滑驱逐Pod?节点维护,Pod驱逐最佳实践

  • drain是驱逐节点上所有Pod,让节点变为空壳
  • delete是直接删除节点对象,节点上的Pod会变成Unknown状态

直接删除节点的后果相当严重,节点被delete后,kubelet和API Server断连,控制平面要等待pod-eviction-timeout(默认5分钟)过期后才判定Pod失效,然后才重新调度,这期间你的服务可能已经不可用了。

drain操作则优雅得多,它会先对节点执行cordon,然后逐个驱逐Pod,驱逐前还会给Pod发送SIGTERM信号,等待容器正常关闭再删除。核心差异在于:drain尊重PDB和terminationGracePeriodSeconds,delete则完全无视这些约束

drain命令的完整操作路径

标准的节点维护流程应该是:

# 1. 先标记节点不可调度
kubectl cordon node-xxx
# 2. 驱逐节点上所有Pod(-ignore-daemonsets参数跳过DaemonSet管理的Pod)
kubectl drain node-xxx --ignore-daemonsets --delete-emptydir-data
# 3. 执行维护操作(如升级内核、修复硬件)
# 4. 维护完成后重新启用调度
kubectl uncordon node-xxx

这中间有个细节容易踩坑:如果你用云服务商的托管的Kubernetes集群(如AKS、EKS、GKE),节点池滚动升级时会自动处理drain逻辑,但自建集群必须自己写清楚上述命令的触发时机,否则节点维护窗口内大概率出现服务抖动。

Pod平滑驱逐的核心策略与配置实操

PDB是平滑驱逐的基石,但光有PDB还不够,生产环境里,你还需要配合优先级类(PriorityClass)来精细化控制驱逐顺序。

用PriorityClass区分Pod等级

节点维护时,如果节点上的Pod数量很多,drain会并行驱逐多个Pod,这时候低优先级的Pod可能会挤占高优先级Pod的调度资源,所以行业共识认为,生产环境必须设置优先级类

配置方式:

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "关键业务Pod使用此优先级"

然后在Pod的spec里声明priorityClassName: high-priority,这样在驱逐和重调度过程中,高优先级的Pod会优先获得节点资源。

PDB配置的三个要点

PDB不是设置了就完事的,还得注意下面这几点:

  • minAvailable适合有状态应用(如数据库),maxUnavailable适合无状态应用(如Web服务)
  • PDB只影响自愿中断,节点宕机等非自愿中断不受PDB约束
  • 如果应用副本数只有1个且没有冗余,设置PDB会导致drain卡死,这种情况要么临时扩副本,要么接受短暂不可用
  • 节点维护窗口内如何平滑驱逐Pod?节点维护,Pod驱逐最佳实践

拓扑分布约束:让Pod分散在不同节点上

如果应用的三个副本都挤在同一台节点上,节点维护时这台节点上的Pod全部被驱逐,等于应用瞬间零副本,这就不叫平滑了,叫雪崩。

配置topologySpreadConstraints可以强制Pod跨节点分布:

topologySpreadConstraints:
- maxSkew: 1
  topologyKey: kubernetes.io/hostname
  whenUnsatisfiable: DoNotSchedule

这个配置的意思是:尽量让Pod均匀分布在不同的主机上,偏差不超过1个副本,配合PDB使用,节点维护时每个节点可能只有1个Pod被驱逐,剩余副本仍然健康运行,应用几乎感知不到变化。

节点池维护与多节点滚动替换方案

单节点的drain操作好搞定,但你接手的是一个大型集群,有几十台节点同时要维护,怎么办?

分批节点池替换的实操要点

推荐的做法是按节点池维度分批操作,每批只处理三分之一或四分之一节点,具体操作路径:

  1. 给这批节点打上维护标签:kubectl label node <node-name> maintenance=true
  2. 检查PDB约束:所有绑定了PDB的工作负载,ALLOWED DISRUPTIONS必须大于0
  3. 批量执行drain(可以用脚本循环,但建议加并发控制,同一时间最多处理2-3个节点)
  4. 完成维护后uncordon节点,移除维护标签

这期间需要持续观察Pod的调度状态,如果发现有Pod长时间处于Pending状态,说明集群剩余节点资源不足,需要暂停维护,等待扩缩容完成后再继续。

本地存储和DaemonSet Pod的驱逐处理

这是drain命令最容易翻车的地方。DaemonSet管理的Pod不能用drain驱逐,因为DaemonSet会自动在节点上重建Pod,所以drain命令必须加--ignore-daemonsets参数,否则命令会卡住。

如果Pod使用了emptyDir存储,drain时数据会直接丢失,这种场景要么用--delete-emptydir-data参数强制驱逐,要么在应用层面做数据同步。

近年来,Kubernetes社区引入了volumeAttributesClassName和更灵活的生命周期钩子来缓解这个问题,但对存量应用来说,最稳的做法还是在架构设计上避免使用本地存储,改用持久卷(PV)或者对象存储。

Pod平滑驱逐核心操作对比

节点维护窗口内如何平滑驱逐Pod?节点维护,Pod驱逐最佳实践

操作方式 是否遵循PDB Pod停机时间 适用场景
kubectl drain 约等于terminationGracePeriodSeconds 节点日常维护
直接kubectl delete node 最多5分钟(pod-eviction-timeout) 节点异常死亡
delete节点前渐进式驱逐 可以控制在秒级 云平台自动维护节点
强制删除Pod(--force) 立即终止 驱逐卡死的兜底方案

据Kubernetes官方文档说明,终止前宽限期(terminationGracePeriodSeconds)的默认值是30秒,如果Pod里的业务容器的优雅退出逻辑比较复杂(比如需要反注册到注册中心、刷新网关缓存、等待存量请求完成),建议设置成60秒甚至更久,避免Pipeline“正在处理一半的请求被硬切”。

node维护窗口内Pod平滑驱逐常见问题解答

驱逐Pod时PDB一直不允许怎么办

PDB允许的驱逐数量是动态计算的。kubectl get pdb查看的ALLOWED DISRUPTIONS如果显示0,说明当前可用副本数已经等于或小于PDB的minAvailable,处理方式是:先扩容副本数(比如从2个扩到4个),再执行drain,完成后缩容回原值,如果业务不允许临时扩容,那只能调整PDB策略,但这需要走变更评审流程。

drain期间新Pod调度到被维护节点怎么办

drain会自动执行cordon操作,节点打上unschedulable标记后,调度器不会再往这个节点调度新的Pod,这个不需要担心,但如果你的环境里有人在drain之前手动执行了kubectl apply并指定了nodeName,则可能绕过调度器强制调度,这属于配置硬编码问题,需要从流程上禁止直接指定nodeName部署Pod。

云平台节点自动修复时,Pod平滑驱逐如何衔接

很多云提供商的托管Kubernetes节点池,在自动修复或系统更新前会执行类似drain的封神操作,比如简米云容器服务ACK节点池的“系统事件”里,会触发“节点维护”事件,云厂商的故障自愈系统在替你把节点踢出集群前,默认会等待节点上的Pod完成驱逐,如果你配置了PDB且副本数足够,整个驱逐过程对用户来说是无感的,如果你的应用没有配置PDB,且只有一个副本,那节点维护期间服务必然中断,这就是为什么业务侧必须为关键应用配置副本数和PDB,别图省事跑单副本。

日常运维的核心,就是把“节点维护”这件事从“被动救火”变成“主动编排”,PDB、PriorityClass、topologySpreadConstraints这三个配置互相配合,能让每一次节点维护都变成一次隐形的手术,用户无感知,业务无抖动,下次再做节点维护,先检查PDB,再执行drain,顺序对了,问题就少一半。

分享本文
本文为 简米科技官网 原创,已由运维技术专家审核。转载请注明来源:原文链接
售前咨询 服务热线 售后 邮箱