kuberetes controller模块代码学习

来源:互联网 发布:金牌淘宝客服 编辑:程序博客网 时间:2024/06/03 22:46

2 ControllerManager模块:

    ControllerManager是Kubernetes集群中的控制管理模块,主要负责集群内部的Node,Pod,Service,Namespace,Deployment, Job, Daemonset等资源的管理。每个资源管理多对应有一个Controller负责,如Pod副本由Replicationcontroller和ReplicaSetController负责管理。接下来将详细介绍各个Controller的控制流程。


2.1 Replicationcontroller (RC)

   RC(由于Replication Controller(控制器) 和replicationcontroller(kubernetes中的一种资源对象)是2个不同东西,所有接下来所有用RC简写代表控制器非简写的代表其所控制的资源对象,接下来的其他模块也类似)的主要功能是确保Kubernetes集群中每个replicationcontroller资源所关联的Pod的副本数与replicationcontroller中所定义副本数保持一致,当集群中的Pod副本数小于replicationcontroller中所定义副本数RC就会创建一些新的Pod副本,同样当Pod副本数大于replicationcontroller中所定义副本数RC就会销毁多余的Pod副本数。RC中维护着一个replicationcontroller key(namespace+name)的queue,当pod,replicationcontroller有变化(创建,删除或者更新)时其对应的replicationcontroller的key将加入queue中,然后后台有线程不断同步key所对应的replicationcontroller。如下几种情况都会触发replicationcontroller key加入queue。

  1. addPod:当发现有Pod被创建时,该Pod对应的replicationcontroller的key将加入queue;

  2. deletePod:当发现有Pod被删除时,该Pod对应的replicationcontroller的key将加入queue;

  3. updatePod: 如果curPod与oldPod相比labels有变化则将curPod与oldPod所对应的replicationcontrolle key都加入queue,否则只将curPod所对应的replicationcontrolle key加入queue;

  4. replicationcontroller 的add,delete,update也会将其key加入queue;


  RC中默认会开5个协程(这个数量是可以配置的在controllermanager模块启动的时候)同时处理queue中key,但是为了防止replicationcontrolle同步时要创建或者删除的副本数过大(如创建或者删除时对应的replicationcontrolle所指定的副本数比较大,或者replicationcontrolle在更新的时候新副本数与老副本数差值比较大)而使某一个replicationcontrolle占用过多的资源,RC会控制每一次同步时最大的Pod副本创建和删除数量(这个临界值现在是500)。RC的具体同步流程如下:

  1. 从queue中取出一个key并从replicationcontrolle cach中找到其对应的replicationcontrolle;

  2. 判断EnableGarbageCollector,默认为true(同样可以在controllermanager模块启动的时候设置),如果EnableGarbageCollector为true则转3),否则从pod cach中取出该replicationcontrolle所有的pods加入到filteredPods数组中。转5);

  3. 从pod cach中取出所有replicationcontrolle所在namespace的pods,并将这些pods分为如下三组:a) matchesAndControlled组,pod的labels与replicationcontrolle的labels匹配且pod的创建者也是该replicationcontrolle;b)matchesNeedsController组,pod的labels与replicationcontrolle的labels匹配但不是controller创建的。c)controlledDoesNotMatch组,pod的labels与replicationcontrolle的labels不匹配但创建者是该replicationcontrolle;转4);

  4. 将matchesNeedsController组的pods的创建者更新为该replicationcontrolle,并同步到apiserver,将controlledDoesNotMatch组的pods删除对该replicationcontrolle的创建引用。将matchesNeedsController和matchesAndControlled组的pod加入到filteredPods数组中。转5);

  5. 判断该replicationcontrolle还在创建或者删除上次同步建立的Pod副本同步任务中,如果不是则转6);(如上次同步要创建100个pod副本现在还只创建了一部分,其它的还在创建中,毕竟创建pod是要时间的)

  6. 如果当前replicationcontrolle所控制的pod数大于其定义的副本数,则删除多余的pod副本数,负责创建不足的副本数;转7);(在删除多余的副本数时优先删除的顺序是1)没有signed到某个node的pod优先删除;2)pod状态处于pending或者unknow的优先删除;3)没有ready的pod优先删除;4)pod ready比较晚的优先删除;5)pod中的Container重启次数越多的优先删除;6)pod创建时间越晚的优先删除;

  7. 更新replicationcontroll的Replicas,FullyLabeledReplicas,ReadyReplicas运行状态。


图 2.1 replicatecontroller创建及同步用例图

图 2.2 replicatecontroller同步流程图

2.2 Node Controller

    Node Controller主要负责管理和监控集群中所有Node的健康状态,Node上kubelet模块在启动的时候会向Apiserver注册节点信息,之后会定期向Apiserver汇报节点信息。Node Controller模块会每隔默认5秒检查一边所有Node的健康状态,但发现某一个Node超过默认40秒没有向Apiserver同步信息了,则将改Node的Ready状态改为unknow,同时将该Node上的所有Pod的Ready状态改为false(但是运行状态还是Running),当Node超过默认5分钟没有同步信息则该Node上的Pod将被删除。Node Controller运行之后主要做了如下四件事:

  1. 每隔5秒对所有Node的状态进行一次同步;

  2. 删除那些失效的Node上的那些没有设置优雅删除时间Pod;

  3. 删除那些失效的Node上的那些设置了优雅删除时间Pod;

  4. 删除那些Pod所属Node不存在的Pod(孤儿Pod),每30秒遍历一遍所有的Pod;

   上面2),3)里面对Pod的删除是有速率控制的,Node Controller为每一个Zone(根据Node的Zone label来判断每个Node属于那个Zone)维护一个删除速率控制的队列。每一个Zone的健康状态分为:部分中断(至少有2个Node不正常且不正常Node所占的比例超过55%),完全中断(Zone中所有的Node全部不正常),正常(其他)三种。 删除除正常Zone里面Pod的速率默认是0.1个/s,删除完全中断zone里面Pod的速率是0.1个/s(但是当所有zone的状态多是完全中断时则会把速率设为0),删除部分中断zone里面的Pod的数率则要根据zone里面Node的个数来设置,如果node个数大于50个则为0.01/s,否则设为0。上面4件事中最主要的还是对Node状态的同步,而其同步流程如下:

  1. 从Apiserver拿所有Node的信息包括Ready的以及非Ready的;

  2. 用拿到的所有Node信息和本地之前保存的Node信息来判断那些Node是新加的那些是被删除的(如果Controller刚启动的第一次同步由于之前就没有保存,那所有Node多属于新加);

  3. 遍历所有新加的Node;

           c.1: 将新加的Node加入本地缓存用于下次同步步骤b)用;

           c.2: 如果该获取Node的zonekey,如果是一个新的zone,则为该zone新建一个有速率    控制的Pod删除queue;

           c.3:如果之前Node已经加入了Pod删除队列则将其移除;

  1. 遍历所有删除的Node,将Node加入其对应的zone的Pod删除队列里, 并将该Node从本地缓存中删除用于下次同步步骤b);

  2. 遍历步骤a)中拿到的所有Node;

           e.1: 如果Node有超过40秒没有心跳包怎将该节点标为unknow状态,并将跑在其上的    Pod的Ready状态标为false(但其statue状态还是running,可以认为该Pod还活着只是不健康了)。

           e.2: 如果Node有超过5分钟没有心跳包则将该Node上的Pod加入对应zone的待删除队列。

  1. 处理集群的断开情况,并根据断开情况设置每个zone中Pod删除速率;

           f.1 如果所有zone的状态多是完全中断,则将所有zone的Pod删除速率设为0。

           f.2 如果上一条不成立则各个zone的删除速率则根据当前zone的状态已经zone中node的数量来设置。如果zone的状态是正常或者完全中断则将其Pod的删除速率设为0.1/s,如果zone的状态为部分中断且zone中的Node数超过50个则将其Pod的删除速率设为0.01/s,如果个数小于50的小zone则将速度设为0。


   从上面的流程可以总结为Node Controller开了三个屠宰场,一个是对那些Pod的删除为立即删除的,一个是对那些Pod的删除为延后执行的(有点像死缓),一个是那些孤儿Pod(所在的Node已经不存在的Pod)每30秒检测一边所有的Pod。其次还有一个检查官每5秒检测一边所有的Node,将那些超时未相应的Node加入删除队列(上面说的第1和2个屠宰场),但是加入屠宰厂的Pod也还是可以赦免的当在其被杀之前所在的Node又好了的时候。其次屠宰场里面杀Pod也是有速率控制的具体见上面的流程。

           

图 2.3 Node同步示意图


2.3 Endpoint Controller

   Endpoint对象并不是由用户所创建的,而是Endpoint Controller根据service,以及pod对象的变化动态创建或者删除的。Pod的生命周期管理是由replicationcontroller来管理,同样Pod的对外访问服务则由endpointcontroller来管理。Pod和Service的变化都会触发Endpoint的同步,一个Pod可能对外提供多种服务所有它可能被多个service所关联。Endpoint Controller只是负责当pod或者service有变化时同步endpoint对象(创建,删除或者更新endpoint),而使用endpoint对象的地方则是在Node节点上的kube-proxy模块。kube-proxy模块通过从apiserver拉取service和endpoint列表,对集群内或者外网对service的访问进行代理访问及负载均衡(详情可以参考后面的proxy模块)。由于同步是以service对象为单位进行的,Endpoint Controller有一个service key的queue,如下是触发endpoint同步的触发条件:

  1. addPod: 所有关联(service的labels匹配该Pod)该Pod的services的key(由service的namespace和name组成的字符串)加入到queue中;

  2. updatePod:如果更新之后的Pod和更新之前相比label有变化或者hostname和Subdomain有变化则会把所有关联更新前Pod的services key和所有关联更新后的Pod的services key加入queue,否则只加关联更新后的Pod的service加入queue;

  3. deletePod:跟addPod一样将所有关联的services的key加入queue;

  4. addService:将新加的service的key加入queue;

  5. updateService:将更新之后的service的key加入queue;

  6. deleteService:将删除的service的key加入queue;

   

   除了上面几个触发endpoint同步的条件之外,endpoint controller会在其启动5分钟之后向apiserver查询所有endpoint对象并将其对应的key(因为endpoint和service是一一对应的name和namespace也和service相同所以它的key和service也是一样的)加入queue。对于这次的整体同步官方给的注释是说endpoint controller重启之后可能存在一下endpoint对象但不存在与之对应的service,所以这些endpoint对象需要被删除。

   有了上面这些触发同步的条件之后,endpoint controller同样会开默认5个协程来处理queue的key,具体的处理流程如下:

  1. 从queue中取出一个key,并根据key从本地缓存中找到其对应的service对象;

  2. 如果1)中key找不到其对应的service说明该service已经被删除,就向apiserver删除同名的endpoint对象结束对当前key的同步,否则转3);

  3. 从本地缓存中找出所有与该service关联的Pods;

  4. 遍历所有的Pods;

  5. 遍历service对外提供的访问端口列表;

  6. 根据4)和5)的2层遍历然后整合得到如下地址和端口的映射表;如下该endpoint对外提供的访问地址有(10.10.1.1:8675,10.10.1.1:309, 10.10.2.2:8675, 10.10.2.2:309, 10.10.3.3:93, 10.10.3.3:76)。

           [

              {

                  Addresses: [{"ip": "10.10.1.1"}, {"ip": "10.10.2.2"}],

       Ports: [{"name": "a", "port": 8675}, {"name": "b", "port": 309}]

  },

  {

      Addresses: [{"ip": "10.10.3.3"}],

       Ports: [{"name": "a", "port": 93}, {"name": "b", "port": 76}]

configuration  }

          ]

  1. 如果之前该endpoint对象不存在则创建endpoint,如果已经存在则更新endpoint为上面得到的地址和端口映射关系;


   从上面的流程来看其实Endpoint Controller工作原理还是挺简单的,就是不断监听Pod和Service的变化然后同步相应Endpoint对象里面的地址映射关系。

2.4 ReplicaSet Controller(RS)

   和RC功能基本差不多,RC通过监听replicatecontroller和Pod的对象的变化然后触发同步保证用户定义的replicatecontroller对象里面副本数与集群运行的Pod副本数保持一致。同样RS通过监听replicaset和Pod对象的变化然后触发同步保证replicaset里面定义的副本数和集群里运行的Pod副本数保存一致。但是不同点是replicatecontroller对象一般是由用户创建的,而replicaset对象则是有deployment controller所管理和创建,详情可见deployment controller的介绍。由于和RS和RC功能差不多只是名字不同,所以这里就不再介绍了详情可见上面RC模块的介绍。

2.5 Deployment Controller(DC)

   Deployment可以看作是对之前replicatecontroller的一次升级,解决了一些replicatecontroller所不能完成的一些功能,如升级,回滚,部署进度查看等。其典型部署场景有如下(引用自kubernetes权威指南):

  1. 创建一个Deployment对象来生成对应的ReplicaSet并完成Pod副本的创建过程。

  2. 检查Deployment的状态开看部署动作是否完成(Pod副本数量是否达到预期的值)。

  3. 更新Deloyment以创建新的Pod(比如镜像升级)。

  4. 如果当前Deployment不稳定,则回滚到一个早先的Deployment版本。

  5. 挂起或者恢复一个Deployment。

   

   跟很多其它模块同步流程一样DC模块内部也维护了一个Deployment对象key(namespace+name)的待同步Deployment对象queue,同样触发key进入queue的条件如下:

  1. 有Deployment对象的add,delete及update:将对应的Deployment对象的key加入queue;

  2. addReplicaset:将关联该replicaset的deployment的key加入queue;

  3. updateReplicaset:将更新之后的replicaset所属deployment key加入queue,同时如果更新之后的Replicaset和更新之前比label或者Pod创建模板有变化将更新前Replicaset所属deployment key加入queue;

  4. deleteReplicaset:将所删除的replicaset所属deployment key加入queue;

  5. addPod:将新加Pod所属的deployment的key加入queue;

  6. updatePod:将更新之后Pod所属deployment的key加入queue,同时如果与更新前的Pod比有变化将更新前Pod的所属deployment的key加入queue;

  7. deletePod:将删除Pod所属的deployment的key加入queue;

   

   注意:上面步骤里的找replicaset所属deployment和pod所属deployment的方法是遍历所有deployment如果其label与该replicaset和pod的label匹配即找到,但是可能找到多个(应该尽量设置不同label是匹配的deployment是唯一的)。如果找到多个deployment那么就选一个deployment的selector更新最近的。

   有了上面这个queue以及不断会有条件触发deployment的key加入queue,DC模块也会默认开5个协程来处理queue里面的key对应的deployment对象使其保持同步。deployment的同步流程如下:

  1. 从queue中取出一个key,并根据该key从deployment的本地缓存中找到其对应的deployment对象;

  2. 如果该deployment定义的selector为空则返回(如果selector为空则会匹配所有pod)。

  3. 如果该deployment被pause了,则不会创建与deployment最新匹配第rs(replicaset)而是会将最近一个创建第rs慢慢scale(up或者down)到deployment制定到副本数然后返回。

  4. 如果deployment被设置了回滚版本号(如果设置到回滚版本号为0这默认为上一个最新版本)如果找到了指定回滚到版本则更新deployment的template到该rs的template并同时清楚deployment的回滚标记后返回。

  5. 如果deployment处于scale中则处理逻辑和3)是一样的。(也就是说如果deployment还在scaling的时候deployment有更新,也会先等scaling完成才会创建新的rs);

  6. 如果非如上状态且deployment被配置成Recreate模式则会先将之前版本的Pods删除完之后再创建最新的rs,如果被配置成RolingUpdate模式则会创建与当前deployment匹配的rs(如果该rs已经存在则不需要创建)然后慢慢增加其副本数和减少老rs的副本数来带到部署。(在设置来一个初始副本数之后下次同步将进入如上的步骤5,直到最新的rs副本数与deployment指定的副本数相同该同步才算完成)。


    从上面的deployment的同步过程来看可知,deployment支持2种部署模式Recreate和RolingUpdate。其次当最新部署的deployment有问题的时候也可以回滚到之前指定的版本(deployment可以指定最多保存多少个历史版本如果不指定则历史版本会一直保存,保存来多少历史版本就是通过保存rs来实现的每个rs多有一个版本号在annotations里)。我们知道rs和rc一样多是通过label去关联其各自的pod的,那各个rs是如何区分各个版本的pod呢。kubernetes在创建rs的时候会根据其template计算一个hash值然后将其加入到自己的template label和selector里,这样不同rs创建的pod的label也会有其所属rs的hash的label。

图 2.4 deployment与rs的关系


图 2.5 deployment一次sync流程

  如上图只是一次sync过程而要sync到deployment的最终状态可能需要经过多次sync不断调整新rs副本个数和old rs副本个数来逐步从老版本升级到新版本(如果设置的部署模式为rollingupdate)。


2.6 Persistent Volume Controller

     在Pod的定义中可以声明本Pod所用到一些Volume(如:可以映射Node的一些本地目录到Pod,但是这需要保证运行该Pod的Node物理机上存在该目录),多数情况下这些存储资源是独立于k8s集群的(不随Pod的创建或者删除,以及Node的添加或者删除而改变)不属于任何Pod或者Node。为了管理这些网络存储资源并且给用户提供一种透明及使用简单的用户体验,k8s实现了一个Persistent Volume Controller的模块来管理。为了达到这个目标在k8s中定义了Persistent Volume(PV), Persistent Volume Claim(PVC)这2种资源(如下为nfs存储的用法,其中10.19.132.196为nfs服务器)。


      从名字以及用法上可以认为PV对应一个网络存储块,PVC为对网络存储资源的占用,当创建的PVC从所有PVs中找到了一个满足其需求的PV时则该PV和PVC绑定成功。kubelet模块在创建包含PVC Volume的Pod时,kubelet会先将nfs mount到运行pod的node物理机上然后再将其映射到使用该volume的container里。这里整个使用流程主要涉及到ControllerManager和Kubelet这2个模块,接下来主要介绍ControllerManager里的详细工作流程,而Kubelet的工作流程将在Kubelet模块里介绍。

    Persistent Volume Controller通过分别向APIServer监听PV和PVC的变化,然后分别调用PV以及PVC的同步逻辑。

1)有新的PV被创建,触发该PV的同步;

2)有PV被更新,触发更新之后PV的同步;

3)有PV被删除,如果该PV之前没有绑定过任何PVC则什么也不做负责,同步之前绑定的PVC;

4)有新的PVC被创建,触发该PVC的同步;

5)有PVC被更新,触发更新之后PVC的同步;

6)有PVC被删除,如果该PVC之前有绑定过PV则触发对该PV的同步;


   如上可知Persistent Volume Controller的主要工作就是对PV和PVC的同步,如下将分别详细介绍PV及PVC的同步流程。

1 PV的同步流程:

1)如果PVC没有绑定过任何PVC则更新PV的状态为Available然后返回,否则转2.1);

   2.1)如果之前绑定的PVC已经不存在了且当前PV的状态不为Failed则将PV的状态标为Released, 转3);

   2.2)  如果之前绑定的PVC还存在但是该PVC没有绑定任何PV时将该PVC加入到PVC同步队列里然后返回;

   2.3)如果之前绑定的PVC还存在且其绑定的PV也是该PV时将PV的状态更新为Bound,然后返回;

   2.4)如果之前绑定的PVC还存在但是该PVC所绑定的PV是别的PV时;

       2.4.1) 如果PV支持动态配置且回收策略为Delete且当前状态不为Failed则将状态更新为Released,转3);

       2.4.2) 如果PV不支持动态配置则将该PV解除对任何PVC的绑定,并将状态更新为Available然后返回;

3)  判断PV的回收策略(如下策略中并不是所有的网络存储多支持,如nfs共享并不支持Delete策略如果将nfs的PV回收策略定义为Delete则会回收失败)

   3.1)如果回收策略为Retain则返回什么也不做;

   3.2)如果回收策略为Recycle则删除之前绑定目录里的东西,然后解除PV的绑定并将状态更新为Available;

   3.3)如果回收策略为Delete则删除之前绑定的目录,并将PV删除;

   3.4)如非如上3种策略之一则将PV的状态更新为Failed;


图 2.6 PV同步流程图



                 

2 PVC的同步流程:

1)当前要同步的PVC没有绑定完成;

1.1)当前PVC是否声明要绑定到特定的PV(VolumeName是否为空),如果没有指定则转1.2负责转1.3;

1.2)当前PVC没有什么要绑定到特定的PV,则从所有符合PVC绑定条件的PV中找一个如果有多个满足的PV则选择PV定义容量与PVC申请容量最接近的PV;如果有找到这样的PV则转1.2.1,否则转1.2.2;

1.2.1)上面有找到满足条件的PV,则将该PV和PVC进行绑定,返回;

1.2.2)没有找到满足条件的PV,如果PVC有定义StorageClassAnnotation,则根据StorageClass从支持的存储插件里面找对于的插件然后创建相应的PV以及网络存储目录并将该创建的PV和PVC进行绑定,如果没有定义StorageClassAnnotation或者找不到对于的插件则将PVC状态更新为Pending,返回;

1.3)PVC指定要绑定的PV不存在则将PVC的状态更新为Pending然后返回,如果要绑定的PV存在且该PV没有进行绑定则将其与该PVC进行绑定,如果要绑定的PV以及被其它PVC绑定了则将当前同步的PVC状态更新为Pending,返回;

2)当前要同步的PVC已经绑定完成

2.1)如果PVC绑定的PV已经不存在了或者该PV被绑定到了其它PVC则将PVC的状态更新为Lost然后返回,否则将PVC与其PV进行绑定;

图 2.7 PVC同步流程图



2.7 Tokens Controller

     一般情况下Kubernetes的Apiserver对外只提供https的访问,也就是说所有外部对Apiserver的访问多需要进过https的安全认证,Token Controller 主要是为了解决Kubernetes中Pod中需要访问Apiserver的情况。很显然如果将认证信息写在Pod的创建文件中不是很安全,所以Kubernetes添加了ServiceAccount和Secret这2个对象来控制。Tokens Controller的主要功能就是对这2个对象对同步。Tokens Controller里维护了这2个对象对同步队列当监听到Apiserver上有ServiceAccount和Secret的添加,修改和删除时多会将其加入同步队列里,接下来将分别详细介绍ServiceAccount和Secret的同步流程:

1 ServiceAccount同步流程:

1)从ServiceAccount同步队列里取出一个需要同步的ServiceAccount key,转2);

2)查找该key所对应的ServiceAccount对象如果该对象不存在了则转3),否则转4);

3)  取出该删除ServiceAccount所在namespace下所有Secret对象,遍历所有Secret对象如果Secret的type为kuberntes.io/service-account-token,且annotations所指定的kuberntes.io/service-account.name和kuberntes.io/service-account.uid和该ServiceAccount的name和uid相等则将给Secret对象删除;返回;

4)key所对应的ServiceAccount对象存在:

 4.1)找出所有该ServiceAccount namespace下的Secret的type为kuberntes.io/ service-account-token, 且annotations所指定的kuberntes.io/service-account.name和kuberntes.io/service-account.uid和该ServiceAccount的name和uid相等的所有Secrets。如果该ServiceAccount Secrets里面所引用的Secret name在刚刚找到的Secrets里有则返回(这样做主要是为了避免短路),否则转4.2);

 4.2) 比较Tokens Controller 缓存里的ServiceAccount对象与Apiserver里最新的ServiceAccount对象是否一样,如果不一样则返回(忽略本次更新),否则转4.3);

 4.3)创建一个Secret对象,并用controllermanager启动时service-account-private-key-file所指定的私有key签名生成一个token赋值给刚创建的Secret对象的data.token, root-ca-files所指定的CA根证书赋值给data.”ca.crt”, namespace赋值给data.namespace,然后创建该Secret对象,并更新ServiceAccount对象引用改创建的Secret对象;返回;


2 Secret同步流程:

1)从Secret同步队列里取出要同步的Secret key,转2);

2)  查找该key所对应的Secret对象是否存在,如果不存在转3),否则转4);

3) Secret对象不存在了,找到该Secret对象所属的ServiceAccount对象,并将ServiceAccount对象的Secrets对该删除对Secret对象的引用移除;返回;

4)Secret对象还存在:

  4.1)找到Secret对象所映射的ServiceAccount对象如果该ServiceAccount对象不存在了转4.2), 否则转4.3);

  4.2)删除Secret对象返回;

  4.3)如果Secret对象的data.token,data.”ca.crt”以及data.namespace需要更新则更新。返回;

2.8 Service Account Controller

   Service Account Controller的主要功能是分别在每个Namespace下维护一个name为default的ServiceAccount对象,Controller里面维护了一个Namespace的queue,该queue的更新流程如下:

1)有ServiceAccount对象被删除,则将该ServiceAccount的namespace加入queue;

2)有namespace被添加将该namespace加入queue;

3)有namespace被更新,将更新的namespace加入queue;

  有了上面这个namespace的同步queue,后台会有一个线程不断的从queue中取出namespace然后进行同步,具体同步流程如下:

1)从queue中取出一个namespace转2);

2)如果该namespace的状态不为Active则返回,否则转3);

3)遍历所有需要在每个namespace下维护的ServiceAccount(目前只需维护default)对象在该namespace下是否存在,如果存在则返回,否则在该namespace下创建该ServiceAccount;返回;

  从上面的流程来看Service Account Controller是比较简单的,只是简单的在每个namespace下维护一个default ServiceAccount。

2.9 Certificate Controller

  主要处理Kubelet创建的证书签名申请,Kubernetes通过CertificateSigningRequest数据结构来处理,Kubelet可以通过向Apiserver创建该对象来申请对证书对签名,Certificate Controller检测到之后会根据情况来对该请求进行相应。Certificate Controller维护了一个CertificateSigningRequest同步对队列,当检测到apiserver上有CertificateSigningRequest对象对创建,更新以及删除时会将相应的CertificateSigningRequest加入到同步队列,然后后台有一个独立的线程来处理队列中的请求。具体处理流程如下:

1)从同步队列里取出一个CertificateSigningRequest key并查找相应CertificateSigningRequest对象,如果对象不存在(已经被删除)则返回,否则转2);

2)如果该对象的status.conditions状态已经是拒绝或者核准则转4)否则转3);

3)如果对象spec.groups所定义的groups有属于Controller Manager启动时定义的自动核准组且对象的一些信息满足要求则将对象的状态标为核准然后转4)否则返回;

4)如果对象状态为核准则将其spec.request的证书签名请求进行签名(这里签名用的就是根证书及私钥由Controller Manager启动的时候传入)然后同步更新到APIServer,这样Kubelet就可以拿到经Controller Manager签发的证书了;

   Certificate Controller要能正常工作需要传入证书签发的根证书,私钥以及自动核准组传入参数分别为cluster-signing-cert-file,cluster-signing-key-file,insecure-experimental-approve-all-kubelet-csrs-for-group,而证书和私钥如果不传也可以分别放在/etc/kubernetes/ca/ca.pem和/etc/kubernetes/ca/ca.key位置。

2.10 Job Controller

  Job和RC,RS有些类似,不同的是RC和RS所维护的Pod是不退出的会一直Running并且会维护其Running的数量保持不变。而Job也是维护一定数量的Pod但是这些Pod是会退出的,而且只要正常退出Pod的数量满足Job的定义则该Job才算完成。Job主要用于一些一定时间就会完成的任务如编译等,而RC和RS主要用于一些长期运行的任务如Web服务等。Job Controller主要是不停检测系统中Job以及Job所关联的Pod的一些运行情况,动态的创建和删除一些Pod等。Job Controller中维护了一个需要同步的Job队列且后台会有多个协程来不断同步这些Job的状态,这个队列的更新逻辑如下:

1)检测到APIServer有Job被创建或者删除,该Job加入同步队列;

2)检测到有Job被更新,且该Job的状态不是完成,失败则将Job加入同步队列;

3)检测到有Pod被创建或者删除,且该Pod被关联到某一个Job,将该关联Job加入同步队列;

4)检测到有Pod被更新,且该更新后的Pod被关联到某一个Job,将该关联的Job加入同步队列;

   有了Job同步队列的更新,接下来就是对队列里需要同步的Job进行同步了,具体同步流程如下:

1)从Job同步队列里取出需要同步Job的Key(namespace+name),转2);

2)查找Key对应的Job对象,如果该Job对象不存在了则返回,否则转3);

3)查询该Job对象所关联的所有Pod,并计算这些Pod中有多少是Active的(Pod状态非成功或者失败且没有删除)有多少是Succeeded的(Pod的状态为成功)。转4);

4)看该Job是否已经结束(Job的状态非完成和失败),如果已经结束则返回,否则转5);

5)该Job是否已经超时(当前时间-Job开始时间>Job定义的activeDeadlineSeconds),如果已经超时转6)否则转7);

6)将当前Job所有关联的Active的Pod删除,并将Job的状态更新为失败。返回;

7)判断当前Job是否之前已经有一个正在Job关联的Pod的创建或者删除任务还没有结束(就是之前是否已经有Job同步处于8)中的任务没有结束),如果还没结束则返回,否则转8);

8)如果当前Job所关联的处于Active的Pod数量大于Job所定义的最大并行数则按照一定策略删除一些Pod使Active Pod的数量等于最大并行数,否则转9);

9)如果Job没有定义需要完成的数量(spec.completions)且还没有Pod完成则创建Job所定义的最大并行数的Pod,如果有Pod完成则不处理(就是说如果没有定义completions就以最大并行数为完成数)转10)。如果Job有定义完成数则创建min(completions-suceeded - active, parallelism-active)数量的Pod转10);

10)如果没有定义完成数量,succeed>0且active==0则将Job标记为完成,如果定义了完成数且succeed>定义的完成数则将Job标记为完成。返回;

图 2.8 Job同步流程图


2.11 Daemonsets Controller

    创建一个Daemonsets就会在Kubernetes集群的每一台符合条件的Node(如何判断是否符合后面会详细介绍)上创建一个Pod(仅一个)。Daemonsets主要用于需要在每个Node上多需要运行的守护进程,如:log收集,数据存储等,而Daemonsets Controller就是根据系统中创建的Daemonsets来维护运行在每个Node上的Pod。同样Daemonsets Controller里也维护了一个需要同步的Daemonset队列,队列的更新逻辑如下:

1)检测到APIServer上有Daemonset的创建或者删除将该Daemonset加入同步队列;

2)检测到有Daemonset的更新,将更新的Daemonset加入同步队列;

3)有Pod被创建或者删除,如果该Pod是被关联到某一个Daemonset的则将该Daemonset加入队列;

4)有Pod被更新,如果更新之后的Pod是被关联到某一个Daemonset的将该Daemonset加入队列,如果Pod更新前后的Labels有变化且更新前的Pod有关联到某一个Daemonset的将Daemonset加入队列;

5)有新的Node加入集群,遍历系统所有Daemonset如果该Daemonset需要在该Node上运行(如何判断是否需要将在后面介绍),则将该Daemonset加入同步队列;

6)有Node被更新,遍历系统所有Daemonsets,如果Daemonset可以在更新前的Node上运行不可以在更新后的Node上运行,或者可以在更新之后的Node上运行而不能在更新之前的Node上运行则将该Daemonset加入同步队列;

   在介绍了Daemonset队列更新逻辑之后接下来说一下如果判断一个Node是否需要运行Daemonset所定义的Pod的流程。

1)如果Daemonset定义的Pod创建模版里有指定NodeName,如果该NodeName等于要判断的node则转2)否则不需要运行返回。如果没有定义NodeName转2);

2)如果判断的Node的磁盘空间不足则不需要运行,否则转3);

3)判断在Node当前情况下是否可以将Daemonset模版所定义的Pod调度到该Node上(如CPU,内存,GPU是否足够,nodeselector是否满足等,详情可见Scheduler那一章里的Predicates判断)。如果通过了Predicates的判断则该Node需要运行,否则不需要运行;

图 2.9 判断一个Node是否需要运行某个Daemonset流程图


  有了Node是否需要运行Daemonset的方法以及Daemonset同步队列的更新逻辑,下面我们就可以介绍Daemonset的同步流程了。

1)从同步队列里取出一个需要同步Daemonset的Key,转2);

2)通过该Key查询对应Daemonset对象如果该对象以及不存在了则返回,否则转3);

3)如果Daemonset没有定义Selector或者为空则返回(如果没有定义就意味着所有Pod多属于该Daemonset,而这是不允许的所以直接返回),否则转4);

4)如果之前有该Daemonset在同步中或者Daemonset将被删除(DeletionTimestamp!=nil),转6),否则转5);

5)遍历每一个Node,判断Node是否需要运行该Daemonset,以及计算当前Node上所运行的Pod有多少属于当前该Daemonset;

    5.1)如果当前Node需要运行该Daemonset,但当前Node目前还没有运行则根据Daemonset的Pod创建模版创建一个Pod到该Node上运行;转6);

    5.2)如果当前Node需要运行该Daemonset,但当前Node目前运行的属于该Daemonset的Pod个数大于1,则删除后创建的Pod;转6);

    5.3)如果当前Node不能运行该Daemonset,但是当前Node有运行属于该Daemonset的Pod,则删除所有这些Pod;转6);

6)遍历所有Node及其上所运行属于该Daemonset的Pod,统计期望运行的Pod数,当前以及正常运行的Pod数,以及错误运行数(Node不需要运行但是当前又运行了),并将统计结果更新到APIServer;

图 2.10 Daemonset同步流程图


2.12 Namespace Controller


2.13 Pod GC Controller

   Pod GC Controller主要功能就是当系统中Terminated的Pod超过一定数量(可由Controller Manager启动的时候传入默认是12500)时删除多余的Terminated的Pod,删除一些Node已经不存在的孤儿Pod,以及删除一些还没有被调度的且Pod.DeletionTimestamp!=nil的Pod(这里只要Pod的状态不是Pending,Running以及Unknow的Pod多属于Terminated Pod)。具体回收流程如下(系统没隔20秒进行一次Pod回收):

1)当terminated-pod-gc-threshold所定义的数量大于0则转2)否则转3);

2)遍历系统中所有的Pod,找出所有Terminated Pod,如果Terminated Pod数大于terminated-pod-gc-threshold所定义的数量,则按创建时间依次删除多余的Terminated Pod,转3);

3)遍历系统中所有的Pod如果该Pod所运行的Node不存在了(该Node以及被删除而不是Node非Ready)则将该Pod删除,转4);

4)遍历系统中所有的Pod如果该Pod还没有被调度到某一个Node上运行且Pod.DeletionTimestamp != nil则将该Pod删除,返回;

2.14 Service Controller

   Servcie Controller 主要是监听Service的变化(创建,删除和更新),然后动态调整第三方Cloud的负载均衡。这里只会处理Service Type为LoadBalance的Service所以后面提到的Service是否需要LoadBalance的时候多是通过去判断Service的Type的。Service Controller里面维护了一个Servcie同步队列,以及一个Node变化监听的协程没隔100秒检测一下集群中的Node是否有变化如果有则更新Sevice的LoadBalance。所以接下来也是从这2个方面来详细介绍Service Controller的工作流程。

   首先介绍的是Service的同步流程,Service同步队列的更新逻辑如下:

1)检测到APIServer有Service的创建或者删除时将该Service对应的Key加入同步队列;

2)检测到有Service的更新时如果满足如下条件将更新后的Service加入到同步队列,a)更新前后Service一个需要LoadBalance一个不需要;b)更新前后Service多需要LoadBalance但是LoadBalancerSourceRanges不同;c)多需要LoadBalance且更新前后Service的端口有变化或者SessionAffinity有变化;d)多需要LoadBalance且Service的LoadBalancerIP有变化;e)多需要LoadBalance且Service的ExternalIP有变化;f)多需要LoadBalance且Service的Annotations有变化;g)多需要LoadBalance且Service的UID有变化;

   有来Service同步队列的更新逻辑,Service的同步流程如下:

1)从Service同步队列里取出一个需要同步Service的key,转2);

2)如果该Key所对应的Service不存在了则删除该Service所创建的第三方LoadBalance然后返回,如果该Key对应Service还存在转3);

3)如果该Service不需要LoadBalance且该Service有创建过LoadBalance则将该LoadBalance删除,如果Service需要LoadBalance则为该Service创建LoadBalance返回;

   

   接下来介绍NodeSync的流程如下:

1)获取所有当前集群中正常可调度的Node列表,如果该Node列表和上一次NodeSync所保存的列表相比没有变化则更新上次那些Servcie更新没有成功的Service返回,如果Node有变化转2);

2)遍历所有Services,如果Service需要LoadBalance,更新该Service所创建的LoadBalance的Host,返回;

2.15 Route Controller

   Route Controller的主要工作就是根据当前集群的Node情况更新第三方云提供商的Route Table。Route Controller会默认每10秒(也可以通过route-reconciliationperiod修改)会检测集群当前的Node列表,然后根据这个Node列表更新云服务商的Route Table,具体更新流程如下:

1)获取云服务商当前的Route Table,以及当前集群的Node列表,转2);

2)遍历获得的Node列表,如果Node的spec.PodCIDR为空则忽略当前Node接着处理下一个Node,查看当前Node是否已经在云服务商的Route Table中存在,如果存在转3),否则转4);

3)如果Node Condition列表里的NetworkUnavailable为true,则更新为false;转5);

4)在第三方云服务商的Route Table中创建一条路由;转5);

5)删除第三方云服务商中那些多余的路由(如有些Node已经被删除但是路由还存在);

2.16 Resource Quota Controller

   在Kubernetes集群上的应用各种各样,为了避免某些应用因为设计或者其它原因导致整个集群不稳定。Kubernetes提供了资源配额管理的功能,而这一功能是有APIServier的Admission Control(准入控制)以及Controller Manager里面的Resource Quota Controller这2个模块共同完成的, 如用户可以创建如下ResourceQuota来限制testnamespace下的资源使用情况(如下定义限制testnamespace下的Pod创建数不能超过2该,request cpu个数不超过1个等)。其中Controller Manager里面的Resource Quota Controller主要是负责当前这些资源已经使用情况的收集,而APIServier的Admission Control负责在APIServer端判断当前资源是否已经超出限额(如先在已经有2个pod在testnamespace下,下次有模块再在该Namespace下创建Pod时将会被拒绝)。

apiVersion: v1

kind: ResourceQuota

metadata:

 namespace: testnamespace

 name: compute-resources

spec:

 hard:

   pods: "2"

   requests.cpu: "1"

   requests.memory: 1Gi

   limits.cpu: "2"

   limits.memory: 2Gi


     Resource Quota Controller里有如下几个资源计算器:Pod, Service, ReplicationController,PersistentVolumeClaim,Secret,ConfigMap,它们分别统计各自的资源使用情况(如Pod资源计算器负责统计CPU,内存申请量,Pod数量等)。和之前介绍的其它Controller只有一个相应的同步队列相比,Resource Quota Controller维护量2个队列一个主队列和一个missingUsageQueue(优先队列)(之所以设计2个同步队列可能是因为资源统计比较慢,因为主队列会每隔5分钟会将所有的ResourceQuota对象从新同步,这样可能会阻塞一些有变化的ResourceQuota的更新)。了解了Resource Quota Controller的工作内容之后我们来详细介绍一下其工作流程。首先先介绍这2个同步队列的更新流程:

1)检测到有ResourceQuota对象被创建,如果Spec.Hard和status.Hard比有修改或者有定义的资源约束但是没有使用则将该ResourceQuota对象加入优先队列,否则加入主队列;

2)检测到有ResourceQuota对象被删除,将该ResourceQuota对象加入主队列;

3)检测到有ResourceQuota对象更新,如果定义的资源约束没有变化返回,否则同1);

4)默认每隔5分钟(可以在ControllerManager启动时通过resource-quota-sync-period参数修改)将当前系统中所有的ResourceQuota对象加入主队列。

   Resource Quota Controller默认会给每个同步队列分别开5个协程处理队列中需要同步的ResourceQuota对象,虽然2个队列的更新逻辑不同,但是同步逻辑是一样的。同步流程如下:

1)从队列中取出需要同步的ResourceQuota对象的Key,转2);

2)根据Key找对应的ResourceQuota对象是否存在如果不存在了则返回,否则转3);

3)调用资源计算器统计集群中ResourceQuota对象所在Namespace下的资源使用情况(如Pod个数,CPU和内存申请量等,但只使用Spec.Hard所定义的),转4);

4)如果和之前比有变化则更新到APIServer保存,返回;


原创粉丝点击