zoukankan      html  css  js  c++  java
  • K8s预选策略和优选函数简介

    调度器选择策略:
     预选策略(Predicate)
      1. 根据运行Pod的资源限制来排除不符合要求的Node
      2. 根据运行Pod时,是否要求共享宿主机的网络名称空间来判断,如: 某Pod启动要共享宿主机的网络名称空间,启动80端口,而某些Node的80已经被占用,那它就不符合,就也要不排除。

     优选策略(Priority):
      此阶段会经过一系列函数,会把预选出来的节点的相关属性都输入到这些函数中,通过计算来获取每个节点的优先分值,然后在倒序排序,取得分最高者,作为运行Pod的节点,若计算后,发现有多个Node得分相同,此时将随机选取一个Node作为该Pod运行的Node。

     选定(Select):
      当优选选出一个Node后,该Pod将会被绑定到该Node上运行。

      我们在定义一个Pod要运行在那个Node,是有一定偏向性的,比如有些Pod需要运行的有SSD硬盘的Node上,而有些Pod则需要运行在有GPU的节点上等,这时就需要我们给Node上打上不同的标签,来标识整个集群中不同Node的特性,这些特性包括,特有硬件标识,地理位置标识,不同机房标识等。

      我们使用kubectl explain pod.spec 可以看到其中有nodeName 和 nodeSelector 两个属性,第一个是明确指定Node节点名,我就要运行在这个Node上,第二个就是通过匹配Node标签,来选择运行的Node。

    调度方式有以下几类:
      1. 节点倾向性(或叫 节点亲和性调度)
       这种调度通常会采用 nodeSelector 来完成调度。

      2. Pod倾向性 和 Pod非倾向性(这也称为Pod亲和性和非亲和性)
       有时候我们需要让某些Pod能运行在同一处时,就需要使用Pod亲和性来定义,如:我们要定义一个NMT(Nginx+Tomcat+MySQL),若这三个Pod分别被调度三个不同地域的机房中,比如北京,上海,深圳等地,那肯定不是我们所希望的,这时我们就需要使用Pod亲和性来定义这三个Pod能运行在相同的Node 或 相邻的Node上。
      而在另外一些情况下,我们又希望某些Pod一定不能运行在相同Node上,这时就需要使用Pod的非亲和性了。

      3. 污点 和 污点容忍调度
       Taints(污点):是定义在Node上的
       Tolerations(容忍): 是定义在Pod上的
        污点:指有时我们希望不将某些Pod调度到某些Node上,这时我们可对某些Node上打上污点,然后,当我们创建Pod时,我们可定义此Pod能容忍的污点范围。
        假如:NodeA上有5个污点: "吃喝嫖赌抽",而此次要调度的Pod比较大度,能容忍5个污点,而NodeA的污点正好是Pod能容忍污点的范围内,于是Pod就能"嫁入"该NodeA。
        假如,Pod运行到NodeA上后,后期我们又给该NodeA打上了新污点如:"坑蒙拐骗偷",此时Pod发现NodeA的恶性原来有这么多,怎么办?通常有两种选择策略:
        1. Pod继续运行在NodeA上"艰苦度日"
        2. Pod被驱逐,或者叫Pod逃离。

    默认调度器的调度规则是:根据Node最大资源空闲率来进行均衡调度。

    调度器:
     预选策略
      【注意:下面这些预选策略仅是其中一部分,并且k8s在进行预选时,它会是综合评估的,即所有启用的策略规则都要做判断,所得结果越符合就越会被挑选出来,进入优选策略中】:
      CheckNodeCondition: 检查节点是否符合调度条件
      GeneralPredicates:
      HostName: 这种是判断Pod是否定义了pod.spec.hostname属性,若定义了,就在预选时,看看这些Node上是否存在相同主机名的Pod,若有,就排除该Node; 这可能是因为,某些Pod是需要有固定主机名,才会使用。
      PodFitsHostPorts: 此预选策略是判断 pods.spec.containers.ports.hostPort 属性是否定义了,若定义了就表示该Pod要绑定到Node上指定的Port上,这时在进行预选时,就要判断这个端口是否被占用了,若占用就会排除该Node。
      MatchNodeSeletctor:此预选策略会判断 pods.spec.nodeSelector 属性是否定义了,若定义了就根据Pod所定义的NodeSelector来选出匹配指定标签的Node。
      PodFitsResources: 此预选策略会判断 Node上是否符合运行Pod所需的最小空闲资源。

      NoDiskConfict: 用于判断若Pod定义了存储卷,则要检查该存储卷在该Node上是否可用,若Node能满足Pod存储卷的使用需求,则表示此Node可用。
      PodToleratesNodeTaints:检查Pod上的spec.tolerations可容忍的污点是否完全包含Node上定义的污点,若完全包含,则表示Pod能容忍该Node节点的污点,否则该Node不会通过预选。
      PodToleratesNodeNoExecuteTaints:
       #首先,污点并非单一属性,它有三种属性,NoExcute是污点的一种属性。
       #此检查是,若Pod被调度到某Node上时,最初Node上没有Pod不能容忍的污点,但后来Node的污点改变了,多了Pod不能容忍的污点,这时要怎么处理那?默认是继承既定事实,继续在该Node上运行。
       # 第二种是不接受,也就是检查此属性,此时Node会驱逐该Pod,让该Pod重新调度到新Node上。

      CheckNodeLabelPresence:这是定义在选择Node时,检查Node上是否存在指定的标签,若存在则可调度。此规则默认不启用。
      CheckServiceAffinity: 当我们需要创建一些Pod时,这些Pod都使用相同的Serivce,这时,若启用该预选策略,则将这些Pod优先调度到 已存在较多该Service下的Pod的Node上。

      #下面三个默认启用,因为这三个是最大的三个云运营商
      MaxEBSVolumeCount:此预选策略是指若Pod需要使用亚马逊的EBS(弹性块存储服务),则检查该EBS是否已经达到最大运行挂载数39个,若没有,则表示还有存储空间可用。
      MaxGCEPDVolumeCount: GCEPD:是Google的云环境中的持久存储,默认是16个
      MaxAzureDiskVolumeCount: 最大是16个

      CheckVolumeBinding:检查Node上是否存在以绑定 和 未绑定的PVC,若Node上有PVC,并且没有被绑定则能满足Pod运行的需求。
      NoVolumeZoneConflict:它是在给定了区域限制的情况下,Zone在IDC机房是有限制的,他们通常会按房间,机柜,机架来划分范围,假如机架上有20台服务器,每2台一个区域,这个就是一个逻辑区域,此配置项的含义就是检查区域中Node上是否存在逻辑卷冲突。

      CheckNodeMemoryPressure:检查Node上是否存在内存压力,即若某Node上已经出现内存紧张的情况了,那就不要在往它上面调度了。
      CheckNodePIDPressure:检查Node上是否存在PID压力过大的情况,即若某Node上运行的进程过多,导致PID可能紧张,这时在选择调度时,也不会选择它。
      CheckNodeDiskPressure:检查Node上是否存在磁盘使用压力过大的情况

      MatchInterPodAffinity:检查Node上是否满足Pod的亲和性,假如Pod是Nginx,它是要为Tomcat Pod做代理的,那么在调度tomcat Pod时,就会检查Node上是否有Nginx Pod,若有这个非常亲和的Pod则优先调度到该Node上。



    优选函数:
      默认启用了7个红色的优选函数,它们的得分相加值越高,该节点越优选被选中。
      LeastRequested 【与它相反对一个优选函数:MostRequested,它是Node资源占用越高得分越高,有点像是,先将某Node的资源先全部占满的意味,然后空出部分Node.】
         计算得分的算法公式=(cpu((capacity - sum(requested))*10 / capacity) + memory((capacity - sum(requested))*10 / capacity))/2
           此公式的含义:
          假如当前有16个核心,已经用了8个核心,那么总的使用率就是 (16-8)/16 = 0.5 ,而乘以10,是为了计算得分方便,将其转化为0~10之间的数,
          而内存的计算含义一样,假如内存32G,已经用了8G,则使用率为 (32-8)/32 = 0.75 ,乘以10
          将CPU + Memory/2 就可以得到一个平均值。该平均值越高,表示该节点的资源空闲率越高。

      BalanceResourceAllocation:
        它表示CPU和内存资源占用率的相近程度,作为评估标准,越接近也优选选择,比如: Node1的CPU和内存使用率一个48%一个50%,而Node2的CPU和内存使用率都是50%,则Node2胜出。
      #它通常要和LeastRequested 结合来评估Node的资源利用率的。
      #两个组合主要是为了平衡各Node上资源的利用率。

    NodePreferAvoidPods
      根据Node节点上是否定义了注解信息"scheduler.alpha.kubernetes.io/preferAvoidPods",若没有则该Node得分为10,而且权重为1万,有此注解则说明该Node是适合运行Pod的。而对于那些有Replicas Controller的控制器则,该Node的得分为0,此时则表示Pod不能运行在该Node上,但这不能决定Pod一定不能运行在该Node上,因为最终是要看总体得分的。
      注解信息:
        # kubectl describe node node1.zcf.com
          Name: node1.zcf.com
          Roles: node
          ..........
          Annotations: node.alpha.kubernetes.io/ttl: 0      #这就是所谓的node上的注解信息。
          volumes.kubernetes.io/controller-managed-attach-detach: true

    TaintToleration
      基于Pod对调度Node上的污点容忍度来评估是否可调度到该Node上。
      简单说:就是取出Pod对象中定义的spec.tolerations列表,查看其中能容忍的污点,然后,一个一个去对比Node上存在的污点,若匹配的越多,则该Node的得分越低,就越不会调度到该Node上。

    SelectorSpreading
      此优化函数是希望将相同标签选择器选取的Pod尽可能的散开到多个Node上。因此假如:ReplicaSet控制器,已经在B上创建了一个Pod,那么它要再创建一个Pod时,此优选函数在计算A,B,C三个Node的得分时,会根据那个Node上拥有此ReplicaSet控制器的标签选择器所匹配的Pod数量,来评分,数量越少,分数越高,反之越低。
      而使用标签选择器的资源对象有:Service,Replication Controller,ReplicaSet,StatefulSet。

    InterPodAffinity
      遍历对象的亲和性条目,并将能够匹配到给定节点的条目的条目数相加结果值越大得分越高。
      简单说:NodeA上运行了3个Pod,NodeB上运行了6个Pod,NodeC上运行了5个Pod,现在要调度一个Pod到其中一个Node上,而该Pod的比较亲和某类Pod,此优选函数就会将这些Node上,所有匹配该Pod亲和条目的数量相加,值越大,则越得分越高。其中条目的理解,我还不是很懂。

    NodeAffinity
      它是根据pod中定义的nodeSeletor来对Node做亲和性检查, 能成功匹配的数量越多,则得分越高。

    NodeLabel:
      根据Node是否有某些标签来做评估,然后计算得分的,此优选函数只关注标签,不关注值,只有Node上有这个标签就可以得分。

    ImageLocality:
      它评分的标准是,判断Node上是否有运行该Pod的镜像,若有则得分,否则不得分。
      但它评估镜像是根据镜像总的体积大小来计算得分的,例如:NodeA上有1个镜像1G,NodeB上有2镜像,NodeC上有3个镜像,而运行此Pod需要4个镜像,其中一个镜像1G,其它都是比较小,这时NodeA的得分会最高。

    #实验:
      1. 定义一个Pod,并设置其nodeSelector,若指定的nodeSeletor标签没有匹配到任何Node,则Pod将处于Pinding状态,只有你给某个Node打上指定的标签和值后,该Pod才会被调度上去。

    #实验2:
      kubectl explain pods.spec.affinity
        nodeAffinity: 定义Node的亲和性
        podAffinity: 定义Pod的亲和性
        podAntiAffinity:定义Pod非亲和性

      nodeAffinity:
       # 软亲和性,即若能满足则一定运行在满足条件的Node上,否则运行在其它Node上也不是不可以。
       preferredDuringSchedulingIgnoredDuringExecution

       #硬亲和性,若不能满足运行条件,则不运行Pod。
       requiredDuringSchedulingIgnoredDuringExecution
       nodeSelectorTerms:
        matchExpressions :     这是更强大的一种方式,它是配表达式的。

        matchFields:
          key: 要对那个label key做匹配操作
          operator:指定你是做什么比较操作,支持: In/NotIn(包含/不包含), Exists/DoesNotExist(存在/不存在) , Gt/Lt(大于/小于)
          values:若操作符为Exists/DoesNotExists则,values必须为空。

    vim  pod-node-required-affinity.yaml
        apiVersion: v1
        kind: Pod
        metadata:
           name: pod-node-affinity-1
           labels:
                 app: myapp
                 tier: frontend
        spec:
          containers:
          -  name: myapp
             image: ikubernetes/myapp:v1
          affinity:
             nodeAffinity:
               requiredDuringSchedulingIgnoredDuringExecution:
                 nodeSelectorTerms:
                 -  matchExpressions:
                    - key: zone
                      operator: In
                      values:
                      - foo
                      - bar
    
    #默认node上没有打上zone=foo 或 zone=bar的标签,此时是硬亲和,若不能满足条件,则无法调动.
    
    
    vim  pod-node-affinity.yaml
        apiVersion: v1
        kind: Pod
        metadata:
           name: pod-node-affinity-1
           labels:
                 app: myapp
                 tier: frontend
        spec:
          containers:
          -  name: myapp
             image: ikubernetes/myapp:v1
          affinity:
             nodeAffinity:
               preferredDuringSchedulingIgnoredDuringExecution:
               -  preference:
                    matchExpressions:
                    - key: zone
                      operator: In
                      values:
                      - foo
                      - bar
                  weight: 60
    
    # 此为软亲和,若node上有zone=foo 或 zone=bar,则优先调度上去,若全部都没有,勉强选一个调度上去也不是不可以

    为啥有了节点亲和性,还要有Pod亲和性?
      假如我们有一个分布式跨地域的大集群,现在想构建一个NMT的架构,为了实现这个目的,我完全可以给Node上打标签,然后,让NMT这些Pod都匹配这些标签,然后被调度到这些Node上去运行,这不是也可以实现我们的目的吗?
      看上去没有问题,但问题是,假如你跨地域的分布式K8s集群,分别分布在北京,上海,深圳,杭州等地,你就不得不精心规划不同地域,不同IDC,不同机房,不同机柜的Node标签规划,否则你很可能在将来实现这个目的是变得困难不堪,你可以想象一下,你希望让一组NMT运行在北京某IDC的某机房中,这样一个NMT架构在工作时,才能更加高效的通信,你想实现这样的控制,你不规划Node的标签,你让预选策略如何按照你的想法去调度那?这还仅是一方面,你为了NMT架构能分离开,北京一套,上海一套.....你也要配置Pod的在选择Node标签时,设定它亲和哪些标签,所以工作量你可自己评估。

      Pod亲和性的逻辑时,我要部署NMT环境,我的MySQL Pod第一个被调用,我不管调度器会把MySQL Pod调度到哪里,反正只要调度完成,并且运行了,我后续的Nginx,Tomcat的Pod是亲和MySQL Pod的,MySQL Pod在哪个Node上,那在调度NT时,就更加优先调度到那个Node上。
      这样说看上去很简单,但仔细想想,MySQL被调度到某Node上,要是将NT也调度到那个Node上合适吗?若那个Node没有这么多资源运行它们怎么办?能不能将NT调度到M所在Node旁边的Node上,或同机房的Node中?那这算亲和吗? 其实也算,但怎么知道运行MySQL Pod的Node ,它旁边的Node是否那个主机?这其实还是需要借助于Node标签来实现,因此为集群标签的规划是在所难免的,因为你必须给预选策略一种判断标准,哪些是相同机柜,哪些是不同机房对吧,否则鬼知道集群中那么多Node那个和那个是邻居对吧,当然若你靠主机名来判断也不是不可以,你就要定义根据主机名的判断标准了。所以,比较通用的方法是,给每个Node打上不同的标签,如:北京机房1 机柜20 机架号等等来定义,这样根据这些标签值,就可以判断Node和Node之间的临近关系了。
      打个比方: MySQL Pod被调度到 rack=bjYZ1 ,那后续调度NT时,就会优选rack=bjYZ1的Node,只要有这个标签值得Node会被优选调度,这样NMT它们就都可以运行在北京亦庄的机房中了。

      kubectl pods.spec.affinity.podAffinity.
      requiredDuringSchedulingIgnoredDuringExecution : 硬限制
      labelSelector: 指定亲和Pod的标签
      namespaces: 你要指定亲和Pod,你就需要指定你亲和的Pod属于哪个名称空间,否则默认是Pod创建在那个名称空间,就匹配那个名称空间的Pod

    Pod亲和性示例:
    vim  pod-required-affinity.yaml
      apiVersion:  v1
      kind:  Pod
      metadata:
       name:  web1-first
       labels:
         app:  web1
         tier:  frontend
     spec:
       containers:
       -  name:  myapp
          image:  harbor.zcf.com/k8s/myapp:v1
     
     ---
     apiVersion:  v1
     kind:  Pod
     metadata:
      name:  db1-second
      labels:
         app:  db1
         tier:  db1
    spec:
       containers:
       - name:  busybox
         image:  busybox:  latest
         imagePullPolicy:  IfNotPresent
         command:  ["sh", "-c", "sleep  3600"]
       affinity:
          podAffinity:     #若需要测试Pod的反亲和性,只需要修改podAffinity为 podAntiAffinity 即可测试
            requiredDuringSchedulingIgnoredDuringExecution:
            -  labelSelector:
                  matchExpressions:
                   -  {key: app,  operator: In,  values: ["web1","web2"]}
               topologyKey:  kubernetes.io/hostname   #通过默认Node上主机名标签来临时代表地区标签,即:只有主机名相同,就认为是在相同位置的。
        
    
    #Pod反亲和测试:
    Pod亲和性示例:
    vim  pod-required-affinity.yaml
      apiVersion:  v1
      kind:  Pod
      metadata:
        name:  web1-first
        labels:
          app:  web1
          tier:  frontend
     spec:
       containers:
       - name:  myapp
         image:  harbor.zcf.com/k8s/myapp:v1
     
    ---
    apiVersion:  v1
    kind:  Pod
    metadata:
       name:  db1-second
       labels:
          app:  db1
          tier:  db1
    spec:
      containers:
      - name:  busybox
        image:  busybox:  latest
        imagePullPolicy:  IfNotPresent
        command:  ["sh", "-c", "sleep  3600"]
        affinity:
          podAntiAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
            -  labelSelector:
                 matchExpressions:
                   -   {key: app,  operator: In,  values: ["web1","web2"]}
                 topologyKey:  kubernetes.io/hostname        
                 #第一次测试: 使用存在的标签来测试,web1会运行在一个节点上,db1一定不会运行在web1所在的Node上。
                 topologyKey:  zone     
                     #第二次测试: 使用此key,因为两个Node上都没有此标签key,因此预选策略将认为,没有找到不同区域的Node,因此db1将处于Pinding状态,无法启动。
    vim  myapp-toleration.yaml
      apiVersion: apps/v1
      kind: Deployment
      metadata:
           name: myapp-toler1
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: myapp
            release: canary
        template:
          metadata:
             labels:
               app: myapp
               release: canary
          spec:
            containers:
            -  name: myapp
               image: ikubernetes/myapp:v2
               ports:
               -  name: http
                  containerPort: 80
            tolerations:
            -  key: "node-type"
               operator: "Equal"
               value: "production"
               effect: "NoSchedule"
      #说明: 在定义Pod的容忍度时,若指定为NoSchedule,则不能定义tolerationSeconds,即容忍不了时,可宽限多久被驱逐。
      #      若定义为NoExecute 则可以定义tolerationSeconds。
      #     Equal:做等值比较,即node-type标签 和 其值必须完全匹配。
      #     Exists :做存在性比较,即只要node-type标签存在,即可匹配,不看其标签值是否相同。
    
      #测试NoExecute影响
      #目前node2.zcf.com 上定义了node-type污点的影响为: NoExecute
         ............
         tolerations:
         -  key:  "node-type"
             operator:  "Exists"
             value:  ""             #设置node-type的值为空,因为Exists是做标签判断,只要node-type标签存在,即可.
             effect:  "NoSchedule"  
     #定义污点影响度为NoSchedule,即若Node上没有这个node-type污点标签,就不调度到其上.
     #若Node上污点影响(effect)为NoExecute,它是不包含NoSchedule的,即Node是不允许不能容忍NoExecute的Pod调度到自己上面的。
    
    
        #若Pod能容忍node-type标签的污点,无论它是什么值,什么影响,都可以容忍
        ...............
        tolerations:
        -   key:  "node-type"
          operator:  "Exists"
          value:  ""
          effect:   ""
    
    
     #删除节点上的污点:
         kubectl  taint  node   node01.zcf.com  node-type-   
             #注意:node-type是:node01上的标签,最后的“-”是删除该标签的意思。
  • 相关阅读:
    hadoop安装前的准备
    记录一次Qt5程序无法运行的解决过程
    C#里的Thread.Join与Control.Invoke死锁情况
    qbxt7月笔记
    zhxのDP讲
    有n*m的方格图
    最长上升子序列相关问题笔记
    qbxt游记(清北澡堂划水记
    DAZの七下道法(持续更新
    模板
  • 原文地址:https://www.cnblogs.com/wn1m/p/11290733.html
Copyright © 2011-2022 走看看