kubebulder使用以及相关原理介绍
本片博客将介绍kubebuilder的使用以及相关原理 ,原始教程kubebuilder项目地址。但由于原教程kubebuilder版本为v4而教程部分为v3,故而本篇会做出修正并进行相关解释。相关组件版本如下
- k8s版本:v1.33.1
- kubebuilder版本:v4
- go版本:v1.22.0
- controller-runtime:v0.18.2
kubebuilder的简要说明:
- 是什么:是针对controller-runtime的封装脚手架工具
- 为什么:开发者使用controller-runtime开发operator过程繁琐
- 怎么样:使用marker机制自动化生成相关资源代码,用户只需要再顶层填充代码逻辑即可
K8s Operator工作原理
CRD,CR 和 controller
- CRD(Custom Resource Definition)为K8s 自定义资源定义,比起K8s内置的资源(如deployment,configmap),这种资源由开发者自己定义
- CR(Custom Resource)为K8s 自定义资源,由CRD实例化而来,CR和CRD的关系类似于 opp中的类和实例的关系
- controller:对CRD/CR 进行控制的程序/组件
informer,cache 和reconcile
controller对于CR的控制很大一部分是reconcile(调和),其本质为做出一系列动作是的CRD的Staus(状态)趋近于Spec(期待)
informer的本质是在 client-go 的基础上提供缓存(cache)+ 事件分发(event handler)
其主要由三部分组成
- ListWatcher:当informer被创建的时候,伴随着fieldSelector过滤器访问apiserver将对应资源全部拉下来存到本地cache中,这部分为List。然后再和apiserver建立https长连接,当资源发生变化时apiserver将event推送给informer让其维护cache,这部分为Watch。而一个event大概是这样的
1 | type Event struct { |
- ResourceEventHandler:当informer察觉到cache中资源发生变化的时候触发回调,然后创建一个key出来塞到workqueue中,有三种触发回调函数,对应三种触发时机
- addFunc():当资源被创建的时候
- updateFunc():当资源被更新的时候
- deleteFunc():当资源被删除的时候
当回调函数被触发时候,回调函数会根据event中对应的Object的ns/name 生成一个key塞到workqueue中
- ResyncPeriod:当长时间没有资源变动时候,自动触发updateFunc(),类似于一个保活机制

webhook 和ca-manager
kubebuilder使用教程
初始化kubebuilder
初始化kubebuilder的project就一行
1 | kubebuilder init --domain tutorial.kubebuilder.io --repo tutorial.kubebuilder.io/project |
其中
- domain:为你生成CRD,controller,webhook的域,当生成相关api时,其指定的group和这里的domain拼接就生成该资源的Group
- repo:该go项目的repo,写在go.mod中表明这个项目的总package
这个项目初始会有如下的目录结构
1 | my-operator/ |
PROJECT:项目的元信息文件,描述 Kubebuilder 插件版本、项目类型、domain 等。例如会包含 domain: my.domain 这样的配置。
Makefile:自动化命令集合。常用目标包括 make run(运行控制器)、make install(安装 CRD)、make docker-build(构建镜像)。
go.mod / go.sum:Go modules 的依赖管理文件。记录了项目依赖的库(比如 controller-runtime)。
config/:存放 Kubernetes 部署配置(YAML 文件)。包括 CRD 定义、RBAC 权限、Manager 部署清单等。一般在开发过程中会用 kustomize 组合这些文件。
controllers/:存放控制器(Controller)的实现代码。当用 kubebuilder create api 创建新的 API 时,会在这里生成对应的控制器逻辑文件。
api/:存放 API 类型定义(CRD 对应的 Go struct)。用于定义 Kubernetes 自定义资源(CustomResourceDefinition)。
bin/:存放项目需要用到的二进制文件,例如 controller-gen、kustomize。通常在 make 时自动下载。
然后需要开始创建API,如同教程中所说
1 | kubebuilder create api --group batch --version v1 --kind CronJob |
我们将会创建api,并指定其group,version,kind,也就是GVK。
执行 这条命令之后 之后,Kubebuilder 自动为我们完成了大量重复性工作:
PROJECT 文件:
在 PROJECT 文件中新增资源与控制器的描述:
1
2
3
4
5
6resources:
- group: batch
version: v1
kind: CronJob
path: tutorial.kubebuilder.io/project/api/v1
controller: true这表示我们要管理一个新的 GVK(Group-Version-Kind = batch/v1/CronJob),对应的 Go 类型定义在 api/v1,并且需要生成 controller。
API 类型代码:
新建 api/v1 文件夹,其中包括:- groupversion_info.go:定义 API 的 Group/Version:
1
2
3
4var (
GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v1"}
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
)- cronjob_types.go:定义 CronJob 和 CronJobList 两个 struct,并带有 kubebuilder 标记(markers):
1
2
3
4
5
6
7
8// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
type CronJob struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CronJobSpec `json:"spec,omitempty"`
Status CronJobStatus `json:"status,omitempty"`
}这些 struct 会被 controller-gen 转换成 CRD YAML。
Controller 模板:生成 controllers/cronjob_controller.go,内容包括:
CronJobReconciler 结构体(内嵌 client、scheme)
空的 Reconcile 方法,留待我们实现逻辑
SetupWithManager 方法,将 controller 注册到 manager
配置清单:生成 CRD YAML、Sample CR、RBAC 权限文件
config/crd/bases/batch.tutorial.kubebuilder.io_cronjobs.yaml:CRD 定义文件(由 controller-gen 自动生成)
config/samples/batch_v1_cronjob.yaml:示例 CronJob 资源,方便测试
config/rbac/role.yaml:新增了对 cronjobs 的 RBAC 权限,例如:
1
2
3apiGroups: ["batch.tutorial.kubebuilder.io"]
resources: ["cronjobs", "cronjobs/status", "cronjobs/finalizers"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]main.go 修改:注册新的 Controller 到 Manager
在 main.go 的 main() 函数中,注册了新的控制器:1
2
3
4
5
6
7if err = (&controllers.CronJobReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "CronJob")
os.Exit(1)
}这样 manager 启动时就会自动加载并运行 CronJob 的控制循环。
填充CRD
再完成初始化的相关信息之后,下面要开始填充设计我们的CRD了,这部分再官方教程中已经有很详细的解释了,但想补充一些我认为的关键信息
subresource的含义:
在CronJob的结构体定义中
1
2
3
4
5
6
7
8
9
10
11// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// CronJob is the Schema for the cronjobs API
type CronJob struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CronJobSpec `json:"spec,omitempty"`
Status CronJobStatus `json:"status,omitempty"`
}第二条maker表明status是一个subresource,其含义是Status是一个独立resource,subresource 会让 API Server 暴露独立 endpoint,并区分权限,用户不可修改,只能由controller自身修改。从设计角度来看,Status应该是集群资源的实时状态,是观测而来而不应该是用户定义,用户应该对Spec进行操作再通过reconcile来达到自己想要的状态
GVK信息注册到scheme
在
cronjob_types.go中的init()函数中SchemeBuilder.Register(&CronJob{}, &CronJobList{})将Cronjob和CronjobList的kind信息注册到这个SchemeBuilder中,这个SchemeBuilder在该api/v1下的groupversion_info.go中维护了其GV信息,这样GVK信息就齐了1
2
3
4
5
6
7
8
9
10var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "batch.tutorial.kubebuilder.io", Version: "v1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)而在cmd/main.go中的
init()函数调用这个gv的AddToScheme将GVK的信息注册到Scheme中1
2
3
4
5
6func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(batchv1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}当然,如果要说,scheme是什么,scheme就是manager所维护的一份crd yaml到crd go struct的一份dict(字典)
设计controller
同上,controller设计在官方教程中已经比较全面了,照抄就行
设计webhook
由于官方最近v4教程更新不全面,所以关于webhook可能还是v3版本
再创建webhook后
1 | kubebuilder create webhook --group batch --version v1 --kind CronJob --defaulting --programmatic-validation |
其相关webhook代码并非在internal/webhook下,而是合并到了api/v1文件夹下。
同时,webhook的启动也并非通过手动实例化CronJobCustomValidator和CronJobCustomDefaulter来进行,因为在v4版本中,通过kubebuilder创建的webhook关联的crd都模式实现了Default(),ValidateCreate(),ValidateUpdate()和ValidateDelete()接口,我们不需要再额外创建,只需要维护其对应函数即可。在当前时间点[2025/9/26],其与官方教程对应的cronjob_webhook.go的代码如下
1 | var cronjoblog = logf.Log.WithName("cronjob-resource") |
部署operator
部署CRD & controller
在完成相关CRD,controller,webhook的搭建之后,需要对这些资源进行部署。查阅MakeFile文件我们可以看到会有以下的目标
- make manifest:将我们写的CRD,controller,webhook都通过controller-runtime生成对应的yaml文件
- make install:将CRD apply到k8s集群
- make deploy:将CRD,controller,webhook都apply到k8s集群
- make docker-build IMG=: 将controller打包成镜像
但这里会有一个问题,问题在于,kubebuilder v4 的controller-gen 在生成yaml时候会自动生成属性的ip和name两个字段,但k8s在新版本会对这个两个字段进行校验检查是否为required或者有default值,但自动生成的yaml没有,所以会导致apply出错。解决方法是在Makefile中添加一个make目标
1 |
|
在make install和make deploy中所依赖的manifest后添加此目标即可
1 |
|
部署cert-manager & webhook
关于cert-manger和webhook的原理请看这里:
挖坑:cert-manger原理以及使用
挖坑:webhook原理以及使用
我们首先需要安装cert-manager使其进行webhook 私钥和证书secert的制作
1 | kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.yaml |
查看以下secret是否存在以及对应controller-pod是否运行起来
1 | kubectl get secret -n xxx-system |
然后按照教程中将config/default/kustomization.yaml和config/crd/kustomization.yaml中prefix为[CERTMANAGER]和[WEBHOOK]的注释代码给解除注释即可
具体来说
- 针对config/crd/kustomization.yaml中的注释解除是为了启用 config/patch中的yaml,包括启动webhook和ca 注入功能
- 而针对config/default/kustmozation中的注释主要是替换cert-manager中的的相关属性字段



