跳转到内容

scheduling

starlightBlog.tags.count

赛博工地的“五分钟定律”:揭秘节点宕机后的 Pod 驱逐真相

在我们的 K3s 高可用堡垒中,我进行了一次“暴力断电”演习:直接关掉了 k3s-master-03 节点。

当时的情况如下:

  • 业务中断:原本运行在 03 节点上的 Headlamp(单副本)瞬间无法访问。
  • 状态迷惑:控制台输入 kubectl get pods -o wide,发现该 Pod 居然还显示为 Running,且依然赖在已经失联的 03 节点上。
  • 没有漂移:想象中的“毫秒级瞬移到其他节点”并没有发生。

这种“僵尸 Pod”现象并非 Bug,而是触发了 Kubernetes 调度架构中极其核心的机制:基于污点的驱逐(Taint-based Eviction)与五分钟定律

📖 官方原典参考:本文底层逻辑均来源于 Kubernetes 官方文档:污点和容忍度


要理解这五分钟,必须先看懂 K8s 官方定义的一对博弈属性,我们直接用命令在案发现场求证:

1. 污点 (Taint) —— 节点的“逐客令”

Section titled “1. 污点 (Taint) —— 节点的“逐客令””

03 节点断电失联时,K8s 控制面会迅速给该节点打上严厉的污点。 🕵️ 求证指令:我们怎么确定节点真的被打上污点了?

Terminal window
# 查看 03 节点的污点状态
kubectl describe node k3s-master-03 | grep Taints

输出结果:你会看到 Taints: node.kubernetes.io/not-ready:NoExecute, node.kubernetes.io/unreachable:NoExecute。 这就是节点的逐客令。NoExecute 效果意味着:不仅新 Pod 不能来,已经在上面的 Pod 必须立刻被驱逐!

2. 容忍度 (Toleration) —— Pod 的“特权通行证”

Section titled “2. 容忍度 (Toleration) —— Pod 的“特权通行证””

面对驱逐令,Pod 能否抗旨?这就看它自身有没有配置对应的容忍度。


🕵️ 破案:这 300 秒是从哪来的?

Section titled “🕵️ 破案:这 300 秒是从哪来的?”

既然污点是 NoExecute (立刻驱逐),为什么 Headlamp 还能在上面赖 5 分钟?这是因为 API Server 在创建普通 Pod 时,默认、悄悄地给它们注入了限时的容忍度。

🕵️ 求证指令:抓出 Pod 身上被暗中注入的“宽限期”

Terminal window
# 查看 Headlamp 这个 Pod 的详细配置
kubectl describe pod <你的-headlamp-pod-名字> -n kube-system | grep -A 5 Tolerations

输出结果:你会赫然发现这样两行配置:

node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s

官方文档解读:“这些自动添加的容忍度意味着 Pod 可以在检测到对应的问题之一时,在 5 分钟内保持绑定在该节点上。” 即:长官让我滚,但我有 300 秒的宽限期。


🏗️ 架构思考:DaemonSet 为什么永远不漂移?

Section titled “🏗️ 架构思考:DaemonSet 为什么永远不漂移?”

Ciliumkube-vip 这类组件,即便过了 5 分钟也不会去别的节点重新拉起。

🕵️ 求证指令:看看 DaemonSet 的容忍度有何不同?

Terminal window
# 使用 -A 10 确保能把 DaemonSet 身上长长的一串特权全打印出来
kubectl describe pod -l app.kubernetes.io/name=kube-vip-ds -n kube-system | grep -A 10 Tolerations

输出结果:你会发现它们也有 not-readyunreachable 的容忍度,但是没有 for 300s 这个时间限制!

官方文档明确指出:“DaemonSet 中的 Pod 被创建时,针对不可达污点添加的 NoExecute 容忍度,将不会指定 tolerationSeconds。这保证了出现上述问题时 DaemonSet 永远不会被驱逐。”


⚡ 极客进阶:掌控驱逐倒计时的“快与慢”

Section titled “⚡ 极客进阶:掌控驱逐倒计时的“快与慢””

作为集群的架构师,你可以通过在 YAML 的 spec.tolerations 中显式声明,来覆盖这默认的 300 秒:

  • 场景 A(无状态前端):想让面板瞬间漂移?配置 tolerationSeconds: 30(警告:容易引发网络抖动时的重建风暴)
  • 场景 B(有状态存储):数据库绑了本地硬盘?必须延长保命!配置 tolerationSeconds: 6000 死等网络恢复。

  1. 架构侧:核心业务增加副本数(Replicas >= 2)配合反亲和性,是应对这 5 分钟断档期的最优雅解法。
  2. 应急侧:演练时不想等待?直接执行强制抹除:kubectl delete pod <POD_NAME> -n <NAMESPACE> --force --grace-period=0
  3. 排查侧:想要系统性掌握这套机制的排查手法?👉 点击查看《赛博工地巡检手册:污点与容忍度排查指令集》

赛博赶羊战术:不改 YAML 强制定向调度 Pod

在赛博堡垒的日常巡检中,我们偶尔会萌生一些“非分之想”:我想把正在运行的 Headlamp 面板,强行从 01 节点挪到 03 节点上去。

按照正规军的做法,我们应该去修改 Deployment 的 YAML 文件,加上 nodeSelector 或节点亲和性。但如果这仅仅是一次临时测试,为了这么点小事去改核心图纸,未免太兴师动众。如果给 0102 节点打 NoSchedule 污点,又会像“核弹打蚊子”一样误伤集群里的其他无辜业务。

今天,包工头教你一招极客圈里经典的“障眼法”——利用节点封锁 (Cordon) 机制的“赛博赶羊战术”


🐑 战术核心:关上多余的门,只留一条路

Section titled “🐑 战术核心:关上多余的门,只留一条路”

Kubernetes 提供了一个专门用于节点临时维护的命令:kubectl cordon(封锁)。 当节点被 Cordon 后,它会被打上 SchedulingDisabled 的标记。这就像给节点挂上了“暂停营业”的牌子:已经在里面吃饭的顾客(运行中的 Pod)不受影响,但绝对不接待新客(新创建的 Pod 无法调度进来)。

我们的战术逻辑极其简单:把不想去的节点全封锁,然后把 Pod 杀掉让它重生。K8s 大脑环顾四周发现只有一台节点“开着门”,就只能乖乖把 Pod 丢进去。


假设我们要把 kube-system 命名空间下的 Headlamp 强行赶到 k3s-master-03 节点。

第一步:封锁非目标节点 (关门)

Section titled “第一步:封锁非目标节点 (关门)”

我们要逼迫目标去 03,所以先把 0102 的大门焊死:

Terminal window
kubectl cordon k3s-master-01 k3s-master-02

验证:此时敲击 kubectl get nodes,你会看到 0102 的 STATUS 变成了 Ready,SchedulingDisabled

第二步:击杀当前 Pod (放狗咬羊)

Section titled “第二步:击杀当前 Pod (放狗咬羊)”

直接删掉当前正在运行的 Headlamp Pod。由于它是由 Deployment 管理的,K8s 会瞬间拉起一个新的替代品。

Terminal window
kubectl delete pod -l app.kubernetes.io/name=headlamp -n kube-system

去看看新重生的 Headlamp 落在哪了:

Terminal window
kubectl get pods -n kube-system -o wide | grep headlamp

因为大脑别无选择,你会发现新的 Pod 已经精准无误地降落在了 k3s-master-03 上!微操成功!

第四步:光速解除封锁 (极其重要!)

Section titled “第四步:光速解除封锁 (极其重要!)”

羊已经进圈了,赶紧把 0102 的大门重新打开,否则你后续部署的任何新业务都会因为找不到节点而卡在 Pending 状态。

Terminal window
kubectl uncordon k3s-master-01 k3s-master-02

验证:再次 kubectl get nodesSchedulingDisabled 标记消失,集群恢复常态。


虽然这套连招玩起来行云流水,极其酷炫,但我必须提醒你:它终究是一张“体验卡”。

Kubernetes 的核心信仰是声明式架构(Declarative)。Cordon 大法只是一次性欺骗了调度器。如果明天 03 节点重启了,或者 Headlamp 意外崩溃了,K8s 大脑在重新调度时,面对三扇全开的大门,依然会随机把 Headlamp 扔回 0102。系统并没有记住你的真实意图。

总结:

  • 临时测试、排查干扰:用 Cordon 大法,干净利落。
  • 永久固化、生产要求:请务必遵守纪律,老老实实去写 nodeSelectornodeAffinity。这才是让 Tech Fortress 坚不可摧的终极规范。