client-go 最终章


EventBroadcaster 事件管理器

Kubernetes 的事件(Event)是一种资源对象(Resource Object),用于展示集群内发生的情况,Kubernetes 系统中的各个组件会将运行发生的各种事件上报给 Kubernetes API Server。

此处的 Event 事件是 Kubernetes 所管理的 Event 资源对象,而非 Etcd 集群监控机制产生的回调事件。

  • Kubernetes 系统以 Pod 资源为核心,Deployment,StatefulSet,ReplicaSet,DaemonSet,CronJob 等,最终都会创建出 Pod。因此 Kubernetes 事件也是围绕 Pod 进行的,在 Pod 生命周期内的关键步骤都会产生事件消息。

  • Event 资源数据结构描述了当前时间段内发生了哪些关键性事件。事件有两种类型,分别为 Normal 和 Warning,前者为正常事件,后者为警告事件。

EventBroadcaster 事件管理机制运行原理

Actor 可以是 Kubernetes 系统中的任意组件,当组件中发生了一些关键性事件时,可通过 EventRecorder 记录事件。EventBroadcaster 事件管理机制可以分为如下部分:

  • EventRecorder: 事件生产者,也称为事件记录器。Kubernetes 系统组件通过 EventRecorder 记录关键性事件。
  • EventBroadcaster: 事件消费者,也称为事件广播器。EventBroadcaster 消费 EventRecorder 记录的事件并将其分发给目前所有已连接的 broadcasterWatcher.分发过程由两种机制,分别是非阻塞分发机制和阻塞分发机制。
  • broadcasterWatcher: 观察者管理,用于定义事件的处理方式,例如上报事件至 Kubernetes API Server。

EventRecorder

EventRecorder 拥有如下 4 种记录方法,EventRecorder 事件记录器接口代码示例如下:

type EventRecorder interface {
    Event(...)
    // 对刚发生的事件进行记录
    Eventf(...)
    // 通过使用 fmt.Sprintf 格式化输出事件的格式
    PastEventf(...)
    // 允许自定义事件发生的时间,以记录已经发生过的消息
    AnnotatedEventf(...)
    // 功能与 Eventf 一样,但附加了注释字段
}

Event 方法示例,记录当前发生的事件,Event -> recorder.generateEvent -> recorder.Action 代码示例如下:

func (m *Broadcaster) Action (action EventType, obj runtime.Object) {
    m.incoming <- Event{action, obj}
}

Action 函数通过 goroutine 实现异步操作,该函数将事件写入 m.incommit Chan 中,完成事件生成过程。

EventBroadcaster

EventBroadcaster 消费 EventRecorder 记录的事件并将其分发给目前所有已连接的 broadcasterWatcher.EventBroadcaster 通过 NewBroadcaster 函数进行实例化:

func NewBroadcaster() EventBroadcaster {
    return &eventBroadcasterImpl{watch.NewBroadcaster(maxQueuedEvents, watch.DropIfChannelFull), defaultSleepDuration}
}

在实例化过程中,会通过 watch.NewBroadcaster 函数在内部启动 goroutine 来监控 m.incoming,并将监控的事件通过 m.distribute 函数分发给所有已连接的 broadcasterWatcher。在非阻塞分发机制下使用 DropIfChannelFull 标识,在阻塞分发机制下使用 WaitIfChannelFull 标识,默认为 DropIfChannelFull 标识。代码示例如下:

func (m *Broadcaster) distribute (event Event) {
    m.lock.Lock()
    defer m.lock.Unlock()
    if m.fullChannelBehavior == DropIfChannelFull {
        for _, w := range m.watchers {
            select {
                case w.result <- event:
                case <- w.stopped:
                default:
            }
        }
    } else {
        for _, w := range m.watchers {
            select {
                case w.result <- event:
                case <- w.stopped:
            }
        }
    }
}

在分发过程中,DropIfChannelFull 标识位于 select 多路复用中,使用 default 关键字做非阻塞分发,当 w.result 缓冲区满的时候,事件会丢失。WaitIfChannelFull 标识也位于 select 多路复用中,没有 default 关键字,当 w.result 缓冲区满的时候,分发过程会阻塞并等待。

Kubernetes 中的事件与其他的资源不同,它有一个很重要的特性,那就是它可以丢失。(与 etcd 的性能有关,事件的重要性远低于集群的稳定性)

broadcasterWatcher

broadcasterWatcher 是每个 Kubernetes 系统组件自定义处理事件的方式。每个 broadcasterWatcher 拥有两种自定义处理事件的函数,分别介绍如下:

  • StartLogging: 将事件写入日志中。
  • StartRecordingToSink: 将事件上报至 Kubernetes API Server 并存储至 Etcd 集群。

以 kube-scheduler 组件为例,该组件为一个 broadcasterWatcher,通过 StartLogging 函数将事件输出至 klog stdout 标准输出,通过 StartRecordingToSink 函数将关键性事件上报至 Kubernetes API Server。代码示例如下:

if cc.Brodcaster != nil && cc.EventClient != nil {
    cc.Broadcaster.StartLogging(klog.V(6).Infof)
    cc.Broadcaster.StartRecordingToSink(&vlcore.EventSinkImpl{Interface: cc.EventClient.Events("")})
}

StartLogging 和 StartRecordingToSink 函数依赖于 StartEventWatcher 函数,该函数内部运行了一个 goroutine,用于不断监控 EventBroadcaster 来发现事件并调用相关函数对事件进行处理。

下面重点介绍下 StartRecordingToSink 函数,kube-scheduler 组件将 v1core.EventSinkImpl 作为上报事件的自定义函数。

  • 上报事件有三种方法,分别是 Create(Post), Update(Put), Patch(Patch),以 Create 方法为例,Create -> e.Interface.CreateWithEventNamespace 代码示例如下:
func (e *events) CreateWithEventNamespace(event *v1.Event) (*v1.Event, error) {
    ...
    result := &v1.Event{}
    err := e.client.Post().NamespaceIfScoped(event.Namespace, len(event.Namespace) > 0).Resource("events").Body(event).Do().Into(result)
    return result, err
}

上报过程通过 RESTClient 发送 Post 请求,将事件发送至 Kubernetes API Server,最终存储在 Etcd 集群中。

代码生成器

前面已经讲解过 5 种代码生成器的核心实现,它们用于在构建 Kubernetes 核心组件之前生成执行代码。

本节将介绍关于 client-go 的其他 3 种代码生成器,如下表所示:

代码生成器 说明
client-gen client-gen 是一种为资源生成 ClientSet 客户端的工具
lister-gen lister-gen 是一种为资源生成 Lister 的工具(即 get 和 list 方法)
informer-gen informer-gen 是一种为资源生成 informer 的工具

client-gen 代码生成器

client-gen 代码生成器是一种为资源生成 ClientSet 客户端的工具。ClientSet 对 Group,Version,Resource 进行了封装,可以针对资源执行生成资源操作方法(create,update,delete,get 等),这些方法由 client-gen 代码自动生成器自动生成。

client-gen 代码生成器的代码生成策略与 Kubernetes 的其他代码生成器类似,都通过 Tags 来识别一个包是否需要生成代码及确定生成代码的方式。

  • 生成基本的资源操作方法,例如 create, update, delete, get, list, patch, watch 等方法。另外,如果存在 Status 字段,则生成 UpdateStatus 函数,其 Tags 形式如下:
// +genclient
  • 生成基本的资源操作函数,不生成 Namespaced 函数,其 Tags 形式如下:
// +genclient:nonNamespaced
  • 即便存在 Status 字段,也不生成 UpdateStatus 函数,其 Tags 形式如下:
// +genclient:noStatus 
  • 不生成基本的资源操作方法,其 Tags 形式如下:
// +genclient:noVerbs
  • 仅生成指定的基本操作方法,例如 create, get 方法,其 Tags 形式如下:
// +genclient:onlyVerbs=create,get
  • 生成基本的资源操作方法,但排除 watch 方法,其 Tags 形式如下:
// +genclient:skipVerbs=watch
  • 生成相关扩展函数,例如生成 UpdateScale 函数,在 UpdateScale 函数中会对 scale 子资源进行 update(更新)操作,input 和 result 是用于 设置输入和输出的参数,其 Tags 形式如下:
// +genclient:method=UpdateScale,verb=update,subresource=scale,input=Scale,result=Scale

lister-gen 代码生成器

lister-gen 代码生成器是一种为资源生成 Lister 的工具。Lister 为每一个 Kubernetes 资源提供 Lister 功能(即提供 get 和 list 方法)。get 和 list 方法为客户端提供只读的本地缓存数据。

  • lister-gen 代码生成器的代码生成策略与 Kubernetes 的其他代码生成器类似,都通过 Tags 来识别一个包是否需要生成代码及确定生成代码的方式。

  • lister-gen 代码生成器与其他代码生成器相比,其并没有可用的 Tags,它依赖于 client-gen 的代码生成器 // +genclient 标签。

informer-gen 代码生成器

informer-gen 是一种为资源生成 informer 的工具,Informer 为 client-go 提供了与 Kubernetes API Server 通信机制,这些功能由 informer-gen 代码生成器自动生成。

  • informer-gen 代码生成器的代码生成策略与 Kubernetes 的其他代码生成器类似,都通过 Tags 来识别一个包是否需要生成代码及确定生成代码的方式。

  • informer-gen 代码生成器与其他代码生成器相比,其并没有可用的 Tags,它依赖于 client-gen 的代码生成器 // +genclient 标签,类似于 lister-gen 代码生成器。


文章作者: MJ-CJM
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MJ-CJM !
评论
  目录