Kubernetes 源码 staging 目录:为什么要有它
一、背景:一个让社区头疼的矛盾
早期 Kubernetes 的 k8s.io/api、k8s.io/apimachinery、k8s.io/client-go 这些库,就生活在 kubernetes/kubernetes 主仓库的 pkg/ 目录下。后来社区把它们拆成独立仓库,目的是让 controller、operator、第三方客户端能够单独 go get 引入——但这立刻撞上一个死结:
- 一方面,这些库和 k8s 核心代码强耦合。API 类型一变,
kube-apiserver、kubelet、kubectl等几十个组件都要跟着改,必须随主仓库同步演进。 - 另一方面,外部项目需要独立仓库、独立 tag、独立 Go module,不能只给它们看主仓库。
如果纯手工维护两套仓库,每次 API 变更要提交两遍,版本容易漂移,忘了同步就直接断掉外部构建。Kubernetes 主仓库根目录下的 staging/ 目录,就是为解决这个矛盾而设计的——它把"单仓库开发 + 多仓库发布"变成了一条单向自动流水线。
二、staging 目录是什么
位置在主仓库:staging/src/k8s.io/<库名>/。
目前(Kubernetes master 分支)这里一共暂存了 32 个库,包括最常用的:
staging/src/k8s.io/
├── api
├── apimachinery
├── apiserver
├── client-go
├── cli-runtime
├── code-generator
├── component-base
├── cri-api
├── kube-aggregator
├── kube-proxy
├── kube-scheduler
├── kubectl
├── kubelet
├── metrics
├── mount-utils
├── pod-security-admission
└── ...(共 32 个)
主仓库 staging/README.md 里有一句非常关键的话:
The code in the staging/ directory is authoritative, i.e. the only copy of the code.
也就是说,staging 里的代码是唯一权威副本,外部独立仓库里的代码只是它的镜像。
三、整体机制:一个代码源,两条消费路径

整个机制可以拆成三段:
- 主仓库内部消费:k8s 自己的组件(apiserver、kubelet 等)通过 go.mod 里的
replace指令,直接把k8s.io/client-go这样的 import 解析到本地staging/src/k8s.io/client-go。改完 staging 代码,主仓库编译立刻用新代码,不存在"改了源码但编译用旧版本"的问题。 - publishing-bot 自动镜像:每次 Kubernetes 发版后,发布机器人(publishing-bot)拉取主仓库对应 release 分支,把 staging 下每个子目录同步推送到
github.com/kubernetes/<repo>独立仓库,并打上对应 tag。 - 外部项目消费:controller、operator 等外部项目
go get k8s.io/api@v0.30.0时,拉到的就是 publishing-bot 同步过去的镜像。
四、四个关键设计
1. 权威副本在 staging,发布仓库是只读镜像
所有改动只在主仓库 staging 里提交。发布仓库的 CONTRIBUTING.md 明确写着不接受直接 PR——想修 k8s.io/api 的 bug?去 kubernetes/kubernetes 提 PR,合并后 publishing-bot 会自动同步过去。这样保证代码永远只有一个事实来源。
2. go.mod replace 打通主仓库编译
主仓库 go.mod 里有大量 replace 指令,例如:
replace k8s.io/api => ./staging/src/k8s.io/api
replace k8s.io/client-go => ./staging/src/k8s.io/client-go
这意味着 k8s 组件 import 的 k8s.io/client-go/dynamic,编译时实际解析到主仓库里的 staging/src/k8s.io/client-go/dynamic,而不是上次发布出去的版本。
3. publishing-bot 管两件事
它维护两个关键配置:
staging/publishing/rules.yaml:定义每个发布仓库由哪个 staging 子目录发布、依赖哪些其他 staging 仓库;publishing-bot/hack/repos.sh:维护要发布的仓库清单。
新增一个 staging 库的流程大约是:SIG Architecture 邮件列表审批 → 创建 staging/src/k8s.io/<新库> → 更新 import-restrictions.yaml → 在 kubernetes/org 仓库提 issue 创建空发布仓库(开 stage-bots 机器人写权限)→ 更新 rules.yaml 和 repos.sh → 更新 sigs.yaml 和 README 清单。
4. 版本号有固定映射规则
以 client-go 官方兼容矩阵为例:
| client-go tag | 匹配的 Kubernetes |
|---|---|
kubernetes-1.29.0 / v0.29.0 |
Kubernetes 1.29 |
kubernetes-1.30.0 / v0.30.0 |
Kubernetes 1.30 |
kubernetes-1.31.0 / v0.31.0 |
Kubernetes 1.31 |
规则很简单:主仓库版本 v1.x.y 对应独立库版本 v0.x.y——主版本从 1 换成 0,minor 和 patch 完全一致。bugfix 樱桃挑选到旧 release 分支时,对应发布仓库只递增 patch 号。
五、收益与约束
收益
| 维度 | 效果 |
|---|---|
| 单一代码源 | API 定义只提交一次,主仓库组件和外部项目拿到同一份代码,不漂移 |
| 自动发布 | 发版即镜像,不需要人肉同步两个仓库 |
| 统一贡献入口 | 改任何 k8s.io 库都是"改主仓库 staging + 提 PR 合并",流程单一 |
| 独立演进 | 外部项目可按自己节奏锁定 v0.30.0 或升级到 v0.31.0,不受主仓库补丁节奏影响 |
约束
- staging 库不能 import 主仓库内部
pkg/代码(否则镜像出去就断依赖),只能依赖其他 staging 库和第三方库,由import-restrictions.yaml强制校验; - 发布仓库需要
stage-bots团队写权限、初始空提交等特殊配置,新库加入要走 SIG Architecture 审批。
六、总结
staging 目录本质上是 "单仓库内开发、自动多仓库发布"的桥梁:代码物理上留在主仓库,主仓库用 replace 直接用;发版时 publishing-bot 自动把每个子目录镜像成独立 GitHub 仓库并打 tag。它牺牲了一点仓库目录的整洁,换来的是 k8s 内部组件和整个云原生生态用的 k8s.io/* 库永远版本对齐、永不漂移。
参考
- 主仓库
staging/README.md:https://github.com/kubernetes/kubernetes/tree/master/staging - client-go 版本兼容矩阵:https://github.com/kubernetes/client-go/blob/master/README.md
- publishing-bot:https://github.com/kubernetes/publishing-bot
浙公网安备 33010602011771号