关键在于Pod必须设置spec.schedulerName匹配自定义调度器名称,否则默认调度器会抢先绑定;需确保Pod定义、调度器启动参数和RBAC权限三者一致,并通过Informer监听指定schedulerName且nodeName为空的Pod。

直接对接自定义调度器的关键是让 Pod 明确“找谁调度”——必须设置 spec.schedulerName,否则默认调度器会抢先绑定,你的调度器永远收不到待调度 Pod。
为什么 Pod 没触发你的调度器?
最常见原因是没设 spec.schedulerName,或设错了值。Kubernetes 调度器只监听 spec.nodeName == "" 且 spec.schedulerName == "your-scheduler-name" 的 Pod。默认调度器监听的是 spec.schedulerName == "default-scheduler"(或该字段为空)。
- Pod 创建时未指定
spec.schedulerName→ 默认调度器接管 → 你的调度器收不到事件 - 你设了
spec.schedulerName: "my-scheduler",但调度器启动时用的 ConfigMap 或命令行参数里注册名是custom-scheduler→ 匹配失败,Pod 卡在 Pending - Deployment/StatefulSet 等控制器生成的 Pod 不继承父级的
spec.schedulerName,必须在 Pod template 中显式写入
如何正确配置 Pod 使用你的调度器
不是改 YAML 就完事,要确保三处一致:Pod 定义、调度器启动参数、集群 RBAC 权限。
- 在 Pod 或 Pod template 中硬编码:
spec.schedulerName: "my-scheduler" - 调度器启动时通过
--scheduler-name=my-scheduler参数注册自身名称(若用 kube-scheduler 二进制扩展);若手写 Go 调度器,则在 Informer 的 ListOptions 或 Binding 对象中不依赖该参数,但必须确保只监听带该字段的 Pod - RBAC 必须授予对
bindings子资源的create权限,否则调用client.CoreV1().Pods(pod.Namespace).Bind()会报错Forbidden: unable to create pods/bindings
Binding 操作失败的典型错误
手写调度器最容易卡在这里:你以为调用了 Bind API 就完成了,其实 Kubernetes 对 Binding 请求校验极严。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 错误信息
Operation cannot be fulfilled on pods "...": the object has been modified→ 说明 Pod 在你查询后被其他组件(比如默认调度器)抢先更新了spec.nodeName,需加重试或用 Patch 替代全量 Update - 错误信息
the server does not allow this method on the requested resource→ clientset 权限不足,检查 ClusterRole 是否包含resources: ["pods/binding"] - 用
client.CoreV1().Pods(ns).Bind()时,Binding.Target.Name必须是真实存在的 Node 名,且该 Node 不能是NotReady状态,否则绑定成功但后续 Pod 启动失败
监听范围与性能陷阱
别用 client.CoreV1().Pods("").List() 全量轮询 —— 这在百节点以上集群会拖垮 API Server 并导致延迟飙升。
- 必须用 SharedIndexInformer 监听 Pod,过滤条件为:
pod.Spec.NodeName == "" && pod.Spec.SchedulerName == "my-scheduler" - Node Informer 也要同步监听,避免每次调度都 List 所有 Node;用
indexer.ByIndex("label", "role=special")加速查找 - Filter 阶段(预选)逻辑务必轻量,比如标签匹配、污点检查;复杂打分(如实时 GPU 利用率)建议异步查外部服务并缓存结果,避免阻塞 Informer 处理队列
真正难的不是写通第一个 Binding,而是让调度器在多副本、高并发、节点频繁上下线的生产环境里不丢事件、不重复调度、不和默认调度器打架——所有这些都藏在 Informer 的 resyncPeriod、Workqueue 的 rate limit 和 client-go 的 retry logic 里,而不是主逻辑里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










