ᕕ( ᐛ )ᕗ Jimyag's Blog

使用 kubebuilder 开发 k8s operator

Kubernetes 是一个高度可扩展的"系统",比如常见的自定义资源,控制器,准入控制及调度器进行扩展开发等。

Operator 是一种包装、运行和管理 K8S 应用的一种方式。它涵盖了 CRD(CustomResourceDefinition) + AdmissionWebhook + Controller,并以 Deployment 的形式部署到 K8S 中。

  • CRD 用来定义声明式 API(yaml),程序会通过该定义一直让最小调度单元(POD)趋向该状态。
  • AdmissionWebhook 用来拦截请求做 mutate(修改)提交的声明(yaml)和 validate(校验)声明式的字段。
  • Controller 主要的控制器,监视资源的 创建 / 更新 / 删除 事件,并触发 Reconcile 函数作为响应。整个调整过程被称作 Reconcile Loop(协调一致的循环),其实就是让 POD 趋向 CRD 定义所需的状态。

operator 可以做什么?

  • 自动化部署应用
  • 自动修复服务,比如资源被别人删除了或者修改了错误的配置,operator 可以自动修复他们

Groups、Versions、Kinds、resources 是什么?

比如现在有一个 calico 的 ippool 资源 ippools.crd.projectcalico.org

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# kubectl get ippools.crd.projectcalico.org default-pool -o yaml
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  annotations:
    projectcalico.org/metadata: '{"creationTimestamp":"2025-07-26T10:49:29Z"}'
  creationTimestamp: "2025-07-26T10:49:29Z"
  generation: 1
  name: default-pool
  resourceVersion: "472"
  uid: 1e88652d-bded-4ede-91e1-53b427f4aebe
spec:
  allowedUses:
  - Workload
  - Tunnel
  blockSize: 26
  cidr: 10.233.64.0/18
  ipipMode: Never
  natOutgoing: true
  nodeSelector: all()
  vxlanMode: Always

其中 crd.projectcalico.org 是 API Group,v1 是 API Version,IPPool 是 Kind,default-pool 是 Name,ippools.crd.projectcalico.org 是 Resource。

在 crd.projectcalico.org 中还有很多其他 Kind。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# kubectl api-resources | grep crd.projectcalico.org
bgpconfigurations                                crd.projectcalico.org/v1            false        BGPConfiguration
bgpfilters                                       crd.projectcalico.org/v1            false        BGPFilter
bgppeers                                         crd.projectcalico.org/v1            false        BGPPeer
blockaffinities                                  crd.projectcalico.org/v1            false        BlockAffinity
caliconodestatuses                               crd.projectcalico.org/v1            false        CalicoNodeStatus
clusterinformations                              crd.projectcalico.org/v1            false        ClusterInformation
felixconfigurations                              crd.projectcalico.org/v1            false        FelixConfiguration
globalnetworkpolicies                            crd.projectcalico.org/v1            false        GlobalNetworkPolicy
globalnetworksets                                crd.projectcalico.org/v1            false        GlobalNetworkSet
hostendpoints                                    crd.projectcalico.org/v1            false        HostEndpoint
ipamblocks                                       crd.projectcalico.org/v1            false        IPAMBlock
ipamconfigs                                      crd.projectcalico.org/v1            false        IPAMConfig
ipamhandles                                      crd.projectcalico.org/v1            false        IPAMHandle
ippools                                          crd.projectcalico.org/v1            false        IPPool
ipreservations                                   crd.projectcalico.org/v1            false        IPReservation
kubecontrollersconfigurations                    crd.projectcalico.org/v1            false        KubeControllersConfiguration
networkpolicies                                  crd.projectcalico.org/v1            true         NetworkPolicy
networksets                                      crd.projectcalico.org/v1            true         NetworkSet
tiers                                            crd.projectcalico.org/v1            false        Tier

API Group 是相关功能的集合,每一个 group 都有一个或者多个版本。不同的版本的行为可能不一样。

每一个 API group-version 都包含一个或者多个 API 的类型称为 Kinds。虽然 Kind 在不同 API Version 中可能会有不同的字段或者结构,但是每个版本必须能表达所有版本的数据。新版本增加的字段,旧版本没有,必须有办法保存起来(放到 annotation 中或者一些保留字段中)。这样即使客户端使用旧版本 API(如 v1beta1),也不会丢失用新版本 API 创建的额外信息。

Resources 是 api 中某个 Kind 的使用。Kind 描述了这个资源是什么。而 resource 是这个 Kind 的实现。通常情况下 kind 和 resource 是一一对应的关系。例如 pods 资源对应的是 Pod Kind。 有时候多个 Resource 会返回同一个 Kind。比如 deployments/scale 和 replicasets/scale 资源都返回 Scale Kind。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# kubectl get deployment -n kube-system calico-kube-controllers   --subresource=scale -o yaml

apiVersion: autoscaling/v1
kind: Scale
metadata:
  creationTimestamp: "2025-07-26T10:49:42Z"
  name: calico-kube-controllers
  namespace: kube-system
  resourceVersion: "100288"
  uid: 6b53a108-627e-4cf1-aa60-6fda4b32f290
spec:
  replicas: 1
status:
  replicas: 1
  selector: k8s-app=calico-kube-controllers
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# kubectl get replicasets.apps -n kube-system calico-kube-controllers-5d8f654785   --subresource=scale  -o yaml

apiVersion: autoscaling/v1
kind: Scale
metadata:
  creationTimestamp: "2025-07-26T10:49:42Z"
  name: calico-kube-controllers-5d8f654785
  namespace: kube-system
  resourceVersion: "100287"
  uid: d9833008-f78e-4c1c-90c9-c0215d8a3ddb
spec:
  replicas: 1
status:
  replicas: 1
  selector: k8s-app=calico-kube-controllers,pod-template-hash=5d8f654785

但是使用 CRD 时,每一个 Kind 都对应一个 resource。并且 resource 名称始终都是小写的,按照惯例都是 Kind 的小写形式。

Kubernetes 的资源对象(如 Deployment、ReplicaSet、StatefulSet)有复杂的 spec 和 status 字段:

  • spec.replicas:期望副本数
  • status.replicas、status.availableReplicas:当前副本数

如果每次扩容/缩容都直接更新主资源(如 Deployment 的 spec.replicas),会有两个问题:

  • 权限问题:扩容/缩容操作只需要改副本数,不应该有权修改 Deployment 其他字段。
  • 接口解耦:HPA 等自动伸缩组件不需要关心 Deployment 的完整结构,只要能读/写副本数即可。

所以 Kubernetes 提供了 subresource 来解决这个问题。除了 scale 之外,还有 status 和 log 等。

以上只是描述 CR 和 CRD,那么 k8s 要知道拿到这个 CR 或者 CRD 后改如何操作呢?

需要实现一个 controller 来监听 CR 或者 CRD 的变化,然后根据变化来更新主资源。

#K8s #Kubebuilder