Pod 反亲和表达一个 Pod 应该避开哪些选中的同伴。主机拓扑可以减少共用节点的风险,可用区拓扑可以覆盖更大的故障域。规则不会创造额外容量,也不能证明应用已经具备高可用性;它负责影响落点,复制机制、就绪、存储与故障恢复仍然有各自职责。三个副本分开只是减少共同故障的一步,并非完整恢复方案。

强制规则过滤候选节点,偏好规则贡献评分。如果硬条件没有留下可行节点,新 Pod 会持续 Pending,直到相关条件改变。对严格隔离要求而言,这可能是正确结果,但必须提前计入容量规划,而不是在故障时才发现“不允许同处”的代价。偏好则允许权衡,不应被描述成几乎等同于硬性分离。

选中同伴,再定义拓扑域

labelSelector 识别同伴 Pod,topologyKey 指定用哪个节点标签划分它们所在的位置。kubernetes.io/hostname 通常区分节点,topology.kubernetes.io/zone 按可用区分组。应查看真实标签值,不要假定不同基础设施提供者都自动具备同一套标签;键存在但值错误,同样可能让故障域定义失真。

命名空间范围也是规则的一部分。既没有显式命名空间列表、也没有 namespaceSelector 时,默认匹配待调度 Pod 自己所在的命名空间。空的 namespaceSelector 对象则匹配所有命名空间。因此经过配置后,规则可以跨命名空间影响调度,并不是天然的租户隔离边界。审阅时必须把同伴标签与命名空间选择一起看,不能只看 app 名称。

相关节点需要一致的拓扑标签。Kubernetes 文档明确警告,缺少标签会导致意外的反亲和行为,但不能把这个警告简化成“所有未标记节点必定被接纳”或“必定被排除”。Pod 间规则、既有落点和 topology spread 是不同机制,需要查看真实规则与候选集合。直接把两种机制的缺失标签表现混在一起,会导向相反而同样错误的诊断。

硬规则可能形成放置上限

对于互相被选择器选中的副本,强制 hostname 反亲和通常让每个合格主机域最多容纳一个匹配副本。九个这样的副本放到六个合格节点,仍会有三个等待,哪怕节点还有很多 CPU。滚动更新的额外副本也可能先撞到同一道上限,所以容量规划不能只计算稳定状态的 replicas。

强制可用区反亲和同样会限制每区匹配同伴数量,但还需要检查准入配置。启用 LimitPodHardAntiAffinityTopology 时,强制反亲和的 topologyKey 被限制为 hostname。不能假定任何集群都会接受区域级硬规则,更不应在未核对允许拓扑之前就把它写进可用性设计。命令行没有执行到调度阶段,可能是准入先拒绝了清单。

IgnoredDuringExecution 表示这些调度规则不会在条件变化后自动驱逐或重新分配既有 Pod。更新工作负载模板影响替换 Pod,不会立即重排全部现有副本。已经存在的硬反亲和也可能限制新 Pod,因此只查看待调度对象的一条偏好 term 并不足够;还要理解当前同伴及其限制。

Kubernetes 1.34.0 的评分插件也会考虑既有 Pod 的偏好 term;当新 Pod 没有 Pod 间偏好时,一个配置选项可以跳过这部分计算。因此,不能无条件宣称已经运行的 Pod 的偏好总会被忽略,实际行为需要结合版本与调度器配置。

用拓扑分布表达有界不均衡

Topology spread 表达的契约不同于“每个域只许一个同伴”。采用 DoNotSchedule 时,maxSkew 限制候选域中的匹配 Pod 数与合格域全局最小值的差。采用 ScheduleAnyway 时,降低偏斜只是偏好。多条约束组合时,每条硬约束都必须可满足;增加一条看似合理的规则,也可能让原先可行的交集消失。

假设两个同样合格的区分别有二和一个匹配 Pod,maxSkew 为一。再放进较满的区会让相关差值变成二,放进较空的区则保持平衡。若合格域数量少于 minDomains,全局最小值改为零,并不是提高。该选项只能与 DoNotSchedule 配合,能够有意阻止进一步集中,代价是新的 Pod 可能无法调度。

均衡结果依赖标签、选择器、资格条件与放置顺序,并不是九个副本永远自动形成三三三。Pod 间亲和在大型集群可能增加明显的调度计算成本。应优先选择能清晰表达目标契约的机制,并实际测量调度性能,不要把“另一种规则一定更便宜”当作无需条件的保证。可读性与真实性比套一个统一模板更重要。

坏掉的样子

合格域不足时,额外副本可能 Pending,而 HPA 仍继续建议增长。如果规则限制每可用区一个,在已经占用的区增加节点并不能解决问题。放宽硬规则会改变可用性契约,应该明确接受这个取舍,不能把它当成没有后果的普通调度修补。

缺少拓扑标签、选错同伴范围,都可能让预期分离失去意义。什么同伴都没有选中的规则证明不了任何隔离,而过宽选择器又可能阻挡无关工作负载。判断分布不均之前,先核对命名空间范围与实际标签;并不是每种肉眼看起来不均匀的布局都代表调度器出错。

快速验证

在指定的只读集群上下文和命名空间执行检查。以下原有命令保持不变,本轮没有在集群重新运行。

kubectl get nodes -L topology.kubernetes.io/zone -L kubernetes.io/hostname
kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName,PHASE:.status.phase
kubectl get events --field-selector reason=FailedScheduling --sort-by=.lastTimestamp | tail -5
kubectl get pods -o jsonpath='{range .items[*]}{.spec.nodeName}{"\n"}{end}' | sort | uniq -c

这些 Pod 列表覆盖整个命名空间,不一定等于规则选中的同伴集合。空节点名包括尚未调度的 Pod,空拓扑单元格则暴露标签缺失。根据实际选择器与命名空间范围筛选观察结果,再比较域和事件。每节点恰好一个 Pod,不证明反亲和就是原因;数量不均也不能单独证明某条分布约束被违反,必须把真正的候选域和 maxSkew 一起计算。

来源