两个步骤,把节点优雅地带出集群。

cordon 与 drain 让节点安全离线

cordon 和 drain 是两件事,只是大家习惯一口气说完。cordon 给节点打上 spec.unschedulable,让调度器不再选它;drain 则把已经在上面跑的 Pod 请走。前者瞬时完成、随时可逆,后者是一个循环,要逐个 Pod 去跟驱逐 API 打交道。 矛盾在于:drain 是一个带安全护栏的客户端流程。它在删除任何东西之前会先拒绝,而每一个能越过拒绝的参数,都在拿掉一部分护栏。搞清楚哪个参数对应哪一类拒绝,就是维护窗口和线上事故之间的差别。 cordon 关门,drain 清场 kubectl cordon node-1 把 spec.unschedulable 置为 true,调度器随即把该节点从新 Pod 的候选里滤掉。已经在跑的 Pod 照常服务,这正是要先 cordon 的原因:先停止接收新工作,再开始搬走已有的工作。cordon 也是让 drain 有意义的前提——不封门,调度器会把替代副本直接放回你正在清空的那台机器上。kubectl drain 自己也会打这个标记,所以单独先执行一次 cordon 只是稳妥,不是必需。kubectl uncordon 就是反向的同一个补丁,调度器立刻会重新使用这台节点——先确认 kubelet 已经 Ready 再放它回去。 ...

二月 3, 2028 · 2 分钟 · XB Field Notes
让调度优先选择或强制要求某些节点标签的规则。

节点亲和把工作负载放到合适的节点

亲和性是放置匹配:可以"偏好"某个标签也可以"强制"它,再用反亲和把副本散开,换取更强的韧性。硬件从来不会长期均匀——这边一池 GPU,那边高速本地 NVMe,还有合规数据的隔离区——而工作负载悄悄依赖落到对的地方。没有显式规则,调度器只按空闲容量铺,需要 GPU 的 Pod 落到没有 GPU 的节点,在运行时而不是调度时才失败。 亲和把这次失败前移到它该在的位置。 placements 写成节点上的数据(标签)配 Pod 上的规则,可 review 的配置代替了"哪些节点是特殊的"部落知识。读 manifest 的人一眼看出工作负载必须跑在哪、为什么。 按标签匹配节点 亲和性规则读取节点标签,让你说明某个 Pod 应该、甚至必须落在带某个标签的节点上。把稳定的标签一次性打在节点上,剩下的就让调度器去匹配。accelerator=nvidia-a100、storage=nvme、zone=regulated 这类标签描述机器的持久属性;因为它们住在 node 对象上,换机器也不影响引用它的 Pod 规则——新硬件打同样的标签,老规则继续生效。 标签名要像 API 字段一样认真起,因为改一个被广泛引用的标签名,就是跨所有匹配它的 manifest 的迁移。约定小而有文档的词表,胜过每队自创 key:workload-class、hardware、compliance 覆盖大多数家底,再配半页约定文档,免得 GPU 标签拼出五种写法。像校验镜像 tag 一样在 CI 里校验标签——要求一个没有任何节点携带的标签的 Pod,是该死在流水线里的拼写错误,而不是凌晨三点的发布。 偏好还是强制 偏好型规则尽力而行,条件允许就把负载摊开;强制型规则则硬性拒绝那些满足不了标签的 Pod。只有放置真地有讲究才用强制,否则偏好能留给调度器更多弹性。成本模型很直白:强制拿可用性换正确——没有匹配节点有位子,Pod 就 Pending,也不去错的地方;偏好拿正确换可用——落到次优但活着的地方。 ...

十二月 9, 2027 · 1 分钟 · XB Field Notes
一个节点级偏好配合一个 Pod 级容忍,共同决定调度准入。

污点会排斥那些不打算容忍它的 Pod

污点是一块“闲人勿进”的牌子,容忍度则是通行证,于是你可以要求预算型、GPU 或竞价节点只接纳匹配的负载。 给专用者的标记 污点(taint)把节点标成「闲人勿进」,而它本质上是一块牌子,不是一个动作:光打污点并不会把已经运行的东西搬走。新来的 Pod 只要没带匹配的容忍度就绕开,容忍度才是那张「通行证」。这正是你为一个子集群留出专属硬件的方式:GPU 节点、竞价节点、带许可证的机器,只接纳确实需要的负载,而其它 pod 仿佛这批节点不存在。由于它默认按范围生效,一套集群里可以同时住着通用池和专用池,而不必靠一堆标签去捋。 效果决定行为 真正把「牌子」变成「行为」的是污点的 effect 字段。NoSchedule 只挡住新 Pod,对已在运行的不动,是你不想再往里面塞容量时的默认选择。PreferNoSchedule 是软偏好,倾向于、但不禁止,适合折价的池而非硬约束。NoExecute 则会把已经绑上去的 Pod 也驱离,它也常常被拿去「把一个节点排空」:给节点打上 NoExecute,调度器会主动把其上的运行移走。挑哪种 effect,取决于你这一刻真正要的是「拦住」还是「清空」,因为软排除与硬排除是两种截然不同的保证。 配对即约定 容忍度与污点要靠 key、运算符与 effect 三者完全对上才算匹配,因此这层配对是明文、是可以被审查的契约。一份容忍度表达的是「在我能扛住这种污点的节点上生活」,它既不表达偏爱、也不会强制去哪台机器,因此一个带容忍度的 Pod 仍可能落在别处。这正是为什么一个集群既能有一批 GPU 节点、又能有一批通用节点而不互相摸错:放不下的 Pod 就不会被放到那里,而专用负载稳当地落在它们需要的硬件上。把这份配对当成正式的约定去写,哪种代价、为什么要加,到期日是什么,否则契约就会慢慢变成一锅标签粥。 坏掉的样子 最贵的故障是过宽的容忍度污染了专用池。一条写着 operator: Exists 却不带值的容忍度,或一份被复制进公共图表的容忍度,会匹配该键下的所有取值,于是普通发布突然有资格落到图形处理器或竞价节点上。预留池被廉价负载占满,而它真正要服务的专用负载反而长期等待调度。这恰好与守护进程集的故障互为镜像:那边是缺了容忍度导致专用节点没有覆盖,这边是多了容忍度导致专用节点失去保护。请把容忍度当作权限通配符来审计,公共模板里必须写清键值与效果三者。 第二种故障是把本该用 NoSchedule 的地方写成了 NoExecute。前者只拦截新负载,后者会把没有匹配容忍度的运行中负载直接驱离,还可能在 tolerationSeconds 倒计时结束后动手。一个维护污点若选错效果,几秒钟就能排空整组生产负载,而从示例里顺手复制的六十秒容忍倒计时,会把一次预留变成一场无人排期的延迟驱逐。与亲和不同,亲和从不驱逐,而污点侧的效果字段自带破坏力,因此维护污点先在单台节点演练,看过负载分布再向整池推广。 ...

二月 25, 2027 · 2 分钟 · XB Field Notes