在看 kube-scheduler 组件的过程中遇到了 kube-scheduler 对于 client-go 的调用,泛泛的理解调用过程总有种隔靴搔痒的感觉,于是调转头先把 client-go 理清楚在回来看 kube-scheduler。
为什么要看 client-go,并且要深入到原理,源码层面去看。很简单,因为它很重要。重要在两方面:
kubernetes 组件通过 client-go 和 kube-apiserver 交互。
client-go 简单,易用,大部分基于 Kubernetes 做二次开发的应用,在和 kube-apiserver 交互时会使用 client-go。
当然,不仅在于使用,理解层面,对于我们学习代码开发,架构等也有帮助。
- client-go 客户端对象
client-go 支持四种客户端对象,分别是 RESTClient,ClientSet,DynamicClient 和 DiscoveryClient:
image
组件或者二次开发的应用可以通过这四种客户端对象和 kube-apiserver 交互。其中,RESTClient 是最基础的客户端对象,它封装了 HTTP Request,实现了 RESTful 风格的 API。ClientSet 基于 RESTClient,封装了对于 Resource 和 Version 的请求方法。DynamicClient 相比于 ClientSet 提供了全资源,包括自定义资源的请求方法。DiscoveryClient 用于发现 kube-apiserver 支持的资源组,资源版本和资源信息。
每种客户端适用的场景不同,主要是对 HTTP Request 做了层层封装,具体的代码实现可参考 client-go 客户端对象。
2. informer 机制
仅仅封装 HTTP Request 是不够的,组件通过 client-go 和 kube-apiserver 交互,必然对实时性,可靠性等有很高要求。试想,如果 ETCD 中存储的数据和组件通过 client-go 从 ETCD 获取的数据不匹配的话,那将会是一个非常严重的问题。
如何实现 client-go 的实时性,可靠性?client-go 给出的答案是:informer 机制。
image
client-go informer 流程图
informer 机制的核心组件包括:
Reflector: 主要负责两类任务:
通过 client-go 客户端对象 list kube-apiserver 资源,并且 watch kube-apiserver 资源变更。
作为生产者,将获取的资源放入 Delta FIFO 队列。
Informer: 主要负责三类任务:
作为消费者,将 Reflector 放入队列的资源拿出来。
将资源交给 indexer 组件。
交给 indexer 组件之后触发回调函数,处理回调事件。
Indexer: indexer 组件负责将资源信息存入到本地内存数据库(实际是 map 对象),该数据库作为缓存存在,其资源信息和 ETCD 中的资源信息完全一致(得益于 watch 机制)。因此,client-go 可以从本地 indexer 中读取相应的资源,而不用每次都从 kube-apiserver 中获取资源信息。这也实现了 client-go 对于实时性的要求。
接下来从源码角度看各个组件的处理流程,力图做到知其然,知其所以然。
2 informer 源码分析
直接阅读 informer 源码是非常晦涩难懂的,这里通过 informer 的代码示例开始学习:
package main
import (
"log"
"time"