client-gokubernetes官方维护go语言实现的客户端客,可以用来对kubernetes做二次开发,但当我在初次看代码中自带的例子时会被ReflectorInformerIndexerDeltaFIFO等一大堆概念搞晕,根本不知道从哪开始,看来没有捷径只能来系统学习下它。

关于client-go的架构官方其实有一篇介绍文档并配了一张架构图

img

一些概念或组件

  • Reflector:实现对apiserver资源对象的监控,当对象Add/Update/Del变化时能及时捕获到。
  • DeltaFIFO:FIFO队列,Reflector捕获到的对象会放入此队列,队列里边的元素称为Delta
  • Informer:从DeltaFIFO取出Delta然后Add/Update/Delete Indexer并调用相应的回调函数。
  • Indexer:本地缓存
  • Resource Event Handlers: 资源事件处理函数,就是当监听到kubernetes对象变化时应该干什么。
  • Controller: 控制器,负责控制整个系统的工作。

需要自已开发实现的部分

自定义控制器需要实现的部分已经在图中用黄色标记出来了,而且官方还提供了一个例子,我们来结合上边的架构图来分析下这个例子以便对上边的介绍的这些概念和运行机制有一个初步的了解。

我们先来分析下main函数,代码做了精简

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

func main() {
  // ...省略
  
	// 创建一个watcher供后边informer调用获取pod列表并监听pod变化
	podListWatcher := cache.NewListWatchFromClient(clientset.CoreV1().RESTClient(), "pods", v1.NamespaceDefault, fields.Everything())

	// 一个工作队列,对应图中步骤(7)
	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())

   // 比较关键,指定Resource Event Handlers函数创建Infomer
	indexer, informer := cache.NewIndexerInformer(podListWatcher, &v1.Pod{}, 0, cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
        // ...
				queue.Add(key)
		},
		UpdateFunc: func(old interface{}, new interface{}) {
			// ...
				queue.Add(key)
		},
		DeleteFunc: func(obj interface{}) {
      // ...
				queue.Add(key)
		},
	}, cache.Indexers{})

	controller := NewController(queue, indexer, informer)

  // ...省略

  启动控制器
	go controller.Run(1, stop)
   
  // ...
}

总结起来就是:

  1. 创建一个watcher,因为要监听资源变化嘛,好理解(怎么知道)
  2. 创建一个Informer关联watcher和资源事件处理函数,也好理解,监听到资源变化后应该干什么(知道后干什么)
  3. 工作队列,其实非必须,但是为什么需要呢,因为资源可能很多,如果监听一个就处理一个可能会有各种问题,所以需要加个队列方便控制(有计划有顺序的干)

然后我们再来分析下controller.Run,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func (c *Controller) Run(workers int, stopCh chan struct{}) {
   // ...
	go c.informer.Run(stopCh)

   // 等待watch的资源都同步到本地缓存也就是Indexer
	if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {
		runtime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
		return
	}

  // 从任务队列中获取任务执行
	for i := 0; i < workers; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}
  // ...
}

controller.Run负责下指令让程序跑起来:

  1. 让Informer跑起来,监听kubernetes资源的变化并执行指定的事件回调函数,回调函数向任务队列中添加任务
  2. worker从任务队列中取出任务执行

示例虽简但五脏俱全,把上边介绍的各种概念都展示了一遍。示例中使用NewIndexerInformer函数创建出来的informer其实是比较偏底层的,实际应用中更多会使用NewSharedIndexInformer创建更高封装的SharedIndexInformer ,但就是因为它更偏底层所以对我们理解整个client-go特别是informer的运行机制是非常有帮助的。

总结

理解client-go整个架构对我们在基此做二次开发是很有必要的,同时也能帮助我们理解kubernetes自身各种控制器的原理。

参考

https://blog.csdn.net/weixin_42663840/article/details/81699303

https://cloud.tencent.com/developer/article/1594941?from=10680

https://github.com/kubernetes/sample-controller/blob/master/docs/controller-client-go.md