Kubernetes Controller

Kubernetes 的一个核心概念就是 “控制论” (Control theory) ,当然这是一个很大的学术命题,这里我们只需要明白,Kubernetes 通过声明式的 API 创建 “期望状态”,Controllers 就负责逐渐将当前状态转化为 “期望状态”。

这个过程被称为 “调谐” (reconciliation) 。

kube-controller-manager 管理着多组控制器 (如 replicaset-controller deployment-controller 等),每个控制器都监测不同的资源对象,确保它们处于用户 “希望的状态”,这被称为 “控制器模式” (Controller Pattern) 。


控制器模式

Kube-Controller

Controller

A single elected master per cell serves both as the Paxos leader and the state mutator.

正如 Borg 论文中提到的,Controller 在此处的作用就是充当一个 “State Mutator” ,维护的其实就是一个状态机。

这里需要认识到 Kubernetes 中有两种 “状态“:

  • “期望的状态” :期望状态由用户通过 yaml 文件进行描述,通过 kubectl apply 提交并写入到 etcd 中进行保存;
  • “实际的状态“ :即 资源对象 实际上所处的状态,获取实际状态一般通过两种方法:
    • 每个节点上 kubelet 通过心跳回报容器状态和节点状态,或是监控系统中保存的 metrics ;
    • Controller 主动收集的状态,多在自定义控制器中使用。

Controller 控制器 的作用就是持续、高效地获取 etcd 中的状态及状态更新,并在实际环境中实现状态的更新。

Controller 如何做到这点?首先我们需要对 Controller 有个大概的认识:

kube-controller-manager 是 Kubernetes 核心组件之一,以容器的形式在主节点上运行,它管理着许多组 Controllers 。

每个 Controller 主要包括 Informer 与 Control Loop 两个主要逻辑部分:

  • Informer : 同步并缓存 etcd 中的状态信息,对资源事件进行回调处理;
  • Control Loop :对比实际状态和期望状态,调谐 (Reconcile) ,执行状态变更操作。

当然你可能会注意到图中工作队列的存在,这个工作队列会将 Informer 中的缓存信息按索引传递到 Control Loop 中,提供一个异步相应的机制,在提升处理效率的同时,也做到了一定程度的解耦 (Decoupled) 。


Informer

Informer 是由 client-go 实现的一套 “本地缓存索引” 与 “资源事件处理” 的机制。总的来说,Informer 的职责有两个:

  • 维护并同步 API 对象的本地缓存;

    Informer 负责建立与 API Server 的连接,在第一次被调用的时候,Informer 中的 Reflector 会在客户端本地调用 List 获取全量的 API 对象集合, 并通过 Watch 来 “监听” 这些对象实例的变化。这也就是我们常听说的 List&Watch 机制。

  • 根据从 API Server 中获取的事件,触发不同的 ResourceEventHandler

    在 List&Watch 机制下,一旦 API Server 端游新的 API 对象实例被创建、删除、更新,Reflector 都会收到 “事件通知” (这么一说听起来很像 “事件驱动” 的那套机制) 。

    此时,该事件与对应的 API 对象 (称之为 “增量” , Delta) ,就会被放入一个 Delta FIFO Queue (增量先进先出队列) 中。

    Informer 会不断从这个队列中 Pop 增量,对每个增量判断事件类型,触发实现注册好的回调函数,并更新本地缓存。

简单的理解,Informer 通过一种 List&Watch 的方法,将 API Server 中的 API 对象缓存在本地,并负责更新与维护这个缓存。

List&Watch
Informer 通过 LIST API “获取” 所有最新版本的 API 对象;通过 WATCH API ”监听“ 所有 API 对象的变化情况。

在这个过程中,每经过 resyncPeriod 时间,Informer 都会使用最近一次 List 返回的结果强制更新一次本地的缓存,保证缓存的有效性。

同时,这个 resync 操作也会触发 Informer 的 “更新” 事件,但由于该 “更新” 事件对应的 API 对象其实已经是最新的了,所以这个时候 Informer 就不需要再对这个更新事件进行任何处理了。


Work Queue

Informer 与 Control Loop 之间的工作队列用于同步 Informer 与 控制循环之间的数据。


Control Loop

要知道,我们从 Informer 的缓存中,乃至于从 ETCD 的存储中获取到的 API 对象的状态,都是我们在 YAML 文件中规定的 “期望状态” ,那么这里肯定会存在一个 “实际状态” ,如何使 API 对象实例对应的 “期望状态” 在实际系统中生效,就是 Control Loop 的工作。

Control Loop 不停的循环,从各种 Lister 中获取目标 API 对象的状态信息,并与实际对象状态进行对比 diff(object1, actual) ,依照对比的结果,执行 删除、修改、新增 等操作。



编写自定义控制器

编写一个自定义的控制器,可以更加深刻地理解上述控制器的工作流程。

Custom Controller


References

  1. “A Deep Dive Into Kubernetes Controller”, https://engineering.bitnami.com/articles/a-deep-dive-into-kubernetes-controllers.html
  2. “深入解析声明式API 系列”, https://time.geekbang.org/column/article/41876
  3. “Controllers And Operators”, https://octetz.com/docs/2019/2019-10-13-controllers-and-operators/