今后之后运维与开发过上了没羞没臊的性福生活

> 原文连接:Kubernetes 控制器的进化之旅缓存

我是一堆 Kubernetes 控制器。微信

你可能会疑惑为何是一堆,由于我不是一我的,我只是众多控制器中的一员,你也能够把我当作是众多控制器的集合。个人职责就是监控集群内资源的实际状态,一旦发现其与指望的状态不相符,就采起行动使其符合指望状态。网络

想当初,Kubernetes 老大哥创造我时,只是打算让我用控制循环简单维护下资源的状态。但我后来的发展,远远超出了他的想象。app

1. 控制循环

所谓控制循环就是一个用来调节系统状态的周期性操做,在 Kubernetes 中也叫调谐循环(Reconcile Loop)。个人手下控制着不少种不一样类型的资源,好比 Pod,Deployment,Service 等等。就拿 Deployment 来讲吧,个人控制循环主要分为三步:运维

  1. API Server 中获取到全部属于该 Deployment 的 Pod,而后统计一下它们的数量,即它们的实际状态。
  2. 检查 Deployment 的 Replicas 字段,看看指望状态是多少个 Pod。
  3. 将这两个状态作比较,若是指望状态的 Pod 数量比实际状态多,就建立新 Pod,多几个就建立几个新的;若是指望状态的 Pod 数量比实际状态少,就删除旧 Pod,少几个就删除几个旧的。

然而好景不长,我收到了 Kubernetes 掌门人(看大门的) API Server 的抱怨:“你访问个人次数太频繁了,很是消耗个人资源,我连上厕所的时间都没有了!”工具

我仔细一想,当前的控制循环模式确实有这个缺陷——访问 API Server 的次数太频繁了,容易被老大反感。oop

因此我决定,找一个小弟。post

2. Informer

此次我招的小弟叫 Informer,它分担一部分个人任务,具体的作法是这样的:由 Informer 代替我去访问 API Server,而我无论是查状态仍是对资源进行伸缩都和 Informer 进行交接。并且 Informer 不须要每次都去访问 API Server,它只要在初始化的时候经过 LIST API 获取全部资源的最新状态,而后再经过 WATCH API 去监听这些资源状态的变化,整个过程被称做 ListAndWatch编码

而 Informer 也不傻,它也有一个助手叫 Reflector,上面所说的 ListAndWatch 事实上是由 Reflector 一手操办的。插件

这一次,API Server 的压力大大减轻了,由于 Reflector 大部分时间都在 WATCH,并无经过 LIST 获取全部状态,这使 API Server 的压力大大减小。我想此次掌门人应该不会再批评我了吧。

然而没过几天,掌门人又找我谈话了:“你的手下每次来 WATCH 我,都要 WATCH 全部兄弟的状态,依然很消耗个人资源啊!我就纳闷了,你一次搞这么多兄弟,你虎啊?”

我一想有道理啊,不必每次都 WATCH 全部兄弟的状态,因而告诉 Informer:“之后再去 API Server 那里 WATCH 状态的时候,只查 WATCH 特定资源的状态,不要一古脑儿全 WATCH。“

Informer 再把这个决策告诉 Reflector,事情就这么愉快地决定了。

本觉得此次我会获得掌门人的夸奖,可没过几天安稳日子,它又来找我诉苦了:“兄弟,虽然你减轻了个人精神压力,但个人财力有限啊,若是每一个控制器都招一个小弟,那我得多发多少人的工资啊,你想一想办法。”

3. SharedInformer

通过和其余控制器的讨论,咱们决定这么作:全部控制器联合起来做为一个总体来分配 Informer,针对每一个(受多个控制器管理的)资源招一个 Informer 小弟,咱们称之为 SharedInformer。大家能够理解为共享 Informer,由于有不少资源是受多个控制器管理的,好比 Pod 同时受 DeploymentStatefulSet 管理。这样当多个控制器同时想查 Pod 的状态时,只须要访问一个 Informer 就好了。

但这又引来了新的问题,SharedInformer 没法同时给多个控制器提供信息,这就须要每一个控制器本身排队和重试。

为了配合控制器更好地实现排队和重试,SharedInformer 搞了一个 Delta FIFO Queue(增量先进先出队列),每当资源被修改时,它的助手 Reflector 就会收到事件通知,并将对应的事件放入 Delta FIFO Queue 中。与此同时,SharedInformer 会不断从 Delta FIFO Queue 中读取事件,而后更新本地缓存的状态。

这还不行,SharedInformer 除了更新本地缓存以外,还要想办法将数据同步给各个控制器,为了解决这个问题,它又搞了个工做队列(Workqueue),一旦有资源被添加、修改或删除,就会将相应的事件加入到工做队列中。全部的控制器排队进行读取,一旦某个控制器发现这个事件与本身相关,就执行相应的操做。若是操做失败,就将该事件放回队列,等下次排到本身再试一次。若是操做成功,就将该事件从队列中删除。(图片来自网络)

如今这个工做模式获得了你们的一致好评。虽然单个 SharedInformer 的工做量增长了,但 Informer 的数量大大减小了,老大能够把省下来的资金拿出一小部分给 SharedInformer 涨工资啊,这样你们都很开心。

4. CRD

全民 Kubernetes 时代到了。

随着容器及其编排技术的普及,使用 Kubernetes 的用户大量增加,用户已经不知足 Kubernetes 自带的那些资源(Pod,Node,Service)了,你们都但愿能根据具体的业务建立特定的资源,而且对这些资源的状态维护还要遵循上面所说的那一套控制循环机制。

幸亏最近掌门人作了一次升级,新增了一个插件叫 CRD(Custom Resource Definition),建立一个全新的资源实例,只须要通过如下两步:

  1. 建立一个 CRD 资源(没错,CRD 也是一种资源类型),其中定义”自定义资源“的 API 组API 版本资源类型。这样就会向 API Server 注册该资源类型的 API。
  2. 指定上面定义的 API 组 和 API 版本,建立自定义资源。

固然,中间还要加入一些代码让 Kubernetes 认识自定义资源的各类参数。

到这一步就基本上完成了自定义资源的建立,但 Kubernetes 并不知道该资源所对应的业务逻辑,好比你的自定义资源是宿主机,那么对应的业务逻辑就是建立一台真正的宿主机出来。那么怎样实现它的业务逻辑呢?

5. 自定义控制器

Controller Manager 见多识广,说:”这里的每一个控制器都是个人一部分,当初创造大家是由于大家都属于通用的控制器,你们都能用得上。而自定义资源须要根据具体的业务来实现,咱们不可能知道每一个用户的具体业务是啥,本身一拍脑壳想出来的自定义资源,用户也不必定用得上。咱们可让用户本身编写自定义控制器,大家把以前使用的控制循环和 Informer 这些编码模式总结一下,而后提供给用户,让他们按照一样的方法编写本身的控制器。“

Deployment 控制器一惊,要把本身的秘密告诉别人?那别人把本身取代了咋办?赶紧问道:”那未来我岂不是很危险,没有存在的余地了?“

Controller Manager 赶紧解释道:”不用担忧,虽然用户能够编写自定义控制器,但不管他们玩出什么花样,只要他们的业务跑在 Kubernetes 平台上,就免不了要跑容器,最后仍是会来求大家帮忙的,你要知道,控制器是能够层层递进的,他们只不过是在你外面套了一层,最后仍是要回到你这里,请求你帮忙控制 Pod。“

这下你们都不慌了,决定就把自定义控制器这件事情交给用户本身去处理,将选择权留给用户。

6. Operator

用户自从得到了编写自定义控制器的权力以后,很是开心,有的用户(CoreOS)为了方便你们控制有状态应用,开发出了一种特定的控制器模型叫 Operator,并开始在社区内推广,获得了你们的一致好评。不能否认,Operator 这种模式是很聪明的,它把须要特定领域知识的应用单独写一个 Operator 控制器,将这种应用特定的操做知识编写到软件中,使其能够利用 Kubernetes 强大的抽象能力,达到正确运行和管理应用的目的。

ETCD Operator 为例,假如你想手动扩展一个 ETCD 集群,通常的作法是:

  1. 使用 ETCD 管理工具添加一个新成员。
  2. 为这个成员所在的节点生成对应的启动参数,并启动它。

而 ETCD Operator 将这些特定于 etcd 的操做手法编写到了它的控制循环中,你只须要经过修改自定义资源声明集群指望的成员数量,剩下的事情交给 Operator 就行了。(图片来自网络)

本觉得这是一个皆大欢喜的方案,但没过多久,就有开发 Operator 的小哥来抱怨了:”咱们有不少开发的小伙伴都是不懂运维那一套的,什么高可用、容灾根本不懂啊,如今让咱们将运维的操做知识编写到软件中,臣妾作不到啊。。“

这确实是个问题,这样一来就把开发和运维的工做都塞到了开发手里,既懂开发又懂运维的可很少啊,为了照顾你们,还得继续想办法把开发和运维的工做拆分开来。

7. OAM

这时候阿里和微软发力了,他们联合发布了一个开放应用模型,叫 Open Application Model (OAM)。这个模型就是为了解决上面提到的问题,将开发和运维的职责解耦,不一样的角色履行不一样的职责,并造成一个统一的规范,以下图所示(图片来自网络):

这个规范告诉咱们:

  • 开发人员负责描述组件的功能,如何配置组件,以及运行须要多少资源
  • 运维人员负责将相关组件组合成一个应用,并配置运行时参数和运维支撑能力,好比是否须要监控,是否须要弹性伸缩。
  • 基础设施工程师负责创建和维护应用的运行时环境(如底层系统)。

其中每个团队负责的事情都用对应的 CRD 来配置。

这样一来,开发和运维人员的职责就被区分开来了,简化了应用的组合和运维。它将应用的配置和运维特征(如自动伸缩、流量监控)进行解耦,而后经过建模构成一个总体,避免了 Operator 这种模型带来的大量冗余。

自从用上了这个模型以后,运维和开发小哥表示如今他们的关系很融洽,没事还能一块儿出去喝两杯。

微信公众号

扫一扫下面的二维码关注微信公众号,在公众号中回复◉加群◉便可加入咱们的云原生交流群,和孙宏亮、张馆长、阳明等大佬一块儿探讨云原生技术

相关文章
相关标签/搜索