k8s之kubebuilder基础


参考资料
https://xie.infoq.cn/article/e3345fdc1c7390a779231e799

Kubebuilder 简介

  • Kubebuilder 是一个用于构建 Kubernetes API 的框架,专注于通过自定义资源定义(CRD)扩展 Kubernetes 的功能。它类似于 Web 开发中的 Rails 或 Spring Boot,能够显著减少 Kubernetes API 开发的复杂性,帮助开发者遵循最佳实践。

Kubebuilder 的核心特点

  • 自动生成代码,提供强大的库和工具。
  • 基于 controller-runtime 和 controller-tools 两个库开发。
  • 是 Operator SDK 的基础。

在这里插入图片描述

核心概念

2.1 GVK 和 GVR

GVK(Group Version Kind)

  • 用于描述资源的类型,由 Group、Version 和 Kind 组成。
  • 示例:apps/v1/Deployment。
  • 主要用于资源的定义和描述,特别是在控制器和元数据操作中使用。

GVR(Group Version Resource)

  • 用于描述资源的实例,由 Group、Version 和 Resource 组成。
  • 示例:apps/v1/deployments/sample。
  • 主要用于资源的实际操作,特别是在动态客户端和工具中使用。

区别

  • GVK 描述资源的类型,GVR 描述资源的具体实例。
  • GVK 用于定义和描述资源,GVR 用于操作资源。
  • GVK 类似类,GVR 类似实例。

2.2 Scheme

  • Scheme 是一个映射表,存储了 Kubernetes 原生资源和自定义资源(CRD)的 GVK 与 Go 类型(Go Type)之间的映射关系。

  • 示例:apps/v1/Deployment 映射到 k8s.io/api/apps/v1 包中的 Deployment 结构体。

  • 作用:帮助 Kubernetes 识别和处理资源。

2.3 Share Informer

  • Share Informer 是一个共享的资源信息管理器,负责监听 GVR 的变化(如创建、删除和更新操作),并将变化信息分发给多个组件(如 Controller)。

核心功能

  • 监听资源的创建、更新和删除操作。
  • 将资源变化通知给所有关注该资源的 Controller。
  • 通过本地缓存和 Delta FIFO 队列高效管理资源状态。

创建与共享

  • 只有 Controller Watch 可以创建 Share Informer。
  • 多个 Controller 可以共享同一个 Share Informer,避免重复监听。

Share Informer 实现缓存机制

Delta FIFO 队列
  • Share Informer 使用 Delta FIFO 队列来存储从 API Server 接收到的资源变化事件。当 Watch API 接收到一个资源变化事件时,Share Informer 会将其包装成一个 Delta 对象,并放入 Delta FIFO 队列中。Delta 对象包含了资源的操作类型(如添加、更新、删除)以及资源的相关信息。
本地缓存
  • Share Informer 还维护了一个本地缓存,用于存储资源的当前状态。当从 Delta FIFO 队列中取出一个 Delta 对象并处理时,Share Informer 会根据 Delta 对象中的操作类型来更新本地缓存中的资源状态。这样,Share Informer 可以在本地快速获取资源的最新状态,而无需频繁地向 API Server 发送请求。

事件分发机制

  • Share Informer 通过注册的 EventHandler 来实现事件的分发。当 Share Informer 从 Delta FIFO 队列中取出一个 Delta 对象并处理完成后,它会根据 Delta 对象中的操作类型调用相应的 EventHandler。常见的 EventHandler 有 AddFunc、UpdateFunc 和 DeleteFunc 等,分别对应资源的添加、更新和删除事件。用户可以通过注册这些 EventHandler 来实现对资源变化的自定义处理逻辑。例如,在资源添加事件中,用户可以将资源名称添加到 Controller 的工作队列中,以便后续进行进一步的处理。

定期同步机制

  • 为了确保本地缓存与 API Server 中的资源状态保持一致,Share Informer 会定期与 API Server 进行全量同步。在全量同步过程中,Share Informer 会从 API Server 获取指定资源的所有实例,并将其更新到本地缓存中。这个过程可以防止由于网络故障或其他原因导致的本地缓存与 API Server 状态不一致的情况发生。

2.4 Manager

  • Manager 是 controller-runtime 中的顶层管理对象,负责管理 Controller、Cache 和 Client 等组件。
  • 初始化并管理 Controller。
  • 管理 Cache 和 Client。
  • 提供统一的入口点,简化开发。

2.5 Cache

  • Cache 负责管理 GVK 对应的 Share Informer。
  • 维护 GVK 与 Share Informer 的映射关系。
  • 提供本地缓存,减少对 API Server 的访问。

2.6 Client

  • Client 是 Reconciler 操作资源的接口,分为两种:
  • Read Client:从本地缓存读取资源状态。
  • Write Client:直接访问 API Server,执行资源的创建、更新和删除操作。

2.7 Controller

  • Controller 是 Reconciler 的载体,负责监听资源变化并触发 Reconciler 的逻辑。
  • 当 Controller 接收到 Share Informer 发出的关于资源变化的通知后,它会将对应的资源名称添加到一个 Queue(队列)里面。这个 Queue 是 Controller 内部用于管理待处理任务的一个数据结构。一旦资源名称被添加到 Queue 中,就会触发后续的处理流程,最终会触发开发者编写的 Reconciler(调和器)的调和操作。Reconciler 的作用是将实际的集群状态与期望的状态进行比较,并在必要时进行调整,以确保集群状态符合预期。

2.8 Reconciler

  • Reconciler 是开发者的核心逻辑实现,负责确保资源的实际状态与期望状态一致。
  • 接收资源变化事件。
  • 从 Cache 中读取资源状态。
  • 通过 Client 更新资源状态。

核心流程

以下是 Kubebuilder 的工作流程:

初始化 Scheme

  • 注册 Kubernetes 原生资源和自定义资源的 GVK 与 Go 类型。

初始化 Manager

  • 初始化 Manager 并创建 Controller、Cache 和 Client。
  • 它不仅创建这些组件,还负责它们的生命周期管理。
  • Controller 的初始化包括创建 Queue 和设置 Watcher。

绑定 Reconciler

  • Controller 由 Manager 创建,并与 Reconciler 绑定。

监听资源变化

  • Controller 从 Cache 中获取 Share Informer,监听资源变化。Share Informer 监控 GVK。
  • Share Informer 通过 GVK 监听资源的变化,并将变化事件(如创建、更新、删除)分发给 Controller。
  • Share Informer 使用本地缓存和 Delta FIFO 队列高效管理资源状态。

触发 Reconciler

  • Share Informer 通过注册的 EventHandler(如 AddFunc、UpdateFunc、DeleteFunc)将资源变化事件通知给 Controller。
  • Controller 将事件(通常是资源的 Namespace 和 Name)加入 Queue。
  • Queue 是 Controller 内部的任务队列,用于管理待处理的事件。

执行调和逻辑

  • Controller 从 Queue 中取出事件(通常是资源的 Namespace 和 Name),并调用 Reconciler 的 Reconcile 方法。
  • Reconcile 方法的参数是一个 reconcile.Request 对象,包含资源的 Namespace 和 Name。
  • Reconcile 方法是开发者的核心逻辑实现,负责确保资源的实际状态与期望状态一致。
  • Reconciler 通过 Client 从 Cache 中读取资源的当前状态。
  • 根据控制逻辑,Reconciler 通过 Client 更新资源状态(如创建、更新、删除)。
  • Client 分为 Read Client(从 Cache 读取)和 Write Client(直接访问 API Server)。

触发 Reconciler

在 Kubernetes Operator 开发中,Reconcile 方法是控制器的核心逻辑,它负责确保集群中资源的实际状态与期望状态一致。Reconcile 方法会在以下几种常见情况下被调用:

1. 资源对象创建时

当用户通过 kubectl 或者其他 Kubernetes 客户端工具创建一个自定义资源(Custom Resource,CR)实例时,Reconcile 方法会被触发。例如,用户使用 kubectl apply -f myjob.yaml 创建一个 MyJob 自定义资源,控制器会接收到创建事件,并将对应的 MyJob 资源的请求加入到处理队列中,随后 Reconcile 方法会被调用以处理这个新创建的资源,使其达到期望状态。

2. 资源对象更新时

当用户对已存在的自定义资源进行更新操作,比如修改 MyJob 资源的 Spec 字段时,控制器会监测到资源的更新事件。同样,控制器会将更新后的资源请求加入到处理队列,Reconcile 方法会被调用,以确保集群中资源的实际状态与更新后的期望状态相匹配。例如,用户修改了 MyJob 资源中 Pod 模板的配置,Reconcile 方法会根据新的配置对关联的 Pod 进行相应的调整。

3. 资源对象删除时

当用户删除一个自定义资源时,控制器会接收到删除事件。此时,Reconcile 方法会被调用,用于执行一些清理操作,例如删除与该资源关联的其他资源(如删除 MyJob 关联的 Pod),确保资源被正确清理,避免留下无用的资源。

4. 周期性同步

为了防止因某些原因(如网络问题、API 服务器故障等)导致控制器丢失部分资源变化事件,控制器通常会设置一个周期性的同步机制。在指定的时间间隔后,控制器会重新对所有关注的资源进行检查,将资源的状态与期望状态进行对比。在这个过程中,Reconcile 方法会针对每个资源被调用,以确保资源状态的一致性。

5. 关联资源变化时

如果控制器设置了对关联资源的监视,当关联资源发生变化时,也可能会触发 Reconcile 方法。例如,在之前的代码中,控制器监视了 Pod 资源的变化,当 Pod 的状态发生改变(如 Pod 完成、失败等),并且该 Pod 的所有者是 MyJob 时,控制器会将对应的 MyJob 资源的请求加入到处理队列,从而调用 Reconcile 方法来更新 MyJob 的状态。

代码示例说明

controller - runtime 中,通常会使用 EnqueueRequestForOwner 来实现关联资源变化触发 Reconcile 方法的逻辑,示例如下:

func (r *MyJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
    c := ctrl.NewControllerManagedBy(mgr)

    // 监视拥有者是 MyJob 类型的 Pod,同时将 Pod 的拥有者 MyJob 扔进处理队列中,对 MyJob 进行调和
    c.Watches(&source.Kind{Type: &corev1.Pod{}}, &handler.EnqueueRequestForOwner{
        IsController: true,
        OwnerType:    &myjobv1beta1.MyJob{},
    })

    return c.For(&myjobv1beta1.MyJob{}).Complete(r)
}

在上述代码中,当 Pod 资源发生变化时,会将其所有者 MyJob 的请求加入到处理队列,进而触发 MyJob 资源的 Reconcile 方法。

### 使用 Golang 对 Kubernetes 组件进行定制化开发 #### 自定义组件开发概述 Kubernetes 提供了强大的可扩展机制,其中 CRD (Custom Resource Definitions)[^2] 和 Operator 是实现自定义功能的核心工具。CRD 允许开发者定义新的资源类型,而 Operator 则通过代码逻辑维护这些资源的状态。 为了简化复杂性的开发工作,社区提供了多种框架支持 CRD 和 Operator 的开发,比如 **Operator SDK** 和 **Kubebuilder**[^1]。这些工具帮助开发者专注于业务逻辑而非底层细节。 --- #### 创建 Custom Resource Definition (CRD) CRD 是一种声明式的资源配置方式,用于定义新类型的 Kubernetes 资源。以下是创建 CRD 的基本流程: 1. 定义一个新的资源对象结构。 2. 编写 YAML 文件描述该资源的字段及其行为模式。 3. 应用到集群中以使 kube-apiserver 认识此资源。 下面是一个简单的 CRD 示例: ```yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: myresources.example.com spec: group: example.com versions: - name: v1alpha1 served: true storage: true scope: Namespaced names: plural: myresources singular: myresource kind: MyResource ``` 上述文件定义了一个名为 `MyResource` 的新资源类型。 --- #### 开发 Operator 来管理 CRD 一旦 CRD 注册成功,就需要编写对应的 Controller 或者 Operator 来监控和响应其生命周期事件。以下是基于 Go 语言使用 **Controller Runtime** 实现的一个简单例子: ##### 初始化项目 假设已经安装好 Operator SDK 工具链,则可以通过以下命令快速搭建环境: ```bash operator-sdk init --domain=example.com --repo=github.com/example/my-operator ``` 接着为特定 CRD 添加控制器支持: ```bash operator-sdk create api --group=mygroup --version=v1alpha1 --kind=MyKind --controller=true ``` 这会生成必要的 scaffold 文件夹结构以及样板代码。 --- ##### 修改 Main 函数启动 Manager 在入口函数 `main.go` 中初始化并运行 Manager 实例负责协调整个操作过程。例如: ```go package main import ( "os" ctrl "sigs.k8s.io/controller-runtime" ) func main() { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, MetricsBindAddress: ":8080", Port: 9443, HealthProbeBindAddress: ":8081", }) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) } if err = (&myv1.MyKindReconciler{}).SetupWithManager(mgr); err != nil { setupLog.Error(err, "无法设置 Reconciler") os.Exit(1) } log.Info("开始监听...") if err := mgr.Start(signals.SetupSignalHandler()); err != nil { setupLog.Error(err, "manager stopped") } } ``` 这里设置了多个选项参数控制服务端口、健康检查地址等内容[^3]。 --- ##### 处理核心逻辑——Reconcile 方法 每个 controller 至少要实现一个 reconcile 循环方法处理实际事务。它接收输入请求后决定下一步动作直到达到期望状态为止。如下所示伪代码片段展示了典型场景下的更新策略: ```go // MyKindReconciler reconciles a MyKind object type MyKindReconciler struct { client.Client Log logr.Logger Scheme *runtime.Scheme } func (r *MyKindReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { instance := &myv1.MyKind{} err := r.Get(context.TODO(), req.NamespacedName, instance) if errors.IsNotFound(err) { return ctrl.Result{}, nil } else if err != nil { r.Log.Error(err, fmt.Sprintf("获取实例失败 %s/%s", req.Namespace, req.Name)) return ctrl.Result{}, err } // 执行具体任务... newPodSpec := corev1.PodSpec{/*...*/} pod := new(corev1.Pod) pod.GenerateName = "mypod-" pod.Spec = newPodSpec if resultErr := r.Create(context.TODO(), pod); client.IgnoreAlreadyExists(resultErr) != nil { r.Log.Error(resultErr, "创建 Pod 错误") return ctrl.Result{}, resultErr } return ctrl.Result{RequeueAfter: time.Minute}, nil } ``` 以上代码片段说明当检测到某个条件满足时触发重新排队等待下一轮循环继续执行。 --- #### 结合案例分析:Spark Operator 作为真实世界中的应用范例之一,Apache Spark 社区贡献出了官方版本的 Spark Operator[^4]。借助于这个插件,用户无需手动调用传统 CLI 方式即可完成批处理或者流计算任务调度至云端平台之上无缝衔接现有基础设施架构优势最大化利用硬件性能指标提升效率降低成本开销等方面均表现出色值得借鉴学习研究价值极高! --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值