• Kubernetes控制平面组件:Controller-Manager控制器管理



    前文介绍的kube-scheduler,要watch pod nodeName变化,若没有nodeName的pod,则需要调度,然后获取node list,根据predicate plugin、priority plugin的筛选得到最后的候选node列表,然后选择list index为0的分数最高的node,reserve这个node相关资源,然后 pod bind node(写nodeName字段),最后把pod yaml回写到apiserver中。这是一个controller loop, kube-scheduler其实也是一个特殊的kube-controller-manager

    apiserver是一个声明式的系统,就是你给我一个应用的期望状态,我根据这个期望去做一些配置,让这个应用最终达到你的期望状态,这个就是kube-controller-manager实现的。是k8s的大脑。

    一、工作流程

    控制器=控制器定义 + 被控制对象的模版(podTemplate)
    控制器定义: 是为了reconcil loop实现达到期望状态(控制器核心原理)

    1、code-generator

    针对任何的api对象,都可以通过code-generator来生成。它可以生成Deepcopy,Conversion,Clientset等。Clientset用来定义我如何访问这个api对象,比如create,delete,get,list,watch等。
    code-generate官方文档

    2、流程

    流程图:
    在这里插入图片描述

    针对kube-controller-manager,code-generator生成了Informer和Lister。这两个统称为Controller Interface
    去apiserver获取任何一个对象的时候,有两种方式,一种是get,一种是watch。针对这两种方式,Controller Interface就分为了:

    • informer:有新的事件推送给用户,是一个消息通知接口。它有三种event。
      🔥 addFunc:一个对象第一次出现的时候
      🔥 deleteFunc:删除对象
      🔥 updateFunc:对象存在要变更属性
    • lister:获取当前的状态

    三种event要去注册EventHandler,event是一个完整的对象,大部分的控制器会去拿到这个对象的KeyFunc(对象的namespace+name),然后把这个key通过RateLimitinglnterface放到队列里面。

    (1)为什么放到队列里的不是对象的完整数据

    如果有一个对象频繁变更,如果把对象的完整信息放到队列里,首先队列需要的内存空间会很大,其次假如对象要变更10次,那么worker也要变更10次。而使用对象的key(例如:pod的key就是namespace_podname string),不管有多少次变更event,推送到队列里的只有一个key,worker线程通过lister接口获取这个对象的最终状态,worker只需要根据这个最终状态变更1次即可。

    (2)控制器协同工作原理

    k8s是一个微服务管理系统,不同的组件负责不同的职责。
    以下图的deployment为例,用户创建一个deployment。
    在这里插入图片描述

    • deployment是一种部署策略,定义了需要什么样的pod,副本数是多少。
    • deployment controller watch到deployment,创建一个replicaset副本集。
    • replicaset controller watch到replicaset,创建pod副本集。
    • 调度器发现pod还没有调度,开始调度pod,把pod和node绑定。
    • kubelet监听所有pod的变更,如果pod的nodeName所调度的节点和本节点的Hostname一致,就会继续检查当前pod是否已经在此节点上存在,若存在,则删除原有pod相关容器,再重新创建pod;若不存在,则调用CRI创建pod相关容器、调用CSI为容器配置存储、调用CNI为容器配置网络
    (3)Garbage Collector(级联删除)

    Garbage Collector:通过ownerReferences处理级联删除,它的内部会有一个Graph Builder,构建一个父子关系图。比如删除 deployment 时它会去扫描自己的GraphBuilder,看有没有对象的ownerReferences是这个deployment,所以也会删除对应的 replicaset 以及Pod。

    deployment是利用rs做版本控制的,其中的revisionHistoryLimit是指定了rs保留版本数。其他控制器都是通过ControllerRevision做版本管理的,但是无法指定版本保留个数

    k get deploy -o yaml
    
    • 1

    在这里插入图片描述

    k get rs -o yaml
    
    • 1

    可以看到metadata有一个ownerReferences属性,代表着它的父辈是谁。这里就是nginx的deployment。uid也是deployment的uid。
    在这里插入图片描述

    k get po -o yaml
    
    • 1

    可以看到pod的ownerReferences中,指向的是nginx的rs。
    在这里插入图片描述
    我们可以清楚的发现各个对象都会记录自己的父对象是谁,这样其实就形成了一张对象相互级联的拓扑图。假如自己现在执行下面:

    # delete默认是级联删除的
    kubectl delete deployment xxxx 
    
    • 1
    • 2

    因为我创建的时候就是创建的deployment,那么对用户来说并不知道其他级联对象比如rs的存在,那么用户删除dp,应该将其他级联的也删除了。假如我们不需要级联删除的话就执行

    # old kubelet reversion
    kubectl delete deployment xxxx --cascade=false
    # new kubelet reversion
    kubectl delete deployment xxxx --cascade=orphan
    
    • 1
    • 2
    • 3
    • 4
    (4)deployment controller中的命名规则

    注:deployment需要有Template是为了创建RS,RS要有template是为了根据template创建pod。RS的pod-template-hash label和deployment pod-template有命名上的弱绑定.

    nginx pod名字是nginx-6cc88c7947-vh4rq,pod是由rs创建的,因此rs控制器名nginx-6cc88c7947+随机字符串就是pod名,这样就保证了pod名的独一无二。其他控制器因为没有两层关系。不需要pod-template-hash 命名弱绑定,因此控制器的名字都没有hash值,podname就是控制器名+随机字符串

    [root@localhost ~]# kubectl get ds
    NAME    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
    lxcfs   57        57        57      57           57          <none>          179d
    [root@localhost ~]# kubectl get pods|grep lxcfs
    lxcfs-2xgrp                                           1/1     Running   0          155d
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、informer工作机制

    • informer会和apiserver有一个长连接,通过list和watch。
    • apiserver的rest调用返回给客户端的是序列化的json字符串。reflector通过反射机制解析json tag,这样就能知道这个json对应的是哪个对象。简而言之就是reflector把json的字符串转换成了go语言的对象。
    • informer内部维护了一个delta fifo 先进先出的队列,reflector把对象放进队列中。
    • 取出来的对象放到indexer中,存储对象和key到一个线程安全的store中。
    • 在添加对象的同时,event会触发事件,注册event的handler。
    • handler把对象的key放到workqueue队列中。
    • worker的线程去获取key。
    • worker从indexer中根据key获取对象的完整数据。
    • 根据对象的完整数据做一些配置管理。
      在这里插入图片描述

    三、各种Controller

    • Job Controller: 处理 job。
    • Pod AutoScaler:处理 Pod 的自动缩容/扩容。
    • RelicaSet:依据 Replicaset Spec 创建 Pod。
    • Service Controller: 为 LoadBalancer type 的 service 创建 LB VIP。
    • ServiceAccount Controller: 确保 serviceaccount 在当前 namespace 存在。
    • StatefulSet Controller:处理 statefulset 中的 Pod。
    • Volume Controller: 依据 PV spec 创建 volume。
    • Resource quota Controller:在用户使用资源之后,更新状态。
    • Namespace Controller:保证 namespace 删除前该 namespace 下的所有资源都先被删除。
    • Replication Controller:创建RC 后,负责创建 Pod。
    • Node Controller:维护 node 状态,处理 evict 请求等。
    • Daemon Controller:依据 damonset 创建 Pod。
    • Deployment Controller:依据 deployment spec 创建 replicaset。
    • Endpoint Controller:依据 service spec 创建 endpoint,依据 podip 更新 endpoint。
    • Garbage Collector:通过ownerReferences处理级联删除,它的内部会有一个Graph Builder,构建一个父子关系图。比如删除 deployment 时它会去扫描自己的GraphBuilder,看有没有对象的ownerReferences是这个deployment,所以也会删除对应的 replicaset 以及Pod。
    • CronJob Controller:处理 cronjob。

    1、Cloud Controller Manager

    Cloud Controller Manager 是从老版本的 API Server 分离出来的。

    • kube-apiserver 和 kube-controller-manager 中一定不能指定 cloud-provider,否则会加载内置的 cloud controller manager。
    • kubelet 要配置 --cloud-provider=external。

    (1)Cloud Controller Manager 主要支持:

    • Node controller:访问 cloud APl,来更新 node 状态;在 cloud 删除该节点以后,从Kubernetes 删除 node;
    • service controller: 负责配置为 loadbalancer 类型的服务配置 LB VIP;
    • Route Controller:在 cloud 环境配置路由;
    • 可以自定义任何需要的 Cloud Controller。

    (2)需要定制的 Cloud Controller:

    • Ingress controller;
    • Service Controller;
    • RBAC controller;
    • Account controller。

    (3)建议:
    保护好 controller manager 的 kubeconfig:

    • 此 kubeconfig 拥有所有资源的所有操作权限,防止普通用户通过 kubectl execkube-controller-manager cat 获取该文件。

    2、确保 scheduler 和 controller 的高可用

    (1)Leader Election:

    • k8s提供基于 configmap 和 endpoint 的 leader election 类库。
    • k8s采用leader election 模式启动应用组件后,会创建对应 endpoint,并把当前的leader 信息 annotate到 endponit 上。其实这个endpoint去做高可用的方式已经在新版本中被淘汰了。
      在这里插入图片描述
      现在新版本统一的高可用管理是通过lease这个crd对象专门去做的:
      在这里插入图片描述
      如果想切换leader,就编辑lease对象的yaml,删除HolderIdentity,就会触发一次重新选举,其实这个HolderIdentity就是一把互斥锁,谁获取到了,就是leader。
      (2)流程:
    • 假如集群有3个master,每个master上都跑了控制器。控制器在启动前都会去尝试获取一把锁。这个锁就是k8s对象(cm或者ep)。
    • 下面的ep配置中,所有的scheduler实例都会以kube-scheduler的名字获取这把锁,看这个ep是否存在,如果存在,就把自己的身份信息更新进去。
    • 当要调度的时候,首先要看ep中的holderldentity是否和当前节点名字一致,如果一样才能进行调度。同时leader也要不停的renew,leaseDurationSeconds如果在15秒内没有来续约,就会认为leader失效,其他standby就会去获取锁。
    • 不一致节点就会一直watch这把锁,直到租约失效,然后抢占锁成为master。

    在这里插入图片描述

  • 相关阅读:
    基于开源 PolarDB-X 打造中正智能身份认证业务数据基座
    驱动开发,IO多路复用(select,poll,epoll三种实现方式的比较)
    安卓APT技术讲解(下)-实现安卓组件化的路由功能
    JDK动态代理和CGLIB动态代理介绍
    终端天线—8.2G手机调试
    微信小程序 JAVA学生选课系统UNIAPP
    JavaFX作业
    SpringBoot 01 HelloSpringBoot
    pysot-master-train.py 运行记录
    Python正则表达式
  • 原文地址:https://blog.csdn.net/weixin_44571270/article/details/126692433