Kubernetes 学习20调度器,预选策略及优选函数

一、概述

  1、k8s集群中能运行pod资源的其实就是我们所谓的节点,也称为工作节点。master从本质上来讲,他其实是运行整个集群的控制平面组件的比如apiserver,scheal,controlmanager,除此之外master还依赖于etcd这样的存储节点。最好还是一个有冗余能力的集群才可以。后来我们使用kubeadm去部署时也把这个部署平面运行为了所谓静态pod的应用程序。从本质上来讲我们可以认为他就是一个简单运行在master本地的守护进程。所以从这个角度来讲master本身是不运行为任何工作负载的,比如我们现在要跑一个nginx,跑一个myapp,那么他一定不能跑在master节点上。所以我们说从这个角度来讲master就是控制平面。我们所有的工作节点才是真正意义上去运行工作负载pod的相关节点。我们在开始运行的时候也说过,终端用户以后在运行时只需要提供给master就行。他甚至不用关心对应的自己的pod运行在哪个节点之上。因为我们工作节点被master组织成为了一个庞大的虚拟的资源池,这个资源池中把cpu,内存,存储卷这样的资源统统整合为同一个统一的资源向客户端提供。话虽如此但是作为集群管理人员来讲,对这样一个虚拟的资源池不是我们所关注的。我们还是需要了解底层我们pod究竟是运行在哪个或者哪些节点上。还要关心这些Pod还要更加优化的部署在哪些节点上。

  2、因此当用户请求创建一个对应的集群资源对象时应该运行在哪个节点上,是由控制平面中的Scheduler来做决策的。我们也称为调度器,整个k8s的调度器也是允许自定义的。但是默认情况下如果我们没有定义它我们也可以理解为说我们用的是默认的调度器,即default Scheduler。如果我们创建自主式pod然后查看这个pod的详细信息,describe的描述结果中的events字段中会存在像Scheduler  successful这类提示信息。会高速我们pod已经被调度至某某节点。接下来在这个节点之上就开始去创建pod,去关联存储卷之类的。所以当用户请求向apiserver要创建一个pod时apiserver检查权限都没有任何问题的话接下来他会把这个请求交由Scheduler,由Scheduler尝试着从众多节点中选择一个适用的节点来作为接下来运行此pod资源对象的节点。他的选择结果并不是反应在节点之上的,他的选择结果会告诉apiserver我帮你选定了大概是某某某节点,并且其选定的结果会记录在etcd中,因为这个选定的结果将会在一段时间内成为一个持久的状态,如果这个节点不发生故障,或者这个Pod资源不会因为资源紧缺而被OOM killd或被驱逐的话那么这个pod节点将一直在这个节点中运行。哪怕被重启他也会在这个节点上运行。所以这个数据要放置存储在我们的etcd中实现存储的目的。接下来由我们的apiserver指挥着被选定节点的kubelet,二者之间就能产生连接了,或者说kubelet始终在watch着apiserver中与当前节点相关联的事件变动,如果某一Scheduler调度的结果已经被apiserver输出出来说它是关乎到某一节点上,那么这个节点上的kubelet一定能watch到和自己相关资源的变动状态。意思是分配给自己一个新的pod要运行了。因此接下来我们这个节点它就要去尝试着获取到apiserver中定义的这个pod的规范和模板,或者我们称之为配置清单,根据配置清单中的定义而后去获取而后创建一个pod。pod从根本上来讲是用于运行容器的,运行容器时他根据镜像获取策略,根据定义的imagepullsecret,如果是私有仓库的话就连到仓库服务器上去下载镜像。当然在下载镜像之前要看获取镜像的策略是什么。如果是always则无论本地有没有都会去下载,如果是其它两个策略就根据其它两个策略来决定是否下载镜像。等镜像下载到本地以后就基于镜像创建容器。而这些都是kubelet要完成的任务。

  3、pod一般是有生命周期的,假设我们这儿创建的是自主式pod显然如果pod出现故障我们可能会重启他。如果节点出现故障那么我们重启就不会实现了。因此最好还是用控制器来控制。此处我们暂时先不说是哪种pod,就说在创建和运行时由于他有生命周期所以在必要的时候我们在前端给其加一个service以提供固定的访问端点,而service其实并不是一个组件,他只是所有节点上相关的iptables或ipvs规则。因此必要的时候当用户通过kubectl或任何客户端包括我们讲到的dashboard的。创建一个service对象时这个请求一样提交给apiserver,apiserver检查认证授权准入控制后发现没问题时就会创建一个service,这个service创建完以后结果一样要存储在etcd中,因此我们apiserver是始终在操作etcd,能够和apiserver打交道的也只有apiserver,scheduler也是做不到的。当存储完以后在每一个节点上还有个重要的组件叫kube-proxy,他会时刻监控着和service资源相关的变动,而后把它创作成当前节点上的iptables或ipvs规则。

  4、无论是kubelet还是kube-proxy他们其实都要连接至apiserver去获取某些资源定义的,而apiserver可不是人人都可以访问的,其需要做一大堆权限检查。因此从这个角度来讲,kubelet和kube-proxy都是apiserver的客户端。和kubectl一样,他只不过是自己内部通信的客户端而已。而且他们之间在数据传输时也要做数据序列化。与kubectl这样的客户端做数据序列化时他使用的序列化方案叫json。而且内部的数据序列化方案叫 particbuffers,这是谷歌自己研发的一种特别底层的数据序列化解决方案,了解即可。

    

  5、那么我们有这么多节点调度器是如何决策应该是由哪一个节点运行pod对象呢?简单来讲我们的scheduler调度时我们调度器应该被想象成这么一种组件,当用户创建某一个pod对象时其实我们有一组节点可以用,这组节点都是能运行pod的节点。我们scheduler应该做出这样的决策,将这一组节点对象列表中找出一个最适合运行这个pod的节点,那么他是如何评判谁最佳呢?这就是调度器干的事情,default scheduler在默认实现此调度决策时是通过三个步骤来完成的,叫三级,事实上主要工作在两级中完成。

    a、第一步,预选(Predicate):假如我们这里有一堆节点,在这堆节点中先排除那些完全不符合此pod运行法则的节点,在k8s中pod中运行容器可以定义两个维度的资源限制,第一叫资源需求,意思是说每一个节点只有满足我的最低资源需求他才能够运行,比如我这个pod运行至少需要2g内存1核cpu,这就是资源下限,低于这个下限就没法运行了。这是基本保证,基本保证满足了pod运行过程中有可能会使用超出我们所定义的需求的资源。最多能到多少呢?我们还可以做资源限额。所以对一个pod来讲他就能做两个维度的限制。第一个维度为起始资源基本要求,满足这个基本要求他才能运行。第二个维度我们称为资源的上限,也叫资源的限额。超出这个限额我们也就不再给他分配任何内存等资源。一般来讲这是一种硬限制。第三个维度我们称为当前占用:而pod当前用了多少,我们虽然说他需求两个g内存,他也不一定一启动一运行就一定有两个g内存,所以这就叫当前占用。 所以这就是三种状态:当前占用;资源需求;资源限额;很显然这众多节点中不是每一个节点剩余的可用资源都能满足其需求,若不能满足的肯定就排除掉了。当然还有一些其它标准,比如我们在定义一个pod的时候如果这个pod被我们定义成直接共享宿主机的网络名称空间并且这个pod中的容器也要监听在80端口上,假设我们这里有很多节点,有些节点的80端口已经被占用了,我们把它调度上去也就没法启动了,因此我们也要排除这些节点。 所以第一步的预选过程就是要从我们所有的节点中去排除那些完全不能符合我们对应Pod运行的基本要求的节点,这个过程我们称为预选过程。

    b、第二步,优选(Priority):基于一系列的算法函数把每一个节点的属性输入进去,然后去计算每一个节点的优先级,计算完以后再对他们进行排序。可以认为叫逆序排序,然后取得分最高那一个,而后得分最高的那一个就是我们选择的节点。即基于优先级找出最佳匹配的节点。

    c、第三步,选定(Select):优选完成后我们将pod绑定至选定好的节点即可。如果说出现一种极端情况,最后计算得分,最高得分者有好几个,这个时候就没有任何偏向性了。就随机选择一个即可。然而有些有特殊偏好的pod资源需要运行在特定的节点上,因为这个pod对节点有一些特殊偏好,比如说有些应用必须要运行在ssd硬盘的节点上。我们不一定所有节点都有ssd,有些pod需要运行在GPU服务器节点上,说不定要挖矿,因此在这种情况下我们需要对节点添加标签分类的,这种分类有地理位置上的分类,有特性上的分类,拥有的硬件设备之类的,我们要给他加上很多的标签,而后我们的pod再去定义他要运行在哪个或哪些节点时是可以完全额外定义自己的特有倾向性的。在我们pod中有一个属性叫做nodeselector,意思是我只对某一些特殊的类型的节点有倾向。哪些特殊类型呢?我们使用nodeselector去匹配节点标签,如果节点拥有这个标签或节点这个标签的值符合我们定义的选择标准那么他就会被分配到这个节点。因此可以理解为这就是一种调度条件的限定。这个时候在预选过程中哪些节点符合条件?因此我们所定义的很多Pod的属性都有可能会影响到预选甚至是优选中的步骤。

  6、事实上,我们在k8s之上能够使用的特殊的调度方式有这么几类

    a、节点亲和性调度,通过nodeselector来完成调度

    b、pod的亲和性和pod的反亲和性调度,有时候我们期望某些个Pod运行在同一或相邻的节点上。这个相邻有可能是网络通信带宽可用量更大一些这样的节点。比如说自己定义的三层结构nmt,n运行在南京的机房,m运行在成都的机房,t运行在北京的机房,这样就晕掉了。因此我们让其运行在同一个机架中的不同服务器中也行。如果我们期望两个pod运行在同一个节点上这个我们就称之为他们具有亲和性。即某些个pod倾向运行在同一位置就表示他们具有亲和性。而倾向于不要运行在同一位置他们就有反亲和性,即老死不相往来。之所以有反亲和性各位应该明白,假如一个地方运行httpd,他还要绑定在你的物理节点的网络名称空间上,另一个地方运行nginx也一样要绑定,假如他两绑定到一块儿同时监听80就冲突了,这种就是反亲和的。我们要想办法让其运行在不同的节点上才行。另外有些节点有可能运行的是一些秘密程序,里面有些很机密很敏感的数据,我不期望其它pod和其运行在同一节点上,万一看到里面的涉密信息就不好了。这个时候我们就要尽可能隔离出来这些节点只运行这一堆或一些pod,这说明这些pod和其它pod都是反亲和的。

    c、污点和污点容忍调度(Taints【定义在节点之上】,Tolerations【定义在pod之上】),它是一种反其道而行之的做法,所谓反其道而行之可以认为说刚刚我们一直讲pod怎么选定哪些节点,但是我们也允许节点可以不让pod来运行,即我们可以给一些所谓的节点打上污点标识说明这个节点是拥有不光彩,见不得人的一些事情存在的,因此一个pod是否能够运行在这些节点上就取决于他是否能容忍这些污点。比如你经常出去吃喝嫖赌,那么一个人是否愿意嫁给你就要看他是否能容忍这些污点了。因此我们去调度Pod时也可以基于此种方式来进行调度。第一,我们在每一个节点上都给其打上污点。然后pod在调度时给这些pod定义容忍污点的能力,容忍哪些污点他就能调度到包含这些污点而且仅含有这些污点子集的节点上去,比如有一个Pod非常大度他能容忍10个污点,第一个节点有5个污点并且正好这5个污点还是这个Pod容忍度的子集,所以这样子就表示这个pod能运行在子节点上。而且这个节点突然之间不想让这个Pod运行了怎么办呢?我们还可以在节点上多打一个污点,这个污点不属于他的容忍度。这种情况下我们这个pod有两种选择,第一,pod既然已经调度完成了,后来发现他还有其它的污点,这个容忍不了就留下来艰难度日。第二,这个节点自己认为对不起这个pod,就可以驱逐这个pod。当然,驱逐的时候我们也可以给其定义一个容忍期,比如给你一个小时离开这儿。

  7、上述为特殊的调度倾向和选择,那么如果我们自己定义时如果期望影响调度器的工作特性,比如他怎么预选,如果我们自己给节点打上了污点,给pod定义了容忍,那么一定会影响预选这一关到底哪些符合条件哪些不符合,所以我们可以基于自己的影响调度机制的调度行为来影响预选和优选的工作结果。从而使得我们能够完成自定义的一些高级调度选择倾向机制。否则的话如果我们的节点即没有什么倾向性也没什么特殊特征那么他的调度就会根据算法来。通常可能是均衡调度的。即当前哪个节点资源空闲较大这个新pod就调上去了。

二、高级调度功能配置

  1、接下来我们来看常用的预选机制有哪些。即在调度pod时他的预选因素有哪些。即他的调度过程是怎么实现的呢?(https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/algorithm)从这个链接中我们可以看到k8s的预选策略(predicates)和优选函数(priorities)。

  2、在https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/algorithm/predicates/predicates.go这个文件中定义了我们常见的预选策略有哪些。这里介绍几个常用的

    a、CheckNodeCondition:检查是否可以在节点报告磁盘,网络不可用,或未准备好的前提下能够把Pod调度到上面去。若上报这些问题肯定就不会调度上去。很显然在此之前说白了就是看这个节点是否是正常的。有些节点可能在你调度那一刻就在报告故障状态,那么我们就不再予以调度了。

    b、GeneralPredicates:通用预选策略,虽然叫通用预选策略但是他并不是一个单独的预选策略,他本身包含着好几个预选策略,比如

      Hostname:如果pod定义了hostname这个属性则检查节点的名字和这个hostname是否相匹配。但是这里并不是定义这个pod必须运行在这个节点上。他只是想说在对应的节点上这个pod还没有被使用,不然的话在这个节点上Pod就同名了。因为有些Pod名称是固定的不是随机生成的。即检查pod对象是否定义了 pod.spec.hostname属性的值,如果定义了那么就检查被调度节点的主机名是否与他相匹配。

      PodFitsHostPort:能适配节点的端口,如果我们Pod的containers中定义了hostPort属性,即如果我们pod中的容器定义了pods.spec.containers.ports表示我们要指定绑定在节点的哪个端口上,如果节点的这个端口已经被占用了就表示这个节点就无法被调度否则就是符合条件的。

      MatchNodeSelector:这是检查pods.spec.nodeSelector这个字段是否定义了,如果定义了我们就看他的标签选择器中能匹配到哪些节点,或者说看看我们节点的标签能否适配到这个pod的节点选择器的。很显然,这就是为什么这里的对应的预选策略中我们一旦定义了pod的节点选择器而我们对应的node没有适配的标签时他就没法运行的原因。

      PodFitsResources:检查Pod的资源需求是否能被节点所满足。表示检查节点是否有足够的资源去支撑这个pod运行。刚刚解释过,pod有可能会有资源的需求,也就是说运行此pod来讲资源最低限制,如果节点上的可用量少于pod的需求量就表示这个节点是不符合当前Pod运行的。每一个节点其实都会声明其对应可用资源量的

[root@k8smaster ~]# kubectl get nodes
NAME        STATUS    ROLES     AGE       VERSION
k8smaster   Ready     master    98d       v1.11.1
k8snode1    Ready     <none>    98d       v1.11.1
k8snode2    Ready     <none>    98d       v1.11.1
[root@k8smaster ~]# kubectl describe node k8snode1
Name:               k8snode1
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    disktype=ssd
                    kubernetes.io/hostname=k8snode1
Annotations:        flannel.alpha.coreos.com/backend-data={"VtepMAC":"ba:f3:a5:6c:9b:f5"}
                    flannel.alpha.coreos.com/backend-type=vxlan
                    flannel.alpha.coreos.com/kube-subnet-manager=true
                    flannel.alpha.coreos.com/public-ip=192.168.10.11
                    kubeadm.alpha.kubernetes.io/cri-socket=/var/run/dockershim.sock
                    node.alpha.kubernetes.io/ttl=0
                    volumes.kubernetes.io/controller-managed-attach-detach=true
CreationTimestamp:  Thu, 09 May 2019 09:53:25 +0800
Taints:             <none>
Unschedulable:      false
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  OutOfDisk        False   Thu, 15 Aug 2019 14:28:42 +0800   Tue, 11 Jun 2019 16:08:40 +0800   KubeletHasSufficientDisk     kubelet has sufficient disk space available
  MemoryPressure   False   Thu, 15 Aug 2019 14:28:42 +0800   Tue, 11 Jun 2019 16:08:40 +0800   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Thu, 15 Aug 2019 14:28:42 +0800   Tue, 11 Jun 2019 16:08:40 +0800   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Thu, 15 Aug 2019 14:28:42 +0800   Thu, 09 May 2019 09:53:25 +0800   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            True    Thu, 15 Aug 2019 14:28:42 +0800   Mon, 08 Jul 2019 11:19:43 +0800   KubeletReady                 kubelet is posting ready status
Addresses:
  InternalIP:  192.168.10.11
  Hostname:    k8snode1
Capacity:
 cpu:                2
 ephemeral-storage:  23049220Ki
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             1867048Ki
 pods:               110
Allocatable:
 cpu:                2
 ephemeral-storage:  21242161117
 hugepages-1Gi:      0
 hugepages-2Mi:      0
 memory:             1764648Ki
 pods:               110
System Info:
 Machine ID:                 89761b2775be41008205f74d4168d946
 System UUID:                F50A4D56-6C0D-C3FA-A5FC-37216BC5FDE9
 Boot ID:                    a6655cb2-1b24-45d2-9591-c806d8477d41
 Kernel Version:             3.10.0-693.el7.x86_64
 OS Image:                   CentOS Linux 7 (Core)
 Operating System:           linux
 Architecture:               amd64
 Container Runtime Version:  docker://18.6.0
 Kubelet Version:            v1.11.1
 Kube-Proxy Version:         v1.11.1
PodCIDR:                     10.244.1.0/24
Non-terminated Pods:         (6 in total)
  Namespace                  Name                                     CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------                  ----                                     ------------  ----------  ---------------  -------------
  ingress-nginx              default-http-backend-846b65fb5f-ggwwp    10m (0%)      10m (0%)    20Mi (1%)        20Mi (1%)
  kube-system                canal-xxvzk                              250m (12%)    0 (0%)      0 (0%)           0 (0%)
  kube-system                kube-flannel-ds-amd64-5plwk              100m (5%)     100m (5%)   50Mi (2%)        50Mi (2%)
  kube-system                kube-proxy-6btdl                         0 (0%)        0 (0%)      0 (0%)           0 (0%)
  kube-system                kubernetes-dashboard-6948bdb78-ks8bq     0 (0%)        0 (0%)      0 (0%)           0 (0%)
  prod                       pod1                                     0 (0%)        0 (0%)      0 (0%)           0 (0%)
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource  Requests    Limits
  --------  --------    ------
  cpu       360m (18%)  110m (5%)
  memory    70Mi (4%)   70Mi (4%)
Events:     <none>

    c、NoDiskConfict:检查pod依赖的存储卷是否能满足需求。检查pod对象所请求使用的存储卷在此节点上是否可用,如果目标节点能满足这个pod的存储卷使用需求就表示他能用,否则就表示这个节点不符合。比如他要使用一个glusterfs类型的存储卷,你的节点根本就找不到glusterfs那该节点就不符合要求。

    d、PodToleratesNodeTaints:检查Pod上的spec.tolerations可容忍的污点是否完全包含节点上的污点。检查pod是否能容忍污点,如果pod定义了此属性,那么就定义看看他能不能接纳节点上所定义的污点。如果是就表示这个节点能被选出来。否则表示无法通过预选。

    e、PodToleratesNodeNoExecuteTaints:不能执行的污点。即如果和pod一样的定义了spec.Tolerates属性,我们就去检查其是否能够接纳节点上所定义的nodeNoExecute的污点。它是检查pod的容忍污点是否能接纳节点上定义的拥有NoExecute属性的污点。我们污点其实有三重属性的,NoExecute只是其中一种,而且是一种特殊的污点,因此这种污点很独特。比如说,一个节点上有污点,我们pod也能接纳这些污点,所以pod有可能会被调度到这些有污点的节点上。被调度到这个有污点节点上以后后来这个节点的污点又改了,改成这个pod不能接纳了,这个时候默认这个pod会继续存在下去,因为调度而来的时候他没有这个污点,后来才有的。而NoExecute就是说他不能容忍,节点发现pod不能容忍他就会驱离pod,而默认情况下的污点容忍行为是即便后来不满足了他也会接受已经存在的事实。这个预选策略默认是没有被启用的,前面三个都是默认启用的。

    f、CheckNodeLabelPresence:他表示检查节点上指定标签的存在性。要检查的标签以及其是否存在就取决于用户的定义了,因为节点中有哪些标签都是用户定义的,所以可以通过修改节点标签来决定对某些类别的pod来讲他在调度时我们愿不愿意或者接不接受能调度到这些节点上。不过这个调度预选策略也是默认没有启用的。

    g、CheckServiceAffinity:这个预选策略目的就是将相同service的pod对象尽可能放置在一块,这样至少能使得service内部的pod之间如果需要通信效率就会变高。 根据当前pod对象所属的service已有的其它pod对象,一个service上可能有多个pod对象,这个service pod对象有一部分已经调度到这个节点上了,有些节点并没有运行这个service所关联的pod,比如123节点运行了service 对应的pod,但是4567就没有运行了,新创建的pod刚好又属于这个service,然后就看这个pod是运行在这个service已有的pod节点上还是没有的pod节点上。所以他的意思就是把这个pod调度到他所属的service已经调度完成的其它Pod所在的节点上去。不过其默认没有启用。

    h、MaxEBSVolumeCount

    i、MaxAzureDiskVolumeCount

    j、MaxGCEPDVolumeCount 

      最后这三个默认是启用的。这对应的是三大云计算服务商,而且他们都是CNCF即云原生计算基金会的成员。毕竟是自家地块上的事情肯定要倾向于自家。

    k、CheckVolumeBinding:检查节点上已绑定和未绑定的PVC

    l、NoVolumeZoneConflict:给定的区域限定的前提下,在当前区域检查此节点上的部署的pod对象是否存在存储卷冲突的情况,每一个节点过来了就检查这个节点的存储卷对象是否与这个pod的需求存在冲突。

    m、CheckNodeMemoryPressure:检查节点内存资源是否存在处于压力过大的状态。检查节点内存是否存在压力。也就意味着如果一个节点上本来内存资源就已经很紧缺了那就表示这个节点不符合需求。

    n、CheckNodePIDPressure:检查节点上是否已经报告PID数量资源压力过大,即已经运行了太多进程了,我们PID不够用了。

    o、CheckNodeDiskPressure:检查磁盘IO是否压力过大了。

    p、MatchInterPodAffity:检查节点是否能满足pod的亲和性或反亲和性。到底用哪一个则取决于自己的定义。若定义为亲和则看看新运行的Pod和节点是否亲和,若亲和则符合条件不亲和就不符合。反亲和条件也是一样的。检查我们pod与pod之间也是有亲和性的。我们可以定义两个pod更倾向于运行在一起。或更倾向于不能和哪些pod运行在一起,这就是pod的反亲和性。

  4、很显然,在我们每一个节点在我们启用了默认的调度器以后他默认启用了很多预选策略。那么预选策略是被一个检查还是被多个检查呢?从某一个节点来的,他第一个策略检查是匹配的,还要不要检查第二个呢?比如他检查一下发现资源够用,那就刚好满足条件了么?肯定不是的,肯定还需要检查其它的预选策略项。若任何一个策略不匹配都不满足,因此,需要满足所有的预选策略才能匹配。

  5、接下来便是优选函数。

    相关连接为https://github.com/kubernetes/kubernetes/tree/master/pkg/scheduler/algorithm/priorities

可以看到它是go写的。我们介绍几个常用的

    a、least_requested:指由节点的空闲资源和节点的总容量来比较。计算这个比值。他是计算比率的,比如你有16g内存,用了14g还空闲2g,这意味着它的空闲量是2g。另一个是4g的内存空闲了2g,这时我们认为哪个节点的空闲值大?前一个节点空闲1/8,第二个空闲1/2,很明显后面这个节点空闲多,因此这个时候他是根据空闲比例来评估的。 但是事实上发现这种方式计算并不是绝佳的,他还可以用另外一个方式来衡量,比如用节点的空闲资源容量与总容量的比值计算出来以后而后他有一个很独特的算法。

      对cpu来讲计算方式为:(总容量(capacity)-已经被别人请求占用的容量之和(sum(requested))) * 10/capacity  之所以乘以10原因在于每一个优选函数的计算得分是10分,到最后要计算总得分的,乘以10就相当于说以1到10之间的数值。

      内存的计算方式和CPU一样。整体得分就是一个计算函数。

      CPU+内存得分就是最终得分,这个得分有可能大于10的。因此要再除以2才行即最终计算方式为:

      (cpu((capacity-sum(requested))* 10/capacity)  + memory((capacity-sum(requested))* 10/capacity))/2

    b、BalancedResourceAllocation:CPU和内存资源被占用率相近的胜出。他表示以cpu和内存资源的占用率的相近程度作为评估标准,我们称之为cpu和内存的资源要占用的足够均衡,二者越接近比率越高。cpu占用率大概被请求占用了50%,内存剩余的也是50%,这就是最佳匹配列。当然如果都是20%也是相近的,平衡并不是评估节点,而是评估cpu和内存的。一定要和第一个函数一块使用才行。

    c、NodePreferAvoidPods:根据节点的注解信息'scheduler.alpha.kubernetes.io/preferAvoidPods'来判定是否节点倾向不要运行这个pod。此优先级函数的优先算法比例占用非常非常高。因为我们最终计算得分时大家各10分。他的计算方法是将来把这每一个函数得分加起来得分最高的胜出。但相比较来讲这个函数的得分计算的时候对应的他的优先级非常高。意思是说他如果倾向于不能运行这个pod,那么就一定不能运行这个pod了。  计算方式是如果给定的节点没有这个注解信息那么他的得分为10,而且权重为10000,所以他的权重会很高。没有这个注解就说明他是适合运行此pod的,否则存在此注解时,对于那些由replaication controal或者replaicatinset控制器管控的pod对象他的得分是0。也就意味着这个pod是不能运行在这个节点上的。

    d、TaintToleration:基于pod资源对节点污点的容忍度的调度偏好进行优先级评估的。评估方式是将pod对象的TaintTolerations的列表即pod.spec.tolerations中所定义的能够容忍的污点列表与节点的污点(taints)列表项进行匹配检查,成功匹配的条目越多得分越低。匹配的越多也就是说这个节点的污点越多。

    e、selectorSpreading:查找与当前pod对象匹配的servie replaction control,replactset等。大家知道标签选择器一般就被service,statefulset,replactionset,replactioncontrol所使用,他是同时用标签选择器去选择我们pod资源的。而后我们去检查与这些选择器去匹配的现存pod对象及其所在的节点有哪些。已经运行此类pod对象的越少的节点得分越高,spread是散开,他的意思是我们一定要把同一个标签选择器匹配到的节点pod对象散开到更多的节点上去。那哪个节点得分高呢?已经被此标签选择器匹配到的pod对象在这个节点上,这个节点上越多拥有此类pod对象他的得分就越低。否则得分越高。

    f、InterPodAffinity:遍历pod对象的亲和性条目,并将能够匹配到给定节点的条目的权重相加,结果值越大的得分越高。什么叫Pod亲和性呢?我们就以一个节点为例说明,一个节点上已经运行了很多pod,而后我们用这个pod自己去评估每一个节点,能在多大程度上满足这个Pod的亲和性。假如第一个节点他有两条能满足那么我们得分为2,第二个节点有三条能满足他的得分就为3,所以匹配条目即匹配项越多的得分就越高。

    g、NodeAffinity:节点亲和性。基于节点亲和性偏好进行评估,根据Pod资源中的nodeselector对节点进行匹配度检查,能成功匹配的数量越多得分就越高。

    h、MostRequested:空闲量越小的优先级越高,他的意思是尽可能把这个节点先用完,把pod集中运行在这些节点上,尽可能的把这个节点资源先用完,把pod集中运行在这些节点上,这样可以把其它节点空闲下来。可能这些空闲节点将来去批处理运行其它任务。和a中least_requested算法一样,意思相反。一般都不同时用。

    i、NodeLabel:根据节点是否拥有特定标签来评估得分。只是有标签就行,不管标签值是什么,因为他只关注标签本身。存在时就直接得分,不存在时就不得分。

    j、ImageLocality:根据满足当前Pod对象需求的已有镜像的体积大小之和。表示这个节点上是否拥有运行此pod所需要的镜像。有镜像就得分没镜像就不得分。他不是根据pod中容器数量对应的镜像个数来算的而是根据节点体积大小之和来计算的。

    后三个默认是没有启用的,默认是启用前面七个的。

  6、预选函数是一票否决的,那么优选函数是怎么评估最优的呢?假如我们5中前七个,每一个pod过来以后第一个函数计算会有得分,第二个函数计算也会有得分,所以对每一个pod来讲他会对你启用的所有函数做评估。评估完以后把每一个函数得分相加,得分分值最高的就是最佳的,如果最高的有好几个那么就随机选一个。

  7、接下来就是select阶段,即从预选和优选中随机选择一个pod

原文地址:https://www.cnblogs.com/Presley-lpc/p/11353441.html