背景与问题界定

在大型 Kubernetes 集群中,Pod 调度直接影响应用性能和集群资源利用率。我们曾遇到一个典型场景:一个日活超千万的微服务集群,由于 Pod 被随机调度到不同可用区和机架,导致跨节点网络延迟增加 40%,P99 延迟从 50ms 飙升到 120ms。更严重的是,GPU 密集型任务被调度到没有 GPU 能力的工作节点,造成大量重调度和资源浪费。Kubernetes 调度器虽然有默认的 predicates 和 priorities 机制,但在超大规模集群(2000+ 节点)下,默认策略无法满足复杂拓扑约束和性能需求,必须通过精细化的调度策略来优化资源编排。

目标拆解与工程约束

  1. 多维度拓扑亲和性要求:Pod 需要优先调度到同一可用区(AZ)以降低跨 AZ 流量费用和延迟,同时避免将同一服务的多个副本聚集在同一故障域,防止单点故障导致整个服务不可用。
  2. 异构资源感知:集群混合部署了 CPU 密集型、内存密集型和 GPU 加速型工作负载,调度器必须能够感知节点上的 GPU 型号、本地 NVMe 磁盘等特殊资源,避免资源错配。
  3. 调度性能和稳定性:在 2000+ 节点的集群中,每个 Pod 的调度延迟需要控制在 100ms 以内,调度器高可用必须保证故障切换时不会丢失已有的调度决策和数据。
  4. 动态资源竞争:高峰期资源争抢严重,需要实现优先级抢占(Priority Preemption),确保关键业务 Pod 在资源不足时能够抢占低优先级 Pod 的资源,同时保证被抢占 Pod 的优雅终止。

方案设计

我们采用分层调度策略体系来应对上述挑战。第一层是基础的 nodeSelectornodeAffinity,用于硬性约束——通过 nodeSelector 将 GPU 工作负载固定到带有 gpu=true 标签的节点池,避免调度到 CPU 节点。第二层引入 Pod 间亲和性(podAffinity)和反亲和性(podAntiAffinity),使用 topologyKey: topology.kubernetes.io/zone 将同服务的多个副本分散到不同的可用区,同时将缓存层和数据层调度到同一节点以降低延迟。

第三层是拓扑感知调度(Topology Aware Scheduling),这是 Kubernetes 1.27 进入 GA 的关键特性。我们利用 topologySpreadConstraints 实现跨拓扑域的最大均匀分布。例如对于日志采集 DaemonSet,配置 maxSkew: 1 确保每个可用区的 Pod 数量偏差不超过 1,避免单区过载。对于有状态服务 StatefulSet,通过 whenUnsatisfiable: ScheduleAnyway 配合 minDomains 实现软约束,保证节点滚动更新时调度器不会过度分散导致 Pod 创建失败。

在调度器扩展方面,我们部署了 scheduler-plugins 项目中的 CoschedulingNodeResources 插件,实现了 Gang Scheduling——确保 MPI 训练任务的所有 Pod 同时得到调度,避免部分 Pod 长期处于 Pending 状态导致死锁。同时自定义了 node-resource-topology 插件,将节点上的 NUMA 拓扑信息上报给调度器,让对内存延迟敏感的应用能够调度到同一 NUMA 节点。

实施路径与关键决策

  • 第一步:梳理集群拓扑结构,为所有节点打标签(zone、region、gpu-type、disk-type),使用 kubectl label node 标准化标签体系,编写自动化脚本从云厂商 API 拉取拓扑信息自动打标。
  • 第二步:对现有 Workload 分类评估,按"硬约束"(必须调度到某类节点)、“软偏好”(优先但允许降级)、“无要求"三类分别设计调度策略 YAML,逐个 namespace 灰度上线。
  • 第三步:部署 descheduler 组件,配置 RemovePodsViolatingNodeAffinityRemovePodsViolatingTopologySpreadConstraint 策略,定期重调度不符合约束的 Pod,确保集群拓扑分布的长期合规性。
  • 第四步:搭建调度性能监控,通过 kube-scheduler 的 metrics endpoint 采集 scheduler_binding_duration_secondsscheduler_e2e_scheduling_duration_seconds 指标,告警阈值设置为 P99 > 500ms。

验证指标与可持续迭代

通过 Prometheus 监控以下关键指标:调度延迟 P99 从基线 120ms 降到 60ms 以下;跨可用区流量减少 60%;GPU 资源利用率从 55% 提升到 78%;Pod 调度失败率(Pending 超过 5 分钟)降低到 0.1% 以下。每两周进行一次调度策略审计,使用 kube-iptables-tailer 配合准入 Webhook 自动检测不符合最新拓扑策略的 Workload 并生成 JIRA 工单。

工程落地思考

Kubernete 调度策略不是一次性配置就能一劳永逸的工作。我们踩过的最大坑是过度依赖 nodeAffinityrequiredDuringSchedulingRequiredDuringExecution 导致滚动更新时 Pod 无法重调度——新节点标签变更后旧 Pod 不会自动驱逐。最佳实践是谨慎使用硬约束,优先用 preferredDuringScheduling 配合 descheduler 实现最终一致性。拓扑感知调度的 maxSkew 参数也需要根据集群规模动态调整,2 节点集群和 200 节点集群的容忍度完全不同。调度策略的本质是在资源利用率和应用性能之间找平衡,没有银弹。