文章目录
前言
Kubernetes 中使用 http 进行通信,如何不依赖中间件的情况下保证消息的实时性,可靠性和顺序性等呢?答案就是利用了 Informer 机制。Informer 的机制,降低了了 Kubernetes 各个组件跟 Etcd 与 Kubernetes API Server 的通信压力。
一、Informer 机制架构设计
Informer 机制架构设计
核心组件介绍如下:
- Reflector:用于 Watch 指定的 Kubernetes 资源,当 watch 的资源发生变化时,触发相应的变更事件,比如 Added,Updated 和 Deleted 事件,并将资源对象存放到本地缓存 DeltaFIFO;
- DeltaFIFO:分开理解,FIFO 就是一个先进先出的队列,它拥有队列基本方法(ADD,UPDATE,DELETE,LIST,POP,CLOSE 等),Delta 是一个资源对象存储,保存存储对象的操作类型,比如 Added,Updated,Deleted,Sync 等;
- Indexer:Client-go 用来存储资源对象并自带索引功能的本地存储,Reflector 从 DeltaFIFO 中将消费出来的资源对象存储到 Indexer,Indexer 与 Etcd 集群中的数据完全保持一致。从而 client-go 可以本地读取,减少 Kubernetes API 和 Etcd 集群的压力。
二、Informer 机制
1 Informers
1.1 Informers Example 代码示例
代码如下(示例):
package main
import (
"time"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
panic(err.Error())
}
// clientset 用于与K8s交互的客户端
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
// stopCh 对象用于在程序进程退出之前通知Informer提前退出,Informer 是一个持久运行的 goroutine
stopCh := make(chan struct{})
defer close(stopCh)
// 第1个参数clientset是用于与K8s交互的客户端
// 第2个参数用于设置多久进行一次 resync (重新同步),resync 会周期性地执行List操作,将所有的资源存放在 Informer Store 中,
// 如果该参数为0,则禁用 resync 功能。
sharedInformerFactory := informers.NewSharedInformerFactory(clientset, time.Minute)
// informer 是具体的Pod资源的informer对象
informer := sharedInformerFactory.Core().V1().Pods().Informer()
informer.Run(stopCh)
}
go.mod 如下
require (
k8s.io/api v0.25.4
k8s.io/apimachinery v0.25.4
k8s.io/client-go v0.25.4
)
执行:go mod tidy,更新第三方依赖
2 Indexer
Indexer是client-go用来存储资源对象并自带索引功能的本地存储,Reflector从DeltaFIFO中将消费出来的资源对象存储至Indexer。Indexer中的数据与Etcd集群中的数据保持完全一致。client-go可以很方便地从本地存储中读取相应的资源对象数据,而无须每次都从远程 Etcd集群中读取,这样可以减轻Kubernetes API Server 和Etcd集群的压力。
2.1 Indexer Example 代码示例
代码如下(示例):
package main
import (
"fmt"
"strings"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
)
//
// UsersIndexFunc
// @Description: 索引器函数,查询出所有 Pod 资源下 Annotations 字段的 key 为 city 的 Pod
// @param obj
// @return []string
// @return error
//
func UsersIndexFunc(obj interface{}) ([]string, error) {
pod := obj.(*v1.Pod)
usersString := pod.Annotations["city"]
return strings.Split(usersString, ","), nil
}
func main() {
// 实例化 Indexer 对象
index := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{"byCity": UsersIndexFunc})
pod1 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-one",
Annotations: map[string]string{"city": "beijing,shanghai"},
},
}
pod2 := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-two",
Annotations: map[string]string{"city": "guangzhou,shenzhen"},
},
}
index.Add(pod1)
index.Add(pod2)
// 查询 byCity 索引器下匹配 beijing 字段的 Pod 列表
beijingPods, err := index.ByIndex("byCity", "beijing")
if err != nil {
panic(err.Error())
}
for _, beijingPod := range beijingPods {
fmt.Println(beijingPod.(*v1.Pod).Name)
}
}
go.mod 如下
require (
k8s.io/api v0.25.4
k8s.io/apimachinery v0.25.4
k8s.io/client-go v0.25.4
)
执行:go mod tidy,更新第三方依赖
总结
本文仅给出 Informers Example 代码示例和 Indexer Example 代码示例,是为了方便学习 Kubernetes Informer 机制的同学快速上手跑通,想要深入研究的同学,可以通过参考资料研究学习。
参考资料
1、《Kubernetes 源码剖析》郑东旭/著-第5章 5.3 节 Informer 机制
2、https://github.com/kubernetes/sample-controller/blob/master/docs/controller-client-go.md
3、深入了解 Kubernetes Informer