首页 > 其他分享 >kubebuilder笔记

kubebuilder笔记

时间:2023-05-19 20:55:55浏览次数:40  
标签:err ctrl 笔记 mgr controller kubebuilder CRD go

一、kubebuilder作用

  • 提供脚手架工具初始化 CRDs 工程,自动生成 boilerplate 代码和配置
  • 提供代码库封装底层的 K8s go-client

二、kubebuilder整体流程

  1. 用户自定义crd,将自定义的crd注册到scheme中,这样通过GVK能找到对应的go的struct,也能通过go的struct找对对应的GVK
  2. Cache监听Scheme中的GVK,同时和Api Server建立list-watch的连接
  3. 当发现某个controller需要的GVK资源发生状态的改变就reconcile调度对应的controller
  4. 对应的controller中是用户自己定义的逻辑,用来保证该类型的crd和k8s集群中声明的该资源的yaml/json中的字段一致
  5. 保证一致主要是通过调用Clients,然后Clients可以和Api Server交互

三、使用方法

1、初始化脚手架 这不创建了一个项目模板并引入一些依赖

kubebuilder init --domain ding.test.com

2、创建api 会在根目录下创建一个api目录 里头有: resourcename_types.go、groupversion_info.go、 zz_generated.deepcopy.go kubebuilder-process

kubebuilder create api --group ding.shin.com --version v1alpha666 --kind DingShinType

3、groupversion_info.go: 会声明 GroupVersion、SchemeBuilder、AddToScheme三个东西(也可以手动给改到别的文件引入) kubebuilder-catalog 每当再执行一遍craete api 如:

kubebuilder create api --group ding.shin.com --version v1alpha666 --kind DingShin888Type

就会多生成一个xxxxx_type.go

备注:

不同的自定义CRD需要调用 SchemeBuilder.Register 注册自己以及一个 list
之后在程序入口要将自定义的 CRD 们添加到 build-in(原生k8s的资源) 资源里面, 这样Cache就知道应该去Watch谁了
在程序的入口处,除了把自定义的 CRD 添加到 scheme 中,还需要使用 manager 初始化资源,如:

  // 1、init Manager
	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{Scheme: scheme, MetricsBindAddress: metricsAddr})
	if err != nil {
		setupLog.Error(err, "unable to start manager")
		os.Exit(1)
	}
  // 2、init Reconciler(Controller)
  // 主要吧Clien Log Scheme 等依赖传进去
	err = (&controllers.ApplicationReconciler{
		Client: mgr.GetClient(),
		Log:    ctrl.Log.WithName("controllers").WithName("Application"),
		Scheme: mgr.GetScheme(),
	}).SetupWithManager(mgr)
	if err != nil {
		setupLog.Error(err, "unable to create controller", "controller", "EDASApplication")
		os.Exit(1)
  }
  
  // 3、调用 SetupWithManager 方法初始化Reconciler
  func (r *DingShin888TypeReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
      For(&dingshincomv1alpha666.DingShin888Type{}).
      Complete(r)
  }

也可以自定义setup方法去初始化,如: 

func VolumeMounterSetup(mgr ctrl.Manager, l logr.Logger) error {
	name := "sage/" + strings.ToLower(sagecorev1alpha2.VolumeMounterTraitGroupKind)
	return ctrl.NewControllerManagedBy(mgr).
		Named(name).
		For(&sagecorev1alpha2.VolumeMounterTrait{}).
		Complete(
		NewVolumeMounterTraitReconciler(mgr,
				core.WithLogger(l.WithValues("controller", name)),
				core.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))))
}

// 里面主要得把 Scheme 以及 Client 和 Record 都传进去
func NewVolumeMounterTraitReconciler(m ctrl.Manager, o ...core.ReconcilerOption) *VolumeMounterTraitReconciler {
	it := &VolumeMounterTraitReconciler{}
	it.Client = m.GetClient()
	it.Scheme = m.GetScheme()
	it.Record = event.NewNopRecorder()
	for _, ro := range o {
		ro(&it.Reconciler)
	}
	it.traitBuilder = sagetrait.NewVolumeMounterTraitBuilder(it.Client, it.Log)
	gc := sagegc.GarbageCollector{
		Client: m.GetClient(),
		Log:    it.Log,
	}
	it.GC = gc

	return it
}

四、启动自定义 CRD 以及 Controller 的大概流程

1、首先入口处 NewManager, NewManager 内部大概就是 NewClient 以及 NewCache

	mgr, err := ctrl.NewManager(cfg, ctrl.Options{SyncPeriod: &options.SyncPeriod, MetricsBindAddress: options.MetricsAddr})

2、其中 NewCache 大概就是 new 了一个 informer 的 map,然后这个 map 以每个Kind,也就是每个 GVK 为 key,value 是它对应的 informer,每个 informer 都会创建一条 List Watch 和 Api Server 通信,监听对应的 Kind(GVK)

3、NewClient 就是创建了一个用于和 Api Server 通信的客户端,其中读操作直接去 Cache 中去读,写的话会间接调用 k8s 提供的 go-client

4、然后把自定义的 CRD 注册给scheme
先:

  // groupversion_info.go 下
  var (
    GroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
  )

然后:

  // 可以在同一个package下调用它的 Register
  SchemeBuilder.Register(&ServerWorkload{}, &ServerWorkloadList{})
	SchemeBuilder.Register(&TaskWorkload{}, &TaskWorkloadList{})
  // ......其他 CRD

再:

  // 引入 SchemeBuilder 所在的那个 package
  import alpha666 "xxxxxx/yyyyyy/xxxxx"
  // 以及 client-go 提供的 schema,也就是哪些 build-in 的资源类型
  buildInScheme "k8s.io/client-go/kubernetes/scheme"
  scheme = runtime.NewScheme()
  _ = buildInScheme.AddToScheme(scheme)
	_ = alpha666.AddToScheme(scheme)

------------------------------------------------------------------

  // or:
  import (
    // 引入 runtime
    "k8s.io/apimachinery/pkg/runtime"
    // 引入 SchemeBuilder 所在的那个 package
    v1alpha1666 "xxxxxx/yyyyyy/xxxxx"
  )
  AddToSchemes = append(runtime.SchemeBuilder, v1alpha1666.SchemeBuilder.AddToScheme)
  
  mgr, err := ctrl.NewManager(cfg, ctrl.Options{SyncPeriod: &options.SyncPeriod, MetricsBindAddress: options.MetricsAddr})

  s := mgr.GetScheme()
  AddToSchemes.AddToScheme(s)

5、直到调用完 AddToScheme 才算是这个 CRD(CRDS) 被注册完事儿,之后需要需要对所有的 CRD 进行Setup

func Setup(mgr ctrl.Manager, l logr.Logger) error {
	for _, setup := range []func(ctrl.Manager, logr.Logger) error{
    // ...
    corecontroller.ServerSetup,
    // ...
	} {
		if err := setup(mgr, l); err != nil {
			return err
		}
	}
	return nil
}
// 可以和 Reconcile 定义在一起
func ServerSetup(mgr ctrl.Manager, l logr.Logger) error {
	// ...
	return ctrl.NewControllerManagedBy(mgr).
		Named(name).
		For(&corev1alpha2.ServerWorkload{}).
		Complete(NewServerWorkloadReconciler(mgr,
			sagecore.WithLogger(l.WithValues("controller", name)),
			sagecore.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))))
}
func NewServerWorkloadReconciler(m ctrl.Manager, o ...sagecore.ReconcilerOption) *ServerWorkloadReconciler {
	sr := &ServerWorkloadReconciler{}
	sr.Client = m.GetClient()
	sr.Scheme = m.GetScheme()
	sr.WorkloadRender = sagerender.SageRender{}
  sr.Record = event.NewNopRecorder()
	proxy := &sageproxy.ApplicatorProxy{
		Client: m.GetClient(),
		Scheme: m.GetScheme(),
		Log:    sr.Log,
	}
	sr.Proxy = proxy
  // 还可以处理一些GC 相关
	return sr
}

7、在 ctrl.NewControllerManagedBy 之后总会调用 complete 方法
8、complete 内部大概逻辑是,doController 方法,初始化一个 Controller, 这个 Controller 有 Cache 负责注册 Watch,Client 负责Kind 的 CUD 没有R,Queue 负责对 Watch 到的资源的事件做缓存,Recorder 负责事件收集,还有重要的 Do 这个就是自己写的那个 Reconcile 方法。
9、之后内部还会触发一个 doWatch 方法,该方法里头初始化了一个 handler,然后调用上面初始化的 Controller 的 Watch 方法,但是 这个 Watch 不是真的开始监听,而是先初始化该类型的 CRD 的 Watch,包括把 handler,src源,queue 等传进去
10、complete 到这儿基本就算是完事儿了,之后调用 manager.Start 方法

return errors.Wrap(mgr.Start(ctrl.SetupSignalHandler()), "can not start controllers")

11、这个方法中,主要逻辑就是启动 Cache 以及 Controller
12、启动 Cache 的话,是去找第2步中的那个 informerMap, 然后把每一个 informer 都run起来,run起来就算是真正开始和 Api Server 建立 List Watch 链接开始监听该 Kind 的 GVK 了
13、每当 Api Server 给发送资源改变的消息之后,会触发对应的第9步中的 handler,handler 中一共有 delete,create,update 三个方法,但是这三种方法干的事儿都是一样的,就是单纯地将对应的资源的 name 和所在的 namespace 放入 queue中(具体到底是 CUR 哪个操作需要在 Reconcile 中自己判断)
14、最后启动 Controller,Controller 中会启动一个 goroutine 的协程不停地查询 queue,如果 queue 中还有东西的话,就 Get 出来,然后会调用该 controller 上的 Do(第5步) 上的 Reconcile 方法,这个方法就是真正自己定义的那个 Reconcile
注意,每个 CRD 都有自己的一个 controller,因为在注册每个 CRD 的时候都会调用 complete 方法,doController 就是在这个 complete 中初始化的

标签:err,ctrl,笔记,mgr,controller,kubebuilder,CRD,go
From: https://www.cnblogs.com/wuchangblog/p/17416221.html

相关文章

  • 人月神话 读书笔记 03
    第9章削足适履9.1程序有多大?除了运行时间以外,它所占据的空间也是主要开销。当系统设计者认为对用户而言,常驻程序内存的形式比加法器、磁盘等更加有用时,他会将硬件实现中的一部分移到内存上。相反的,其他的做法是非常不负责任的。由于规模是软件系统产品用户成本中如此大的一个......
  • 人件集 人性化的软件开发阅读笔记03
    《人件集人性化的软件开发》第三部分工作组织第八章:团队的目标和规划这一章主要讲述如何制定合理的团队目标和规划,以及如何实现这些目标和规划。作者提出,确定特定的、可衡量的目标,并建立一个可靠的评估机制,以便团队能够不断改进和实现更好的效果。我认为这一章非常实用,对于团......
  • Redis笔记(二):三种特殊类型
    geospatial地理位置GEOADDkey[NX|XX][CH]longitudelatitudemember[longitudelatitudemember...]地球两极无法直接添加经度纬度GEODIST#单位m,km,mi,ftGEOHASHGEOPOSGEORADIUSGEORADIUSBYMEMBERJava中的数据结构......
  • sqli-lab学习笔记(学习笔记)(1-10)
    经常会犯的错误:1.在hackbar里面忘记用%23替代#2.在用下一条语句的时候忘记更改limit的值导致结果显示不出来3.双注入里面的语句忘记加()日志是我后来想到发的,前面几关也都基础简单的,giantbranch大佬写的挺好的很详细,我也是按他的一步步学习来的,然后在里面增加自己的知识点和......
  • ④ActiveMQ 与 SpringBoot 集成——(动力节点)ActiveMQ笔记
    第四章ActiveMQ与SpringBoot集成4-1ActiveMQ与SpringBoot集成集成配置1、加载springboot的activeMQ的依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><depen......
  • JavaScript学习笔记: 函数
    概念在js中,函数与其他类型一样,是一个支持所有操作的值,是一个对象,是编程语言里的“一等公民”函数是一个代码块,每被调用一次,其代码就会执行一次。函数有一个被{}包裹的函数体,具体的逻辑代码就写在里面。使用return关键字返回函数的计算结果,如果没有返回值,那函数调用表达式的值......
  • docker从入门到实践学习笔记【环境ubuntu16.04】【一】
    镜像加速国内从DockerHub拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker官方和国内很多云服务商都提供了国内加速器服务,例如: Docker官方提供的中国registrymirrorhttps://registry.docker-cn.com七牛云加速器https://reg-mirror.qiniu.com/ 我们以Docker官......
  • docker从入门到实践学习笔记【环境ubuntu16.04】【二】
     目录获取镜像 运行容器列出镜像虚悬镜像中间层镜像 删除本地镜像批量删除镜像  镜像是docker的三大组件之一。Docker运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker会从镜像仓库下载该镜像。 获取镜像 从Docker镜像仓库获取镜像的命令是dockerpull。......
  • docker从入门到实践学习笔记【环境ubuntu16.04】【三】
    目录 新建容器并启动启动已经终止的容器后台运行终止容器进入容器导入和导出容器导出容器导入容器删除容器清理所有终止状态的容器新建容器并启动 dockerrun 例如,下面的命令输出一个“HelloWorld”,之后终止容器 $dockerrunubuntu:14.04/bin/echo'Helloworld'Hellow......
  • docker从入门到实践学习笔记【环境ubuntu16.04】【四】
    目录数据卷创建一个数据卷查看所有数据卷查看指定数据卷的详细信息启动容器的同时挂在数据卷查看容器的信息删除数据卷在容器销毁时自动删除数据卷挂载主机目录作为数据卷挂载一个本地主机文件作为数据卷  数据卷数据卷特性:1.可以在容器之间共享和重用2.对数据卷的修改立马生效3.......