本文为《Kubernetes 源码剖析》读书笔记,书籍简介: http://www.broadview.com.cn/book/6104
一、简介
Kubernetes 中,控制器需要对集群中的资源对象的状态进行监控,使资源对象的实际状态和通过 yaml 定义的期望状态协调达到一致。那么控制器是如何对资源对象进行监控,并根据对象的实际状态变化做出相应的处理呢?实际上就是通过 Client-go 包中的 Informer 机制实现的。
图片来源:极客时间 --《深入浅出 Kubernetes》
从上图中,我们可以大致了解到 Informer 机制的整个流程:
- Reflector 组件通过对 apiserver 发起 HTTP 请求,List 得到集群中所有资源对象,再通过 HTTP 长连接 Watch 不断监听得到 etcd 变化的数据,封装成一个个事件(Event),再将事件对应的资源对象放入 Delta FIFO 队列中
- Delta FIFO 队列保存了需要操作的资源对象,以及要对该资源对象进行什么操作(Add、Updated、Deleted、Sync)
- Indexer 是一个带索引的本地缓存,它从Delta FIFO 队列取出资源对象的索引(和 etcd 的 key 保持一致)和具体数据进行保存
- 从Delta FIFO 队列中取出的资源对象还会触发三种方法的回调:Add、Delete、Update 分别对应 etcd 中资源对象的增、删、改操作。回调方法中会将需要处理的资源对象的索引(Index)放入 WorkQueue 工作队列中
- Control Loop 控制循环不断地从 WorkQueue 中取出要处理对象的索引,通过 Lister 从 Indexer 维护的本地缓存中获取具体的资源对象数据,进行相应的处理。
二、Reflector 底层原理分析
简单来说,Reflector 就做了两件事:List 和 Watch
// k8s.io/client-go/tools/cache/reflector.go
func (r *Reflector) ListAndWatch(stopCh <-chan struct{
}) error {
...
if err := func() error {
...
pager := pager.New(pager.SimplePageFunc(func(opts metav1.ListOptions) (runtime.Object, error) {
return r.listerWatcher.List(opts)
}))
...
w, err := r.listerWatcher.Watch(options)
...
}
List 指的是向 apiserver 发送 GET 请求获取集群中所有资源的数据,Watch 则是通过与 apiserver 建立 HTTP 长连接并通过分块编码传输(chunked)来监控资源对象的变更事件。而这些请求的发送都是通过我们前面提到的 ClientSet 客户端实现的。
分块编码传输简介
// 提供了 List 和 Watch 接口,让资源对象去实现具体的接口
type ListWatch struct {
ListFunc ListFunc
WatchFunc WatchFunc
// 是否禁用 watch 时的分块编码传输
DisableChunking bool
}
type ListFunc func(options metav1.ListOptions) (runtime.Object, error)
type WatchFunc func(options metav1.ListOptions) (watch.Interface, error)
// 以 Pod 资源对象为例,底层通过 ClientSet 客户端向 apiserver 发送请求
// k8s.io/client-go/informers/core/v1/pod.go
func NewFilteredPodInformer(client kubernetes.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.CoreV1().Pods(namespace