参考资料
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
方法。