3. Kubernetes调度器架构
kube-scheduler 是 Kubernetes 控制平面中唯一负责“把 Pod 放到哪个节点”的组件。它本身不创建容器,也不修改节点上的运行时状态,而是通过 API Server 监听未绑定的 Pod,经过一轮筛选与打分后,向 API Server 写入一条 Binding 记录,由 kubelet 完成后续的容器启动。理解它的架构,是理解 AI 任务调度改造空间的起点。
核心组件:一条从队列到绑定的流水线
kube-scheduler 内部可以拆成四个关键部分。第一是调度队列,分为 activeQ、backoffQ 和 unschedulableQ:新创建或重试的 Pod 进入 activeQ;调度失败需要退避的 Pod 进入 backoffQ;因资源不足、亲和性不满足等暂时无法调度的 Pod 进入 unschedulableQ,等待集群事件(如节点扩容、其他 Pod 释放资源)触发后重新入队。第二是调度缓存(Scheduler Cache),它保存了节点、Pod 及资源占用的快照,使调度决策不必每次全量查询 API Server,是性能的关键。第三是调度框架(Scheduling Framework),把调度过程拆成若干扩展点,允许插件在固定位置介入。第四是绑定器(Binder),负责把选中的节点写回 API Server。
对 AI 任务而言,调度队列的语义尤其重要:一个等待 8 张 GPU 的训练 Pod 如果被反复取出又放回,会带来大量无效计算。因此在大规模 AI 集群中,常需要结合队列排序策略、Pod 优先级与抢占,让“大块资源需求”的任务尽早获得确定性结果。
调度周期:一个 Pod 的两阶段决策
单个 Pod 的调度分为调度周期(Scheduling Cycle)和绑定周期(Binding Cycle)。调度周期是串行的:同一时刻只处理一个 Pod,依次执行 PreFilter、Filter、PostFilter、PreScore、Score、NormalizeScore、Reserve、Permit。绑定周期则可以并发执行:PreBind、Bind、PostBind。这种“决策串行、绑定并行”的设计,使调度器在保证一致性的同时提升吞吐。
其中 Filter 阶段决定“哪些节点可行”,Score 阶段决定“可行节点中哪个最优”。AI 任务常见的约束——GPU 型号、显存容量、NVLink 拓扑、RDMA 网卡、节点是否已缓存模型——大多落在 Filter;而“尽量把同一任务的 Pod 聚到同一 NUMA 域”“尽量分散到不同故障域”则落在 Score。Reserve 阶段用于在真正绑定前占住资源,避免并发调度时资源被重复分配;Permit 阶段则允许插件“等待”,这正是 Gang Scheduling(成组调度)的实现基础。
调度框架扩展点:AI 调度的真正接口
调度框架把上述阶段暴露为可插拔的扩展点,每个扩展点可以注册多个插件,按权重和顺序执行。对 AI 场景,最值得关注的有以下几类:
- Filter 类插件:判断节点是否满足 GPU 数量、显存、拓扑、驱动版本等硬约束。例如一个请求
nvidia.com/gpu: 8的 Pod,只有剩余 8 张同型号 GPU 的节点才能通过。 - Score 类插件:在可行节点中排序。AI 训练常希望同一任务的多个 Pod 尽量落在同一 NVLink 域或同一交换机下,以减少通信开销;推理服务则更关注负载均衡与模型缓存命中。
- Permit 插件:实现 Gang Scheduling。当一组 Pod 无法全部获得资源时,已 Reserve 的 Pod 会等待,超时后回滚,避免“部分启动、整体卡死”。
- Reserve / PreBind 插件:在绑定前完成资源预留、共享存储挂载、环境变量注入等动作,保证 Pod 真正启动时依赖已就绪。
这些扩展点通常通过实现 Kubernetes 的调度框架接口,并以自定义调度器或调度器插件的形式部署。对于 Volcano、Koordinator、Kueue 等面向 AI/批处理的调度系统,本质上都是在这些扩展点上做增强。
与 AI 任务调度的关系:为什么默认调度器不够用
默认 kube-scheduler 面向的是通用在线服务,其假设是“Pod 之间基本独立、资源粒度小、失败可快速重试”。AI 任务打破了这些假设:
- 资源粒度大且不可分割:一个分布式训练任务可能需要几十甚至上百张 GPU,且必须同时获得,否则无法启动。默认调度器逐个 Pod 调度,容易出现“部分 Pod 已绑定、剩余 Pod 永远等不到资源”的死锁。
- 任务内强耦合:训练任务的所有 Worker 需要同时运行、互相通信。默认调度器没有“任务”概念,只看到一个个 Pod,无法保证整组原子性。
- 拓扑敏感:GPU 之间的 NVLink、节点之间的 RDMA 网络带宽差异巨大,放错位置可能使训练速度下降数倍。默认调度器不感知这些拓扑。
- 排队与公平性:多团队共享 GPU 集群时,需要按队列配额、优先级、抢占策略分配资源,默认调度器的优先级与抢占机制粒度较粗。
因此,AI 任务调度通常需要在 kube-scheduler 的扩展点上做定制,或直接使用支持 Gang Scheduling、队列管理、拓扑感知的调度器。理解调度周期与扩展点,才能判断一个需求应该落在 Filter、Score 还是 Permit,而不是盲目替换整个调度器。
kubectl describe pod 查看 Pod 的 Events,确认失败发生在 Filter(如 Insufficient nvidia.com/gpu)还是 Score 阶段。多数“调度不上去”的问题其实是资源不足或约束过严,而非调度器算法问题。一个可观察的调度过程示例
假设集群有 3 个节点,每个节点 8 张 A100。提交一个请求 8 张 GPU 的训练 Pod:
resources:
limits:
nvidia.com/gpu: 8
调度器从 activeQ 取出该 Pod,进入 Filter:节点 A 剩余 8 张、节点 B 剩余 4 张、节点 C 剩余 8 张,则 B 被过滤,A 和 C 进入 Score。若 Score 插件偏好“已缓存模型”的节点,而 A 上已有该模型缓存,则 A 得分更高,最终 Reserve A 的 8 张 GPU 并绑定。此时用 kubectl get pod -o wide 可看到 Pod 被分配到 A;若所有节点都不足 8 张,Pod 会进入 unschedulableQ,并在 Events 中记录资源不足原因,等待节点扩容或其他 Pod 释放后重新入队。
这个例子说明:调度结果不是“随机选一个节点”,而是 Filter 与 Score 共同作用的结果。AI 任务调度的工程工作,很大程度上就是在这两个阶段注入正确的约束与偏好,并在 Permit 阶段保证成组任务的原子性。