9. kubebuilder 进阶: 源码分析
注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
注:本文所有示例代码都可以在 blog-code 仓库中找到
在前面的文章当中我们已经完整的完成了一个 Operator 的开发,涉及到了 CURD、预删除、Status、Event、OwnerReference、WebHook,也算是将一个 Operator 开发中会涉及到的点大部分都了解了一下。kubebuilder 帮我们做了很多事情,让我们的开发基本上只需要关注一个 Reconcile 函数就可以了,但是从另外一个方面来讲,kubebuilder 目前对我们来说它还是一个黑盒,会产生很多的疑问:
- Reconcile 方法是怎么被触发的?
- 怎么识别到不同的资源?
- 整体是如何进行工作的?
- ……
架构
我们先来看一下来自官方文档的这个架构图[1]
- Process 进程通过
main.go
启动,一般来说一个 Controller 只有一个进程,如果做了高可用的话,会有多个 - Manager 每个进程会有一个 Manager,这是核心组件,主要负责
- metrics 的暴露
- webhook 证书
- 初始化共享的 cache
- 初始化共享的 clients 用于和 APIServer 进行通信
- 所有的 Controller 的运行
- Client 一般来说,我们 创建、更新、删除某个资源的时候会直接调用 Client 和 APIServer 进行通信
- Cache 负责同步 Controller 关心的资源,其核心是 GVK -> Informer 的映射,一般我们的 Get 和 List 操作都会从 Cache 中获取数据
- Controller 控制器的业务逻辑所在的地方,一个 Manager 可能会有多个 Controller,我们一般只需要实现 Reconcile 方法就行。图上的 Predicate 是事件过滤器,我们可以在 Controller 中过滤掉我们不关心的事件信息
- WebHook 就是我们准入控制实现的地方了,主要是有两类接口,一个是 MutatingAdmissionWebhook 需要实现 Defaulter 接口,一个是 ValidatingAdmissionWebhook 需要实现 Validator 接口
源码分析
了解了基本的架构之后,我们就从入口 main.go
开始,看一看 kubebuilder 究竟在后面偷偷的做了哪些事情吧。
main.go
1 |
|
可以看到 main.go 主要是做了一些启动的工作包括:
- 创建一个 Manager
- 使用刚刚创建的 Manager 创建了一个 Controller
- 启动 WebHook
- 添加健康检查
- 启动 Manager
下面我们就顺着 main 函数里面的逻辑一步步的往下看看
NewManger
1 |
|
创建 Cache
1 |
|
这里主要是调用 NewInformersMap
方法创建 Informer 的映射
1 |
|
NewInformersMap
会去分别创建,结构化、非结构化以及 metadata 的 InformerMap 而这些方法最后都会去调用 newSpecificInformersMap
方法,区别就是不同的方法传入的 createListWatcherFunc
参数不同
1 |
|
newSpecificInformersMap 和常规的 InformersMap 类似,区别是没实现 WaitForCacheSync
方法
以结构化的传入的 createStructuredListWatch
为例,主要是返回一个用于创建 SharedIndexInformer 的 ListWatch 对象
1 |
|
小结: cache 主要是创建了一些 InformerMap,完成了 GVK 到 Informer 的映射,每个 Informer 会根据 ListWatch 函数对对应的 GVK 进行 List 和 Watch。
创建 Client
1 |
|
client 创建了两个一个用于读,一个用于写,用于读的会直接使用上面的 cache,用于写的才会直接和 APIServer 进行交互
Controller
下面我们看一下核心的 Controller 是怎么初始化和工作的
1 |
|
main.go
的方法里面主要是初始化了 Controller 的结构体,然后调用了 SetupWithManager
方法
1 |
|
SetupWithManager
之前有讲到过,主要是使用了建造者模式,去构建了我们需要监听的对象,只有这些对象的相关事件才会触发我们的 Reconcile
逻辑。这里面的 Complete
最后其实是调用了 Build
方法
1 |
|
Build
主要调用 doController
、doWatch
两个方法
1 |
|
doController
主要是初始化了一个 Controller,这里面传入了我们实现 的Reconciler
以及获取到我们的 GVK 的名称
1 |
|
Watch 主要是监听我们想要的资源变化,blder.ctrl.Watch(src, hdler, allPredicates...)
通过过滤源事件的变化,allPredicates
是过滤器,只有所有的过滤器都返回 true 时,才会将事件传递给 EventHandler hdler
,这里会将 Handler 注册到 Informer 上
启动
1 |
|
无论是不是 leader 最后都会使用 startRunnable
启动 Controller
1 |
|
实际上是调用了 Controller 的 Start
方法
1 |
|
总结
Reconcile 方法的触发是通过 Cache 中的 Informer 获取到资源的变更事件,然后再通过生产者消费者的模式触发我们自己实现的 Reconcile 方法的。
Kubebuilder 是一个非常好用的 Operator 开发框架,不仅极大的简化了 Operator 的开发过程,并且充分的利用了 go interface 的特性留下了足够的扩展性,这个我们可以学习,如果我们的业务代码开发框架能够做到这个地步,我觉得也就不错了
参考文献
关注我获取更新
猜你喜欢
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处,禁止全文转载