bigywater.tech
Kubernetes · 44

拓扑分布约束:maxSkew 怎么计算

想让副本均匀分布在不同可用区,应该怎么写?maxSkew 到底是怎么算的?已经失衡了会不会自己纠正?遇到不可用的 zone 为什么会卡住?这篇建一个有三个 zone 的集群,把这些情形都真跑了一遍。

2 / 2 / 2 vs 4 / 1 / 1
6 个副本有无 zone 约束时在 zone a、b、c 的分布
0/5 nodes are available
空的 zone 带污点、nodeTaintsPolicy 缺省时 4 个副本 Pending 的原因里包含 didn't match pod topology spread constraints
7 + 2
同时按 zone 与 hostname 约束时 9 个副本里 Running 与 Pending 的数量

按 zone 打散

实测 · 本机执行输出
  topologySpreadConstraints(maxSkew 1,topologyKey zone,DoNotSchedule):zone a=2 b=2 c=2;节点 worker=1 worker2=1 worker3=2 worker4=2
  对照:同样 6 个副本,没有任何约束:zone a=4 b=1 c=1;节点 worker=2 worker2=2 worker3=1 worker4=1

给 4 个 worker 打 zone 标签:worker、worker2 在 a,worker3 在 b,worker4 在 c。6 个副本写上 maxSkew: 1、topologyKey: zone、whenUnsatisfiable: DoNotSchedule,结果每个 zone 2 个(2/2/2)。没有约束时默认调度是按节点打散的,zone a 有两个节点,于是 4/1/1。maxSkew 是任意两个域之间 Pod 数的最大差值。

不会纠正已有的失衡

实测 · 本机执行输出
  先用 nodeName 把 4 个 app=s2 的 Pod 强行放在 zone a 的两个节点上:zone a=4 b=0 c=0;节点 worker=2 worker2=2
  再加 3 个带 maxSkew=1 约束的 Pod:zone a=4 b=1 c=2;节点 worker=2 worker2=2 worker3=1 worker4=2(zone a 从 4 个没有再增加)
    新 Pod gpj59 → worker4(zone c)
    新 Pod h4xwp → worker4(zone c)
    新 Pod l5xqr → worker3(zone b)

先用 nodeName 把 4 个 Pod 强行放进 zone a(4/0/0),再加 3 个带约束的 Pod:它们都避开了 zone a,去了 zone c(2 个)和 zone b(1 个),总数 4/1/2。zone a 没有再增加,但已有的 4 个也不会被挪走——约束只在调度新 Pod 时生效。要纠正得靠 descheduler 或重建 Pod。

空的 zone 会卡住你

实测 · 本机执行输出
  默认(nodeTaintsPolicy 缺省 = Ignore:统计 zone 时还把带污点的 worker4 算进去):zone a=1 b=1 c=0;节点 worker2=1 worker3=1;Pending 4 个
    Pending Pod 的原因:0/5 nodes are available: 2 node(s) had untolerated taint(s), 3 node(s) didn't match pod topology spread constraints. preemption: 0/5 nodes are available: 2 Preemption is not helpfu
  加 nodeTaintsPolicy: Honor(统计 zone 时排除掉 Pod 容忍不了污点的节点):zone a=3 b=3 c=0;节点 worker=2 worker2=1 worker3=3
  whenUnsatisfiable: ScheduleAnyway(只是“尽量”):zone a=3 b=3 c=0;节点 worker=1 worker2=2 worker3=3

给 zone c 唯一的节点打上污点。默认情况下统计各 zone 的 Pod 数时仍把 worker4 算进去:c 永远是 0 个,而 a、b 各放 1 个之后再放就会让差值超过 1,6 个副本只调度了 2 个,其余 Pending:2 node(s) had untolerated taint(s), 3 node(s) didn't match pod topology spread constraints。加 nodeTaintsPolicy: Honor(排除掉 Pod 去不了的节点)或改成 ScheduleAnyway(软约束)都能让它们全部调度,得到 3/3。

多个约束的叠加

实测 · 本机执行输出
  zone 与 hostname 两个约束(都是 maxSkew 1):zone a=2 b=2 c=2;节点 worker=1 worker2=1 worker3=2 worker4=2
  扩到 9 个:zone a=3 b=2 c=2;节点 worker=1 worker2=2 worker3=2 worker4=2;Pending 2 个

zone 和 hostname 同时 maxSkew: 1:6 个副本 2/2/2,节点 1/1/2/2;扩到 9 个只放下 7 个(zone 3/2/2,节点 1/2/2/2),另外 2 个 Pending——两个约束要同时满足,条件冲突时宁可不放。常见的写法是 zone 用 DoNotSchedule、hostname 用 ScheduleAnyway。

分步演示

拓扑分布
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。独立的 5 节点 kind 集群 kx-sched(1 控制面 + 4 个 worker,Kubernetes v1.37.0),实验后删除标签与污点;worker 的 zone 标签由脚本打上(a、a、b、c);Deployment 的 Pod 是 busybox:1.36;分布取自每个 Pod 的 spec.nodeName;第 3 节给 worker4 打 dedicated=other:NoSchedule 污点,每个策略各一个 Deployment;每种情形各一次(集群刚创建时 worker 还是 NotReady,第一次运行因此全部 Pending,脚本现在会等所有节点 Ready 再开始)
    • 没有验证:minDomains 与 matchLabelKeys;nodeAffinityPolicy(与 nodeAffinity 的交互);集群级默认的 topologySpreadConstraints(调度器配置里的 defaultConstraints);滚动更新期间 matchLabelKeys: pod-template-hash 的作用;descheduler;大规模下约束的调度性能;topologyKey 使用云厂商的 topology.kubernetes.io/zone 标签的差别(这里用自定义标签)
    京ICP备2026067502号-1京公网安备11010502063487号