题干与适用场景
这道系统设计题面向平台工程师、DevOps、SRE 和负责 Kubernetes 运行时的后端工程师。题目同时要求高可用、资源利用率和滚动扩缩容。回答不能只贴一段 YAML,必须解释故障域、候选 Pod 集合、skew 计算、硬软约束和调度失败后的运维动作。
面试官考察点
- 能否把“跨可用区”翻译成正确的
topologyKey、标签选择器和maxSkew。 - 能否区分
DoNotSchedule的硬约束与ScheduleAnyway的软偏好,并说明业务代价。 - 能否发现标签不匹配、区域无节点、节点污点、滚动发布和缩容造成的隐藏风险。
- 能否用 Pending、skew、可用区容量和故障演练证明方案,而不是把 YAML 当成保证。
回答前需要澄清的问题
先确认“区域不可用”是整区故障、节点池暂时为空,还是仅资源不足;确认服务是否允许降副本、跨区流量和更高成本。询问期望的最小副本数、单 Pod 资源请求、Pod 标签是否由同一个 Deployment 管理、是否有多个版本同时滚动,以及是否必须至少覆盖几个域。若必须覆盖的域数小于 minDomains,硬约束可能让 Pod 长时间 Pending,这需要业务明确取舍。
30 秒回答框架
我会先用 topology.kubernetes.io/zone 作为区域故障域,再用 kubernetes.io/hostname 作为节点级补充约束,并确保 selector 与 Pod 自身标签一致。maxSkew: 1 和 DoNotSchedule 适合副本数足够且不能接受集中;容量紧张或只需要偏好时改用 ScheduleAnyway。如果要保证至少覆盖 N 个区域,再评估 minDomains,同时准备 Pending 告警、跨区流量和降级策略。最后通过节点驱逐、区域缩容、滚动更新和扩容回放验证实际分布。
分步骤深入解答
1. 先定义域和统计集合
topologyKey 指定域的节点标签,例如区域或主机名;labelSelector 决定哪些已有 Pod参与 skew 计算。selector 必须匹配该工作负载的 Pod,否则 Pod 可能不计入自己的分布,形成“ghost pod”,调度结果看似满足却与实际副本不符。域标签还要在所有相关节点上稳定存在。
2. 从 maxSkew 推导硬约束
maxSkew 限制候选节点所在域与最少 Pod 域之间允许的差值。假设三个区域当前计数为 1、1、0,maxSkew: 1 时把新 Pod放到空区域不会超过差值;如果放到已有 1 个 Pod 的区域,差值变成 2,硬约束会拒绝。真实计算还受 selector、节点过滤和可用域集合影响,不能只按 Deployment 副本数心算。
3. 选择 DoNotSchedule 或 ScheduleAnyway
DoNotSchedule 把 skew 当作过滤条件,无法满足时 Pod 保持 Pending;它适合“集中到一个故障域就违反可用性目标”的服务。ScheduleAnyway 仍会优先降低 skew,但在容量不足时允许落到较差域,适合先保住容量和可用性的服务。选择应由副本不足的业务损失与跨区成本共同决定,并配套告警而不是静默接受失衡。
4. 用第二个约束限制节点级集中
区域均衡不保证同一区域内不会把多个副本放到同一节点。可以增加 topologyKey: kubernetes.io/hostname 的约束,但要重新评估节点数量和资源请求。节点级 DoNotSchedule 在小集群或大 Pod 下很容易阻塞滚动更新;此时可把节点级条件设为软偏好,或先扩容节点池再发布。
5. 处理 minDomains、污点与空域
如果要求至少在若干域上分布,可使用 minDomains 配合 DoNotSchedule;它要求满足的域数有明确前提,不能把“集群目前没有该域节点”误当成已经覆盖。节点亲和、污点容忍和 nodeAffinityPolicy、nodeTaintsPolicy 会改变参与计算的节点集合。自动扩缩容还可能把某个节点池缩到零,使调度器暂时看不到该域;需要与 autoscaler 的拓扑感知能力和节点标签治理配合。
6. 为故障、发布和缩容建立验证回路
在类生产集群记录每个区域、节点的匹配 Pod 数、Pending 原因和跨区请求比例。演练驱逐一个区域、让一个区域无可调度容量、把副本从 2 调到 15、执行滚动更新和缩容;检查服务可用副本、skew、发布耗时、流量成本和恢复时间。缩容后分布可能暂时失衡,不能只看一次 kubectl get pods,要验证控制器后续是否重新调度或由 descheduler 修复。
高质量示范回答
我会把区域作为第一故障域、节点作为第二故障域,并让 selector 精确匹配 Deployment 的标签。区域约束可从 maxSkew: 1 开始:对关键 API 使用 DoNotSchedule,让无法满足时的 Pending 直接暴露容量问题;对允许短时集中或集群经常容量紧张的服务使用 ScheduleAnyway,但用 skew 和可用副本告警。若业务必须至少覆盖三个区域,再评估 minDomains 与区域节点池是否始终存在。节点约束要根据节点数和 Pod 资源请求决定硬或软,不能盲目叠加。最后用区域驱逐、零容量域、扩缩容和滚动更新演练,检查 Pending 原因、skew、跨区流量与恢复时间。
常见错误
- 只写
topology.kubernetes.io/zone→ 同一区域内仍可能集中到一个节点 → 按容量增加节点级约束并评估成本。 - 把
maxSkew: 1当成永远均匀 → 标签集合、可见域和缩容会改变统计 → 记录实际匹配 Pod 与域集合。 - 所有服务都使用
DoNotSchedule→ 小集群或单区容量不足会造成发布阻塞 → 按业务损失选择硬或软策略,并告警失衡。 - selector 不匹配 Pod 标签 → 自身副本可能不参与计算,产生 ghost pod 行为 → 让模板标签与 selector 有契约测试。
- 忽略区域节点池缩到零 → 调度器暂时不知道该域,期望的覆盖可能无法实现 → 让 autoscaler 与拓扑标签和域容量共同治理。
追问及应对
两个副本、三个区域时能保证每区一个吗?
不能同时满足三域各一个和只有两个副本。应明确“至少覆盖两个域”或允许一个域没有副本;硬约束只能避免超过设定 skew,不能凭空增加副本。若业务要求三域冗余,副本下限必须至少为三,并验证每个域都有可用容量。
为什么 Pod 仍然 Pending?
检查事件中的 spread constraint、节点标签、selector、资源请求、污点容忍、亲和规则和 minDomains。区分“没有节点满足过滤条件”和“软约束评分较低”;必要时先扩容目标域,再降低硬约束或暂时调整发布策略。
ScheduleAnyway 会不会让高可用目标失效?
会降低保证强度。它只提高满足 spread 的节点得分,容量不足时仍可能集中。只有业务接受短时失衡且有可观测告警、自动修复和容量计划时使用;关键控制面或有明确故障域目标的服务保留硬约束。
滚动更新时旧版本和新版本如何一起统计?
selector 若同时匹配两个版本,旧 Pod 会影响新 Pod 的 skew;若只匹配新版本,发布期间可能暂时失去整体分布约束。应把版本标签策略、matchLabelKeys 的使用和最大不可用副本一起演练,确认升级高峰不会让两个版本挤在同一域。
缩容后失衡需要立即搬迁吗?
拓扑约束主要影响新 Pod 调度,移除 Pod 不会自动保证剩余 Pod再次均匀。若失衡影响风险,使用 descheduler 或滚动重建,并先验证迁移对容量、PDB、延迟和跨区流量的影响。