Kubernetes SIG Scheduling 贡献指南:从提交首个 PR 到深入 kube-scheduler 源码
【免费下载链接】communityKubernetes Community Documentation项目地址: https://gitcode.com/GitHub_Trending/com/community
SIG Scheduling 是 Kubernetes 社区中负责 Pod 放置(Pod placement)决策的核心特别兴趣小组,kube-scheduler 正是该小组的代表性成果。本文以仓库中的 sig-scheduling/CONTRIBUTING.md 为主体,系统梳理贡献者从入门到进阶的完整路径:如何找到第一个可认领的 Issue、如何按社区规范提交与审查 PR、调度器代码的技术与风格红线、四层测试体系,并结合仓库内的 SIG 章程、调度器架构文档 与 插件配置开发指南,带你掌握向 Kubernetes 调度器提交高质量代码的实战方法论。
SIG Scheduling 的使命与职责范围
在动手提交代码之前,先明确这个小组"管什么、不管什么"。根据 SIG Scheduling Charter 的定义:
SIG Scheduling is responsible for the components that make Pod placement decisions.
即:SIG Scheduling 负责所有做出 Pod 放置决策的组件。小组构建 Kubernetes 调度器及调度特性,设计并实现允许用户自定义 Pod 在集群节点上放置方式的功能,包括提升工作负载可靠性、提高集群资源利用率、以及强制放置策略等。
In scope(在范围内)
章程明确了 SIG 的职责边界,主要涉及:
- 调度相关特性:如 Node Affinity(节点亲和性);
- kube-scheduler 性能与可扩展性(与 sig-scalability 协作);
- kube-scheduler 可靠性:问题检测与修复;
- Pod 调度 API(与 sig-api-machinery 协作);
- 节点资源管理与集群资源管理(与 sig-node 协作);
- Pod 调度策略(与 sig-auth 协作)。
跨小组的外部流程还包括 kube-scheduler 的 [test grid] 与性能仪表盘(perf dashboard)。
Out of scope(范围之外)
以下领域不属于SIG Scheduling,贡献时不要走错门:
- 网络管理(属于 sig-network);
- 持久化存储管理(属于 sig-storage);
- 资源配额及其他准入策略的强制(属于 sig-api-machinery)。
相关工作组与子项目
从 sig-scheduling/README.md 可以看到,SIG 还赞助了若干工作组(Working Groups),例如 WG Batch、WG Checkpoint Restore、WG Device Management、WG Node Lifecycle、WG Workload-aware Scheduling。同时维护着一批活跃子项目(Subprojects),包括:
- scheduler:即 Kubernetes 主仓库中的
cmd/kube-scheduler与pkg/scheduler; - descheduler:负责已调度 Pod 的再平衡;
- kueue:面向批处理/排队工作负载的调度系统;
- kwok:用于大规模模拟节点/Pod 的轻量工具;
- scheduler-plugins:开箱即用的调度插件集合;
- 以及 cluster-capacity、kube-scheduler-simulator、scheduler-library、dra-driver-topology 等。
如果对调度生态的其他部分感兴趣,同样可以贡献到这些子项目。
开始贡献前的准备:先读这三样东西
贡献指南强烈建议,任何新贡献者在动手之前先完成以下"预习":
- Kubernetes Contributor Guide:本仓库对应的入门文档位于 contributors/guide/README.md,涵盖开发环境搭建、代码规范、贡献全流程;
- Contributor Cheat Sheet:快速参考手册,位于 contributors/guide/contributor-cheatsheet/README.md,是 GitHub 协作体验的 "TL;DR" 速查表,包含分支策略、同步 fork、squash 提交等高频操作;
- 社区成员角色体系:见 community-membership.md。Kubernetes 的贡献角色由低到高分为 Member、Reviewer、Approver、Subproject owner:
| 角色 | 职责 | 核心要求 |
|---|---|---|
| Member | 社区活跃贡献者 | 2 名 Reviewer 担保 + 多次实质性贡献(至少 1 个已合并 PR) |
| Reviewer | 审查他人贡献的质量与正确性 | 至少 3 个月 Member 身份、主审 5 个以上 PR、累计审/合 20 个以上 PR,由子项目 Approver 提名 |
| Approver | 审查并批准代码贡献 | 至少 3 个月 Reviewer、主审 10 个以上实质 PR、累计审/合 30 个以上 PR,由子项目 owner 提名 |
| Subproject owner | 制定子项目方向与优先级 | 由 sigs.yaml 中子项目的owners条目定义 |
这些角色都由OWNERS 文件界定。以 SIG Scheduling 自身的 sig-scheduling/OWNERS 为例,它通过reviewers/approvers引用sig-scheduling-leads团队,并为该目录下的内容打上sig/scheduling标签——这意味着该目录的 PR 审查与批准权落在 SIG 负责人团队身上。
找到你的第一个贡献入口
SIG Scheduling 明确欢迎 PR、Issue、文档、新提案、帮助回答用户问题、参加会议等多种形式的贡献。入门渠道包括:
从 issue 池起步
- first-good-issue:维护一个"非陈旧"(non-stale)的、面向新人的问题池,带
good first issue+sig/scheduling标签,适合完全不知道从哪开始的贡献者; - help wanted:带
help wanted+sig/scheduling标签的待办问题,适合有一定经验、想接手明确任务的贡献者。
报告 Bug
如果发现 bug,请在 kubernetes/kubernetes 仓库下打开 Issue,并遵循以下规范:
- 添加标签
/sig scheduling与/kind bug; - 遵循
Bug Report模板的要求填写(复现步骤、期望行为、实际行为、环境信息等),这能极大帮助维护者定位错误。
提交功能请求(Feature Request)
流程相对完整,核心要点是"先讨论、后实现":
- 在 kubernetes/kubernetes 下打开 Issue,加标签
/sig scheduling与/kind feature,聚焦于 user stories(用户故事),而不是直接给实现方案; - 如果方案的合理性存在争议,可以拿到 SIG 例会(见 sig-scheduling/README.md 的 Meetings 部分)上讨论;
- 如果存在多个实现选项,建议先写一份包含Pros and Cons的文档,分享到 SIG 的邮件列表收集反馈;
- 任何涉及 API 变更或重大重构的特性,必须先提交 Kubernetes Enhancement Proposal(KEP),KEP 走增强提案流程,由 sig-scheduling 维护的 KEP 目录统一管理。
修正过期文档
发现文档过时是很好的切入点,分两种情况处理:
- 网站类文档(用户文档):到 kubernetes/website 仓库开 Issue 或直接提 PR;
- 开发者类文档(面向开发者的文档):到 kubernetes/community 仓库(即本仓库)处理,例如直接修正 sig-scheduling 目录下的内容。
贡献子项目
SIG 维护的子项目清单见 sig-scheduling/README.md 的 Subprojects 一节,每个子项目都有独立的 OWNERS,可以直接参与。
PR 提交流程与协作最佳实践
SIG 长期遵循一套保证代码质量与 git 历史整洁的实践,新贡献者应当内化于心:
审查链路:Reviewer → Approver
- PR 最好先由Reviewer审查:Reviewer 在 PR 上打
/lgtm(Looks Good To Me)表示代码质量与正确性通过; - 随后由Approver审查并批准:Approver 的关注点是整体接受度,包括前后向兼容、API 与 flag 约定、隐蔽的性能与正确性问题、与其他系统组件的交互等;
- 尽量保留机器人自动分配的 Reviewer,除非确实需要某个特定贡献者的专长;
- 关键 bug 修复可以直接分配给 Approver,以加快合入。
Commit 纪律
- 回应 review 意见时,始终新增一个 commit 而不是 amend,这样 Reviewer 可以清晰看到新改动;Reviewer 可能会在合适时机要求你 squash;
- PR 准备合并时执行 squash,把过程性提交压缩成语义清晰的提交,对 git 历史是极大的帮助;
- 代码贡献应当相对小、简单、文档完备、测试充分;功能较大时尽量拆分成增量 PR 分批提交。
沟通与上下文保留
- 无论是线下讨论还是社区会议上的结论,都要回写到对应的 Issue/PR,以保留决策上下文,方便后来者理解"为什么这么做";
- 遇到 TODO 或后续跟进事项,立即开一个 Issue 记录,防止遗忘。
技术与风格指南:kube-scheduler 开发红线
以下指南主要适用于 kube-scheduler 本身,部分子项目也遵循同样的标准。
设计阶段的思考
设计新特性时,必须考虑依赖 kube-scheduler 代码的组件:
- cluster-autoscaler:依赖调度器逻辑做扩缩容预测与模拟;
- scheduler-plugins:基于调度框架扩展调度能力的插件集合;
- kubelet:作为最终执行 Pod 落地的节点代理,与调度结果直接交互。
也就是说,任何调度器改动都不能破坏这些下游组件的假设。
编码规范
- 遵循effective go的 Go 代码风格;
- 日志方面优先使用contextual logging(基于上下文的结构化日志),部分老包仍在用structured logging,两者都属于 Kubernetes 结构化日志演进路线,详见 contributors/devel/sig-instrumentation/ 下的日志迁移文档;
- 编写 API 时遵循k8s API conventions(见 contributors/devel/sig-architecture/api-conventions.md),包括版本化、持久化、DeepCopy 等约定;
- 命名规则:经验法则是——变量名的长度应与它所在作用域的大小成正比,与它的使用次数成反比。局部小作用域可用短名,全局/跨包作用域应当用含义清晰的长名。
测试要求:四层测试体系
kube-scheduler 的测试分层非常明确,贡献者应当按改动的影响范围选择对应层级:
| 层级 | 覆盖内容 | 说明 |
|---|---|---|
| Unit tests | 单函数/单模块逻辑 | 每个改动都应有高覆盖率单元测试 |
| Integration tests | kube-scheduler 内部组件交互(event handlers、队列、缓存、调度周期)与 kube-apiserver | 覆盖组件间协作 |
| E2E tests | 与 kubelet、kube-controller-manager 等其他组件的真实交互 | 端到端验证 |
| Perf tests | 关键/CPU 密集型操作的性能 | 参见 scheduler_benchmarking.md |
测试代码的通用准则
- 遵循DAMP 原则(Descriptive And Meaningful Phrases,描述性且有意义的短语):测试中的重复是允许的,只要每个片段都有描述意义,不要为了 DRY 把测试抽象得晦涩难懂;
- 断言使用
cmp.Diff代替reflect.DeepEqual,以提供有意义的差异比较输出; - 错误比较使用
errors.Is(配合cmp.Diff时用cmpopts.EquateErrors),禁止比较错误字符串; - 充分利用
pkg/scheduler/testing下的现有工具函数(构造 Pod、Node、测试环境的辅助方法); - 避免创建或使用断言库,使用标准库的
t.Error或t.Fatal; gomega和ginkgo只允许在 E2E 测试中使用。
注意:部分现存代码是在这些规范确立之前写的,可能存在违规。指南明确欢迎你通过 PR 把旧代码改到符合标准——这也是一个不错的入门贡献方向。
深入源码:kube-scheduler 架构与代码层次
要写出符合 SIG 期望的代码,必须先理解调度器的整体骨架。仓库中的 scheduling_code_hierarchy_overview.md 提供了权威的代码导航地图。
调度循环:Scheduling Cycle + Binding Cycle
默认调度器有一个无限运行的主循环,每拿到一个 Pod 就执行一轮调度:
- Scheduling Cycle(调度周期,阻塞式):运行调度算法,选出最合适的节点。步骤为:取下一个待调度 Pod → 用算法调度 → 若因
FitError失败,则运行PostFilter扩展点中的抢占(preemption)插件提名候选节点 → 若成功找到节点,执行AssumePod写入调度缓存,再依次运行Reserve、Permit扩展点 → 全部通过后进入绑定周期,同时开始处理下一个 Pod; - Binding Cycle(绑定周期,非阻塞式):依次调用
WaitOnPermit(等待Permit插件设定的条件,如 gang 调度中等待兄弟 Pod 全部 Assume)、PreBind、Bind、PostBind扩展点。任一环节失败时,会触发所有Reserve插件的Unreserve回滚操作(例如释放为 Pod 组预留的资源)。
整体组件连接关系如下图所示,事件处理器负责把 Pod 正确入队到调度队列,缓存持续更新 Pod/Node 快照,每个 profile 拥有独立的调度框架实例:
kube-scheduler 默认调度器架构:事件处理器、调度队列、缓存与调度/绑定周期协同工作
关键代码位置
调度器代码横跨多个目录,从源码结构看,核心位置包括:
cmd/kube-scheduler/app:控制器代码与 CLI 参数定义(符合所有 Kubernetes 控制器的标准搭建方式),负责初始化命令行选项、校验、metrics(/metrics)、健康检查(/healthz)、读取KubeSchedulerConfiguration、构建插件注册表、leader election 等;pkg/scheduler:默认调度器代码根目录,负责装配调度器(初始化缓存、合并 in-tree/out-of-tree 插件注册表、注册事件处理器);pkg/scheduler/core:默认调度算法实现,定义了ScheduleAlgorithm接口与Schedule/Extenders方法;pkg/scheduler/framework:调度框架及其插件;pkg/scheduler/internal:缓存、队列等内部实现;staging/src/k8s.io/kube-scheduler:ComponentConfig API 类型。
调度框架、缓存与快照
- 调度框架(Scheduling Framework):位于
pkg/scheduler/framework,插件初始化时被传入一个framework.FrameworkHandle,提供访问/操作 Pod、Node、clientset、事件记录器等能力; - 调度缓存(Scheduler Cache):捕获集群当前状态,维护节点列表与 assumed pods(已假设运行的 Pod)列表,通过
AssumePod、FinishBinding、ForgetPod三个操作管理假设 Pod——Assume机制让 Pod 在 kube-apiserver 尚未确认前就先"假跑"在目标节点上,从而提升调度吞吐量; - 快照(Snapshot):每次调度周期开始时从缓存生成集群快照,固定集群状态,避免插件在并发处理中看到不一致的数据。
实战范例:为调度插件添加可配置参数
理解了架构之后,最有代表性的"第一个真功能"就是给 in-tree 插件增加配置参数。仓库中的 scheduler_framework_plugins.md 给出了完整六步走流程。假设插件名为FooPlugin,要新增一个可选的整型参数barParam:
第一步:定义并注册结构体
在pkg/scheduler/apis/config/types_pluginargs.go定义调度器内部表示:
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type FooPluginArgs struct { // metav1 is k8s.io/apimachinery/pkg/apis/meta/v1 (package is in staging/src) metav1.TypeMeta BarParam int32 }嵌入TypeMeta以支持 API 元数据的版本化与持久化;+k8s:deepcopy-gen:interfaces注释用于自动生成DeepCopy函数。
然后在staging/src/k8s.io/kube-scheduler/config/{version}/types_pluginargs.go定义带版本的外部表示。为了让"未指定参数"与"显式默认值"可区分,字段使用指针类型——字段为 nil 时框架会填入默认值:
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object type FooPluginArgs struct { metav1.TypeMeta `json:",inline"` BarParam *int32 `json:"barParam,omitempty"` }每新增一个types_pluginargs.go类型,都要在对应的register.go中注册,调度器才能在解析KubeSchedulerConfiguration时识别它。
第二步:设置默认值
KubeSchedulerConfiguration在cmd/kube-scheduler/app/options/options.go中被解析,随后从版本化类型转换到内部类型并填充默认值。在pkg/scheduler/apis/config/v1beta1/defaults.go定义默认值函数:
// v1beta1 refers to k8s.io/kube-scheduler/config/v1beta1 (package is in staging/src) func SetDefaults_FooPluginArgs(obj *v1beta1.FooPluginArgs) { if obj.BarParam == nil { obj.BarParam = pointer.Int32Ptr(42) } }第三步:运行时校验
在pkg/scheduler/apis/config/validation/validation_pluginargs.go添加校验器,确保用户配置与默认值合法:
// From here on, FooPluginArgs refers to the type defined in pkg/scheduler // definition, not the kube-scheduler definition. We're dealing with // post-default values. func ValidateFooPluginArgs(args config.FooPluginArgs) error { if args.BarParam < 0 && args.BarParam > 100 { return fmt.Errorf("must be in the range [0, 100]") } return nil }第四步:代码生成
提交所有改动后,运行代码生成命令,自动产出 DeepCopy、类型转换、指针/原始类型互转与默认值设置代码:
$ cd $GOPATH/src/k8s.io/kubernetes $ git add -A && git commit $ make clean $ ./hack/update-codegen.sh $ make generated_files第五步:补测试
生成代码后立刻补齐单元测试:
pkg/scheduler/apis/config/v1beta1/defaults_test.go:单测默认值逻辑;pkg/scheduler/apis/config/validation/validation_pluginargs_test.go:单测校验器;pkg/scheduler/apis/config/scheme/scheme_test.go:用一份完整KubeSchedulerConfiguration定义测试整个解析链路。
第六步:在插件中接收参数
修改插件的New方法签名,从runtime.Object断言出FooPluginArgs并校验:
func New(fpArgs runtime.Object, fh framework.FrameworkHandle) (framework.Plugin, error) { // config.FooPluginArgs refers to the pkg/scheduler struct type definition. args, ok := fpArgs.(*config.FooPluginArgs) if !ok { return nil, fmt.Errorf("got args of type %T, want *FooPluginArgs", fpArgs) } if err := validation.ValidateFooPluginArgs(*args); err != nil { return nil, err } // Use args.BarParam as you like. }这套模式正是调度框架"插件可配置化"的标准实现路径,也是社区大量插件的开发模板。
理解调度队列:贡献调度逻辑前必须掌握的基础
调度队列机制是调度器吞吐与公平性的核心,scheduler_queues.md 详细描述了其内部结构。队列机制需要处理 Pod 的各种前置条件(持久卷存在、反亲和规则、容忍污点等),因此设计了三层队列:
- activeQ(活动队列,堆):提供立即可调度的 Pod,默认优先级最高的 Pod 在堆顶,可通过
QueueSort扩展点自定义排序; - backoffQ(退避队列,堆):指数退避那些"暂时失败但终将可调度"的 Pod(如卷还在创建中)。默认初始退避 1 秒、最大退避 10 秒(均可配置);退避超时随失败次数指数增长,例如 3 次失败约 8 秒、5 次失败约 32 秒,达到上限后不再增长;
- unschedulableQ(不可调度队列,Map):停放等待特定条件发生的 Pod,直到有"移动请求"(move request)触发。
两个后台 goroutine 周期性把 Pod 挪回活动队列:flushBackoffQCompleted每秒运行一次(退避到期的 Pod 回到活动队列);flushUnschedulableQLeftover每 30 秒运行一次(停留超过 30 秒的不可调度 Pod 获得重试机会,最坏 60 秒内被挪动)。此外,集群事件(Pod/Node/Service/PV/PVC/存储类/CSI 节点变化)会触发移动请求,把 unschedulable 队列中的 Pod 批量搬回 active 或 backoff 队列:
调度队列中 Pod 在 active、backoff、unschedulable 三个队列之间流转示意
队列还对外暴露pending_pods与queue_incoming_pods_total两个指标,用于观测各队列积压量、入队事件来源与调度吞吐。
性能基准测试:调度器的关键防线
调度器是控制平面中性能敏感的组件。按照 scheduler_benchmarking.md 的约定,任何可能影响调度性能的 PR,都应在提交时运行集成基准测试,并附上前后对比数据。
运行全部集成基准:
make test-integration WHAT=./test/integration/scheduler_perf KUBE_TEST_VMODULE="''" KUBE_TEST_ARGS="-run=^$$ -bench=."只跑指定基准(例如BenchmarkScheduling):
make test-integration WHAT=./test/integration/scheduler_perf KUBE_TEST_VMODULE="''" KUBE_TEST_ARGS="-run=^$$ -bench=BenchmarkScheduling"基准用例定义在./test/integration/scheduler_perf/scheduler_bench_test.go,函数名以BenchmarkScheduling开头,通过结构体数组描述测试规模:
tests := []struct{ nodes, existingPods, minPods int }{ {nodes: 100, existingPods: 1000, minPods: 100}, {nodes: 1000, existingPods: 1000, minPods: 100}, {nodes: 5000, existingPods: 1000, minPods: 1000}, }其中nodes是测试集群节点数,existingPods是初始化阶段预调度 Pod 数,minPods是基准计时的实际调度 Pod 数。想测 5000 节点集群调度 10000 个 Pod,就往数组里加{nodes: 5000, existingPods: 1000, minPods: 10000};还可以用-bench=BenchmarkScheduling/5000Nodes/1000Pods精确跑某一规模组合。这些都是向 SIG 证明"我的改动不拖慢调度器"的标准动作。
@mentions 使用指南:找对人、办对事
在 GitHub 上需要对应角色的关注时,使用以下 SIG 团队提及(@mentions):
| 团队 | 适用场景 |
|---|---|
@kubernetes/sig-scheduling-api-reviews | API 变更与审查 |
@kubernetes/sig-scheduling-bugs | Bug 分诊与排障 |
@kubernetes/sig-scheduling-feature-requests | 功能请求 |
@kubernetes/sig-scheduling-misc | Approver 与 Reviewer 的一般性讨论 |
@kubernetes/sig-scheduling-pr-reviews | PR 审查 |
@kubernetes/sig-scheduling-proposals | 设计提案 |
@kubernetes/sig-scheduling-test-failures | 测试失败与分诊 |
精准使用这些团队别名,能让你的 Issue/PR 第一时间触达最合适的维护者,避免无效等待。
参与渠道与行动清单
SIG Scheduling 的日常交流渠道包括 Slack 的#sig-scheduling频道、邮件列表以及双周例会(分为亚太/欧洲时区与北美/欧洲时区两组,另有 descheduler 专项例会,具体排期见 sig-scheduling/README.md)。
总结一份可执行的贡献行动清单:
- 通读 contributors/guide/README.md 与 contributors/guide/contributor-cheatsheet/README.md;
- 在
first-good-issue/help wanted池中认领第一个任务; - 小步提交 PR,遵守"新 commit 回应 review、合并前 squash"的提交纪律;
- 按 sig-scheduling/CONTRIBUTING.md 的四层测试要求补齐测试,性能敏感改动附基准前后对比;
- 涉及 API 或重大重构时,先走 KEP 提案流程;
- 遇到问题在 Slack/邮件列表/例会中提出,并把结论回写到 Issue/PR。
无论你的起点是修一个文档、修一个 bug,还是完整地给插件添加一个新参数,SIG Scheduling 都欢迎——调度器是 Kubernetes 控制平面中最有挑战也最有成就感的领域之一,祝贡献顺利。
【免费下载链接】communityKubernetes Community Documentation项目地址: https://gitcode.com/GitHub_Trending/com/community
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考