跳到主要内容

Kubebuilder 入门上手

Kubebuilder 是由 Kubernetes SIG API Machinery 官方维护的 Controller / Operator 开发脚手架,官方文档

背景与定位

在 Kubebuilder 出现之前,开发 K8s Controller 需要直接使用 client-go,手动搭建 Informer、WorkQueue、LeaderElection 等基础设施,样板代码量极大,且缺乏统一的项目结构规范。

Kubebuilder 解决了这两个问题:

  • 底层:将 client-go 的复杂性封装进 controller-runtime,提供 Manager、Reconciler、Client、Cache 等高层抽象
  • 上层:提供 CLI 脚手架,一条命令生成符合社区规范的项目结构、CRD 定义、RBAC 清单、Dockerfile、Makefile

三者的关系:

Kubebuilder CLI(脚手架)
↓ 生成项目骨架,引入
controller-runtime(框架库)
↓ 封装
client-go(K8s 官方 Go 客户端)
↓ 调用
K8s API Server

架构概览

下图展示了 Kubebuilder 各核心概念之间的关系(来源:Kubebuilder Book — Architecture):

Kubebuilder 架构概念图

整体可以分为三个层次:

用户代码层(Kubebuilder 脚手架生成)

组件说明
Types / CRD用 Go struct 定义自定义资源的 Spec 和 Status,make manifests 生成对应 CRD YAML
Reconciler业务逻辑实现,接收触发事件后对比期望状态与实际状态并驱动收敛
Webhook可选,在资源写入 API Server 前做 Defaulting(补全默认值)或 Validation(合法性校验)

框架层(controller-runtime)

组件说明
Manager整个进程的运行容器,统一管理 Cache、Client、Controller、Webhook Server 的生命周期,并负责 Leader Election
Cache(Informer)基于 client-go Informer 实现的本地缓存,通过 List/Watch 与 API Server 保持同步;Read 操作走 Cache,不直接打 API Server
Controller订阅 Cache 的变化事件,将触发的对象 key 压入 WorkQueue,再由 Reconciler 逐一消费
Client对 K8s API 的高层封装:读操作默认走 Cache,写操作(Create / Update / Delete)直接调用 API Server
Webhook Server嵌入 Manager 的 HTTP(S) 服务,承接 API Server 发来的 AdmissionReview 请求

K8s 控制面层

组件说明
API Server所有资源变更的入口;Informer 通过 Watch 接口与之保持长连接,Webhook 请求也由其发出
etcdK8s 持久化存储,所有资源对象的最终落点

事件驱动流程:

API Server(资源变更)
↓ Watch 事件
Cache / Informer(本地缓存更新)
↓ 触发事件处理器
Controller WorkQueue(入队 NamespacedName)
↓ 消费
Reconciler.Reconcile()
├── r.Client.Get() → 读 Cache
├── 对比期望 vs 实际
└── r.Client.Update() → 写 API Server → 回到顶部

版本演变

大版本发布时间核心变化
v12018初代版本,项目结构较原始,直接依赖 client-go,无 controller-runtime
v22019引入 controller-runtime,重构项目结构,统一 Reconciler 接口,奠定现代 Controller 开发模式
v32021引入插件系统(Plugin),支持多 Group API,脚手架可扩展性大幅提升
v42023当前主力版本。要求 Go 1.21+,移除废弃 API,优化脚手架生成内容,与 controller-runtime v0.17+ 对齐
信息

v1 和 v2 已停止维护,新项目请直接使用 v4。v3 仍有历史文档可查:legacy docs

版本兼容性

Kubebuilder、controller-runtime、K8s 三者版本紧密绑定。controller-runtime 遵循每个 K8s 次版本发一个次版本的策略(k8s.io/* v0.N 对应 K8s 1.N)。

controller-runtime 与 K8s / Go 的兼容矩阵:

controller-runtimeK8s 版本最低 Go 版本
v0.241.361.26
v0.231.351.25
v0.221.341.24
v0.211.331.24
v0.201.321.23
v0.191.311.22
v0.181.301.22
v0.171.291.21

Kubebuilder 与 controller-runtime 的对应关系:

Kubebuildercontroller-runtimeK8sGo
v4.x(当前)v0.17 ~ v0.241.29+1.21+
v3.xv0.14 ~ v0.161.26 ~ 1.281.17+
v2.xv0.8 ~ v0.131.14 ~ 1.251.13+
提示

新项目选 Kubebuilder v4 + controller-runtime 最新次版本,与目标集群的 K8s 次版本对齐即可。例如集群是 K8s 1.31,选 controller-runtime v0.19,Go ≥ 1.22。

核心概念

K8s 资源标识:GVK 与 GVR

在 K8s 中,每种资源都由 GVK(Group / Version / Kind)唯一标识,而在 API 路径中则用 GVR(Group / Version / Resource)表示。

字段含义示例
GroupAPI 组,core 组资源(Pod、Service)使用空字符串 ""appsbatch""
VersionAPI 版本v1v1beta1v1alpha1
Kind资源类型,单数形式DeploymentPod
ResourceAPI 路径中的资源名,复数形式deploymentspods

以一个 Deployment 为例:

apiVersion: apps/v1 # Group=apps, Version=v1
kind: Deployment # Kind=Deployment,Resource=deployments

GVK vs GVR 的使用场景:

  • GVK:用于 Controller、Scheme 注册、代码中操作资源类型时
  • GVR:用于动态客户端(dynamic.Client)、kubectl、REST API 路径中

框架核心组件

概念说明
Manager整个 Controller 的运行容器,负责 Leader Election、缓存、客户端、信号处理
Reconciler业务逻辑入口,实现 Reconcile(ctx, req) 接口,每次 Watch 到变化时被调用
Client与 K8s API Server 交互,支持 Get / List / Create / Update / Delete
Cache本地缓存(基于 Informer),Watch 对象变化触发事件,Read 操作走缓存不直接打 API Server
Scheme类型注册表,把 Go struct 和 K8s GVK 关联起来

安装

brew 只提供最新版,需要精确匹配 K8s 版本时推荐直接下载二进制(见上方兼容矩阵找对应版本)。

下载指定版本(推荐):

# 自动识别架构,替换 VERSION 为目标版本,如 v4.1.1(对应 K8s 1.30)
VERSION=v4.1.1
ARCH=$(uname -m | sed 's/x86_64/amd64/')
curl -L -o kubebuilder \
"https://github.com/kubernetes-sigs/kubebuilder/releases/download/${VERSION}/kubebuilder_darwin_${ARCH}"
chmod +x kubebuilder
sudo mv kubebuilder /usr/local/bin/

brew 安装最新版:

brew install kubebuilder

验证:

kubebuilder version

Go 版本要求 ≥ 1.21(建议与目标 controller-runtime 版本对齐,见上方兼容矩阵)。

初始化项目

mkdir my-controller && cd my-controller

kubebuilder init \
--domain example.com \ # 资源 Group 的域名后缀,CRD Group 格式为 <group>.<domain>
# 例如 domain=jaco.live,group=core → apps.jaco.live
# 无 CRD 的项目此参数仅影响命名,填公司域名即可
--repo github.com/example/my-controller # Go module 路径,对应 go.mod 的 module 字段
# 填 GitLab/GitHub 实际仓库路径

生成的项目结构:

my-controller/
├── cmd/
│ └── main.go # Manager 启动入口
├── internal/
│ └── controller/ # Reconciler 实现放这里
├── config/ # RBAC、Deployment 等 K8s 清单
├── Makefile # make run / make docker-build / make deploy
├── Dockerfile
└── go.mod

生成 Controller

有 CRD 的场景

kubebuilder create api \
--group apps \ # API Group,最终 CRD 的 group 为 <group>.<domain>,如 apps.example.com
--version v1alpha1 \ # API Version,新资源建议从 v1alpha1 开始,稳定后升到 v1beta1/v1
--kind MyApp \ # 资源类型名,单数大驼峰,对应 YAML 里的 kind 字段
--controller=true \ # 同时生成 Reconciler 骨架文件
--resource=true # 生成 CRD types 定义文件(api/<version>/<kind>_types.go)

执行后会生成:

  • api/v1alpha1/myapp_types.go — CRD 的 Go struct 定义
  • internal/controller/myapp_controller.go — Reconciler 骨架

无 CRD 的场景(只 Watch 已有资源)

kubebuilder create api \
--group core \ # 填被 Watch 的资源所属 Group,core 资源(Pod/Service/Endpoints)填 core
--version v1 \ # 被 Watch 的资源版本
--kind Service \ # 被 Watch 的资源 Kind
--controller=true \ # 生成 Reconciler 骨架
--resource=false # 不生成 CRD,只生成 Controller 文件

Reconciler 结构

type MyAppReconciler struct {
client.Client
Scheme *runtime.Scheme
}

// Reconcile 是核心入口,每次 Watch 到对象变化时被调用
func (r *MyAppReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)

// req.NamespacedName 包含触发本次 reconcile 的对象 namespace/name
// 在这里实现业务逻辑

return ctrl.Result{}, nil
}

// SetupWithManager 声明 Watch 哪些资源
func (r *MyAppReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&appsv1.Deployment{}).
Complete(r)
}

常用返回值

// 正常完成,不再 requeue
return ctrl.Result{}, nil

// 发生错误,自动退避重试
return ctrl.Result{}, err

// 指定时间后重新触发(实现定时对账)
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil

// 立即重新触发
return ctrl.Result{Requeue: true}, nil

常用 Makefile 命令

Kubebuilder 生成的 Makefile 覆盖了从开发到部署的完整流程:

命令说明
make manifests根据 +kubebuilder:... marker 注释生成 CRD YAML、RBAC 清单到 config/ 目录
make generate生成 zz_generated.deepcopy.go 等 Go 代码(修改 types.go 后必须执行)
make installconfig/crd/ 下的 CRD 安装(apply)到当前 kubeconfig 的集群
make uninstall从集群删除 CRD
make run在本地进程中运行 Controller,连接当前 kubeconfig 集群,用于开发调试
make docker-build构建单架构 Docker 镜像
make docker-buildx使用 BuildKit 构建多架构镜像(linux/amd64,linux/arm64),需要先 docker buildx create
make docker-push推送镜像到镜像仓库
make deploy将 Controller 部署到集群(kustomize 渲染 config/default 后 apply)
make undeploy从集群删除 Controller
make build-installer将所有清单合并到 dist/install.yaml,方便一键分发安装

典型开发工作流:

# 1. 修改 api/*_types.go 后,重新生成代码和清单
make generate && make manifests

# 2. 安装 CRD,本地跑起来调试
make install && make run

# 3. 构建并推送多架构镜像
make docker-buildx IMG=my-registry/my-controller:v1.0.0
make docker-push IMG=my-registry/my-controller:v1.0.0

# 4. 生成可分发的单文件安装包
make build-installer IMG=my-registry/my-controller:v1.0.0
kubectl apply -f dist/install.yaml

Leader Election

多副本部署时需要开启,防止并发 reconcile:

// cmd/main.go
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
LeaderElection: true,
LeaderElectionID: "my-controller-leader",
LeaderElectionNamespace: "kube-system",
})

底层基于 K8s Lease 资源实现,获得锁的实例才执行 reconcile,其他实例处于 standby。

进一步阅读