> 原文连接:Kubernetes 控制器的进化之旅缓存
我是一堆 Kubernetes 控制器。微信
你可能会疑惑为何是一堆,由于我不是一我的,我只是众多控制器中的一员,你也能够把我当作是众多控制器的集合。个人职责就是监控集群内资源的实际状态,一旦发现其与指望的状态不相符,就采起行动使其符合指望状态。网络
想当初,Kubernetes 老大哥创造我时,只是打算让我用控制循环简单维护下资源的状态。但我后来的发展,远远超出了他的想象。app
所谓控制循环就是一个用来调节系统状态的周期性操做,在 Kubernetes 中也叫调谐循环(Reconcile Loop)。个人手下控制着不少种不一样类型的资源,好比 Pod,Deployment,Service 等等。就拿 Deployment
来讲吧,个人控制循环主要分为三步:运维
API Server
中获取到全部属于该 Deployment 的 Pod,而后统计一下它们的数量,即它们的实际状态。Replicas
字段,看看指望状态是多少个 Pod。然而好景不长,我收到了 Kubernetes 掌门人(看大门的) API Server
的抱怨:“你访问个人次数太频繁了,很是消耗个人资源,我连上厕所的时间都没有了!”工具
我仔细一想,当前的控制循环模式确实有这个缺陷——访问 API Server
的次数太频繁了,容易被老大反感。oop
因此我决定,找一个小弟。post
此次我招的小弟叫 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,事情就这么愉快地决定了。
本觉得此次我会获得掌门人的夸奖,可没过几天安稳日子,它又来找我诉苦了:“兄弟,虽然你减轻了个人精神压力,但个人财力有限啊,若是每一个控制器都招一个小弟,那我得多发多少人的工资啊,你想一想办法。”
通过和其余控制器的讨论,咱们决定这么作:全部控制器联合起来做为一个总体来分配 Informer
,针对每一个(受多个控制器管理的)资源招一个 Informer 小弟,咱们称之为 SharedInformer
。大家能够理解为共享 Informer,由于有不少资源是受多个控制器管理的,好比 Pod 同时受 Deployment
和 StatefulSet
管理。这样当多个控制器同时想查 Pod 的状态时,只须要访问一个 Informer 就好了。
但这又引来了新的问题,SharedInformer
没法同时给多个控制器提供信息,这就须要每一个控制器本身排队和重试。
为了配合控制器更好地实现排队和重试,SharedInformer
搞了一个 Delta FIFO Queue
(增量先进先出队列),每当资源被修改时,它的助手 Reflector
就会收到事件通知,并将对应的事件放入 Delta FIFO Queue
中。与此同时,SharedInformer
会不断从 Delta FIFO Queue
中读取事件,而后更新本地缓存的状态。
这还不行,SharedInformer
除了更新本地缓存以外,还要想办法将数据同步给各个控制器,为了解决这个问题,它又搞了个工做队列(Workqueue),一旦有资源被添加、修改或删除,就会将相应的事件加入到工做队列中。全部的控制器排队进行读取,一旦某个控制器发现这个事件与本身相关,就执行相应的操做。若是操做失败,就将该事件放回队列,等下次排到本身再试一次。若是操做成功,就将该事件从队列中删除。(图片来自网络)
如今这个工做模式获得了你们的一致好评。虽然单个 SharedInformer
的工做量增长了,但 Informer 的数量大大减小了,老大能够把省下来的资金拿出一小部分给 SharedInformer
涨工资啊,这样你们都很开心。
全民 Kubernetes 时代到了。
随着容器及其编排技术的普及,使用 Kubernetes 的用户大量增加,用户已经不知足 Kubernetes 自带的那些资源(Pod,Node,Service)了,你们都但愿能根据具体的业务建立特定的资源,而且对这些资源的状态维护还要遵循上面所说的那一套控制循环机制。
幸亏最近掌门人作了一次升级,新增了一个插件叫 CRD(Custom Resource Definition)
,建立一个全新的资源实例,只须要通过如下两步:
固然,中间还要加入一些代码让 Kubernetes 认识自定义资源的各类参数。
到这一步就基本上完成了自定义资源的建立,但 Kubernetes 并不知道该资源所对应的业务逻辑,好比你的自定义资源是宿主机,那么对应的业务逻辑就是建立一台真正的宿主机出来。那么怎样实现它的业务逻辑呢?
Controller Manager
见多识广,说:”这里的每一个控制器都是个人一部分,当初创造大家是由于大家都属于通用的控制器,你们都能用得上。而自定义资源须要根据具体的业务来实现,咱们不可能知道每一个用户的具体业务是啥,本身一拍脑壳想出来的自定义资源,用户也不必定用得上。咱们可让用户本身编写自定义控制器,大家把以前使用的控制循环和 Informer 这些编码模式总结一下,而后提供给用户,让他们按照一样的方法编写本身的控制器。“
Deployment 控制器一惊,要把本身的秘密告诉别人?那别人把本身取代了咋办?赶紧问道:”那未来我岂不是很危险,没有存在的余地了?“
Controller Manager
赶紧解释道:”不用担忧,虽然用户能够编写自定义控制器,但不管他们玩出什么花样,只要他们的业务跑在 Kubernetes 平台上,就免不了要跑容器,最后仍是会来求大家帮忙的,你要知道,控制器是能够层层递进的,他们只不过是在你外面套了一层,最后仍是要回到你这里,请求你帮忙控制 Pod。“
这下你们都不慌了,决定就把自定义控制器这件事情交给用户本身去处理,将选择权留给用户。
用户自从得到了编写自定义控制器的权力以后,很是开心,有的用户(CoreOS)为了方便你们控制有状态应用,开发出了一种特定的控制器模型叫 Operator
,并开始在社区内推广,获得了你们的一致好评。不能否认,Operator
这种模式是很聪明的,它把须要特定领域知识的应用单独写一个 Operator 控制器,将这种应用特定的操做知识编写到软件中,使其能够利用 Kubernetes 强大的抽象能力,达到正确运行和管理应用的目的。
以 ETCD Operator
为例,假如你想手动扩展一个 ETCD 集群,通常的作法是:
而 ETCD Operator 将这些特定于 etcd 的操做手法编写到了它的控制循环中,你只须要经过修改自定义资源声明集群指望的成员数量,剩下的事情交给 Operator 就行了。(图片来自网络)
本觉得这是一个皆大欢喜的方案,但没过多久,就有开发 Operator 的小哥来抱怨了:”咱们有不少开发的小伙伴都是不懂运维那一套的,什么高可用、容灾根本不懂啊,如今让咱们将运维的操做知识编写到软件中,臣妾作不到啊。。“
这确实是个问题,这样一来就把开发和运维的工做都塞到了开发手里,既懂开发又懂运维的可很少啊,为了照顾你们,还得继续想办法把开发和运维的工做拆分开来。
这时候阿里和微软发力了,他们联合发布了一个开放应用模型,叫 Open Application Model (OAM)。这个模型就是为了解决上面提到的问题,将开发和运维的职责解耦,不一样的角色履行不一样的职责,并造成一个统一的规范,以下图所示(图片来自网络):
这个规范告诉咱们:
其中每个团队负责的事情都用对应的 CRD 来配置。
这样一来,开发和运维人员的职责就被区分开来了,简化了应用的组合和运维。它将应用的配置和运维特征(如自动伸缩、流量监控)进行解耦,而后经过建模构成一个总体,避免了 Operator 这种模型带来的大量冗余。
自从用上了这个模型以后,运维和开发小哥表示如今他们的关系很融洽,没事还能一块儿出去喝两杯。
扫一扫下面的二维码关注微信公众号,在公众号中回复◉加群◉便可加入咱们的云原生交流群,和孙宏亮、张馆长、阳明等大佬一块儿探讨云原生技术