调度器

优良的调度是分布式系统的核心。Scheduler调度器做为Kubernetes三大核心组件之一, 承载着整个集群资源的调度功能,其根据特定调度算法和策略,将Pod调度到最优工作节点上,从而更合理与充分的利用集群计算资源,使资源更好的服务于业务服务的需求。

随着业务服务不断Docker化与微服务化,Kubernetes集群规模不断的扩大,而Kubernetes调度器作为集群的中枢系统,在如何提高集群的底层计算资源利用率、保证集群中服务的稳定运行中也就变得尤为重要。

调度流程

kube-scheduler的根本工作任务是根据各种调度算法将Pod绑定(bind)到最合适的工作节点,整个调度流程分为两个阶段:预选策略(Predicates)和优选策略(Priorities)。

预选(Predicates):输入是所有节点,输出是满足预选条件的节点。kube-scheduler根据预选策略过滤掉不满足策略的Nodes。例如,如果某节点的资源不足或者不满足预选策略的条件如“Node的label必须与Pod的Selector一致”时则无法通过预选。

优选(Priorities):输入是预选阶段筛选出的节点,优选会根据优先策略为通过预选的Nodes进行打分排名,选择得分最高的Node。例如,资源越富裕、负载越小的Node可能具有越高的排名。

值得一提的是,如果在预选阶段没有节点满足条件,Pod会一直处在Pending状态直到出现满足的节点,在此期间调度器会不断的进行重试。

Pod的整个启动流程总结:
  1. 资源管控中心Controller Manager创建新的Pod,将该Pod加入待调度的Pod列表。
  2. kube-scheduler通过API Server提供的接口监听Pods,获取待调度pod,经过预选和优选两个阶段对各个Node节点打分排序,为待调度Pod列表中每个对象选择一个最优的Node。
  3. kube-scheduler将Pod与Node的绑定写入etcd(元数据管理服务)。
  4. 节点代理服务kubelet通过API Server监听到kube-scheduler产生的绑定信息,获得Pod列表,下载Image并启动容器,然后由kubelet负责拉起Pod。

预选策略(Predicates)

1.基于存储卷数量的判断

  • MaxEBSVolumeCount:确保已挂载的EBS存储卷数量不超过设置的最大值(默认39),调度器会检查直接或及间接使用这种类型存储的PVC,累加总数,如果卷数目超过设最大值限制,则不能调度新Pod到这个节点上。
  • MaxGCEPDVolumeCount:同上,确保已挂载的GCE存储卷数量不超过预设的最大值(默认16)。
  • MaxAzureDiskVolumeCount:同上,确保已挂载的Azure存储卷不超过设置的最大值(默认16)。

2.基于资源压力状态的判断

  • CheckNodeMemoryPressure:判断节点是否已经进入到内存压力状态,如果是则只允许调度内存为0标记的Pod。
  • CheckNodeDiskPressure:判断节点是否已经进入到磁盘压力状态,如果是,则不能调度新的Pod。

3.基于卷冲突的判断

  • NoDiskConflict:卷冲突判断,即如果该节点已经挂载了某个卷,其它同样使用相同卷的Pod将不能再调度到该节点。
  • NoVolumeZoneConflict:对于给定的某块区域,判断如果在此区域的节点上部署Pod是否存在卷冲突。
  • NoVolumeNodeConflict:对于某个指定节点,检查如果在此节点上部署Pod是否存在卷冲突。

4.基于约束关系的判断

  • MatchNodeSelector:检查节点标签(label)是否匹配Pod指定的nodeSelector,是则通过预选。
  • MatchInterPodAffinity:根据Pod之间的亲和性做判断。
  • PodToleratesNodeTaints:排斥性关系,即判断Pod不允许被调度到哪些节点。这里涉及到两个概念Taints(污点)和Toleration(容忍)。Node可以定义一或多个Taint,Pod可以定义一或多个Toleration,对于具有某个Taint的节点,只有遇到能容忍它的(即带有对应Toleration的)Pod,才允许Pod被调度到此节点,从而避免Pod被分配到不合适的节点。

5.基于适合性的判断

  • PodFitsResources:检查节点是否有足够资源(如CPU、内存、GPU等)满足Pod的运行需求。
  • PodFitsHostPorts:检查Pod容器所需的HostPort是否已被节点上其它容器或服务占用。如果已被占用,则禁止Pod调度到该节点。
  • PodFitsHost:检查Pod指定的NodeName是否匹配当前节点。

优选策略(Priorities)

优选过程会根据优选策略对每个候选节点进行打分,最终把Pod调度到分值最高的节点。kube-scheduler用一组优先级函数处理每个通过预选的节点,每个函数返回0-10的分数,各个函数有不同权重,最终得分是所有优先级函数的加权和

优选的优先级函数包括:

  • LeastRequestedPriority(默认权重1):尽量将Pod调度到计算资源占用比较小的Node上,这里涉及两种计算资源:内存和CPU。计算公式如下:其中,capacity表示该节点的现有容量,requested表示Pod所请求的容量。

  • BalancedResourceAllocation(默认权重1):CPU和内存使用率越接近的节点权重越高。该策略均衡了节点CPU和内存的配比,尽量选择在部署Pod后各项资源更均衡的机器。该函数不能单独使用,必须和LeastRequestedPriority同时使用,因为如果请求的资源(CPU或者内存)大于节点的capacity,那么该节点永远不会被调度到。计算公式如下:

  • SelectorSpreadPriority(默认权重1):把属于同一个Service或者ReplicationController的Pod,尽量分散在不同的节点上,如果指定了区域,则尽量把Pod分散在该区域的不同节点。通常来说节点上已运行的Pod越少,节点分数越高。计算公式如下,是基于节点的计算和基于区域的计算的加权和,其中,maxPriority代表系数,默认为10,maxCount为节点最多允许运行的Pod数量,nodeCount为该节点已经存在的Pod数量,maxCountByZone为该区域最多允许的Pod数量,zoneCount为区域内已经运行的Pod数量。

  • NodeAffinityPriority(默认权重1):尽量调度到标签匹配Pod属性要求的节点,判断行为与预选中的MatchNodeSelector相似,未来可能会完全将其取代。

该函数提供两种选择器:requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution。前者是强要求,指定将Pod调度到节点上必须满足的所有规则;后者是弱要求,即尽可能调度到满足特定限制的节点,但允许不满足。计算公式如下,在节点满足requiredDuringSchedulingIgnoredDuringExecution后,该节点会累加preferredDuringSchedulingIgnoredDuringExecution中所有满足条件的规则的权值weight_i,CountWeight为preferredDuringSchedulingIgnoredDuringExecution中所有规则的权值总和:

  • InterPodAffinityPriority(默认权重1):叠加该节点已调度的每个Pod的权重,权重可配置文件里设定,通常亲和性越强的Pod权重越高,如果Pod满足要求则将加到权重和,具有最高权重和的节点是最优的。提供两种选择器:requiredDuringSchedulingIgnoredDuringExecution(保证所选的主机必须满足所有Pod对主机的规则要求)、preferredDuringSchedulingIgnoredDuringExecution(调度器会尽量但不保证满足NodeSelector的所有要求)。

计算公式如下,其中,weight_i为节点上符合亲和性的每个Pod的权重,sumCount为节点上符合亲和性的Pod权重和,maxCount为节点上所有Pod的权重和,minCount为节点上最小的Pod权重,maxPriority是系数,默认为10。

  • NodePreferAvoidPodsPriority(默认权重10000):避免将ReplicationController或ReplicaSet调度到节点。如果Pod由RC或者RS的Controller调度,则得分为0,不对最终加权得分产生影响;如果不是,则总分为100000,意味着覆盖其他策略,直接决定最优节点。

  • TaintTolerationPriority(默认权重1):Pod与Node的排斥性判断。通过Pod的tolerationList与节点Taint进行匹配,配对失败的项越少得分越高,类似于Predicates策略中的PodToleratesNodeTaints。计算公式如下:其中,totalTaints表示Taint总个数,intolerableTaints表示配对不成功的个数。

  • ImageLocalityPriority(默认权重1):尽量调度到Pod所需镜像的节点。检查Node是否存在Pod所需镜像:如果不存在,返回0分;如果存在,则镜像越大得分越高。计算公式如下:其中,sumSize表示该节点上存在的Pod所需镜像大小总和,maxImgSize表示Pod所需镜像总大小,minImgSize表示Pod所需最小镜像的尺寸。

  • EqualPriority(默认权重1):给予所有节点相等权重,一般仅用于测试。

  • MostRequestedPriority(默认权重1):适用于动态伸缩集群环境,会优先调度Pod到使用率最高的节点,方便在伸缩集群时,先腾出空闲机器,从而进行停机处理。

调度方式案例展示:

调度方式总结为:

  1. 节点选择器(nodeSelector)
  2. 节点亲和调度(nodeAffinity)
  3. pod亲和调度 (podAffinity、podAntiAffinity)
  4. 污点调度
节点选择器(nodeSelector)
apiVersion: v1
kind: Pod
metadata: 
  name: pod-demo
  labels:
    app: myapp
    tier: frontent
spec:
  containers:
  - name: myapp
    image: docker.io/busybox
    imagePullPolicy: "IfNotPresent"
    command: ["sh","-c","sleep 3600"]
  nodeSelector:
    disktype: harddisk #指定该pod调度到有disktype=harddisk 标签的node节点上


$kubectl apply -f pod-demo.yaml
   pod "pod-demo" created
   $kubectl get pods
   NAME                                           READY     STATUS    RESTARTS   AGE
   pod-demo                                       0/1       Pending   0          1m  #由于集群所以节点上都没有disktype=harddisk标签,所以pod为“Pending”状态
$kubectl label node k8s-b disktype=harddisk     #为node打标签

$kubectl get pods -o wide
NAME                                           READY     STATUS    RESTARTS   AGE       IP            NODE
pod-demo                                       1/1       Running   0          7m        10.244.1.75   k8s-b  #可见pod被成功调度
$kubectl label node k8s-b disktype-  #去处node标签
node "k8s-b" labeled
亲和性调度

preferredDuringSchedulingIgnoreDuringExecution:软亲和性;不管节点上能否满足设定条件,Pod都可以被调度,只是Pod优先被调度到符合条件多的节点上。

- preference:##与相应权重相关联的节点选择器项。
    matchExpressions:##按节点标签列出的节点选择器要求列表
    - key:     ##键
      operator:##表示键与一组值的关系。有效的运算符有:In、NotIn、Exist、DoesNotExsit。GT和LT
      values:  ##值;若operator为In或NotIn则值必须为非空;若operator为Exists或DoesNotExist则值必须为空;若operator为Gt或Lt则值必须有一个元素。
    matchFields:##按节点字段列出的节点选择器要求列表
    - key:     ##键
      operator:##表示键与一组值的关系。有效的运算符有:In、NotIn、Exist、DoesNotExsit。GT和LT
      values:  ##值;若operator为In或NotIn则值必须为非空;若operator为Exists或DoesNotExist则值必须为空;若operator为Gt或Lt则值必须有一个元素。
  weight:      ##权重,0~100的数值

requiredDuringSchedulingIgnoreDuringExecution: 硬亲和性;节点必须满足设定条件,Pod才能被调度到这个节点。

 nodeSelectorTerms: ##节点选择器列表
  - matchExpressions:##按节点标签列出的节点选择器要求列表
    - key:     ##键
      operator:##表示键与一组值的关系。有效的运算符有:In、NotIn、Exist、DoesNotExsit。GT和LT
      values:  ##值;若operator为In或NotIn则值必须为非空;若operator为Exists或DoesNotExist则值必须为空;若operator为Gt或Lt则值必须有一个元素。
    matchFields:##按节点字段列出的节点选择器要求列表
    - key:     ##键
      operator:##表示键与一组值的关系。有效的运算符有:In、NotIn、Exist、DoesNotExsit。GT和LT
      values:  ##值;若operator为In或NotIn则值必须为非空;若operator为Exists或DoesNotExist则值必须为空;若operator为Gt或Lt则值必须有一个元素。

example 节点亲和调度(nodeAffinity):
硬亲和

apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeaffinity-demo
      namespace: default
    spec:
      containers:
      - name: app
        image: docker.io/nginx:latest
        imagePullPolicy: "IfNotPresent"
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:  ## 硬亲和性
            nodeSelectorTerms:
            - matchExpressions:
              - key: zone  ## 检查有zone标签的节点
                operator: In
                values: ["foo","bar"]  ## zone标签的值是foo 或 bar

软亲和

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-demo2
  labels:
    app: myapp
    tier: fronttend
spec:
  containers:
  - name: myapp
    image: docker.io/nginx:latest
    imagePullPolicy: "IfNotPresent"
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:  ## 软亲和
      - preference:
          matchExpressions:
          - key: zone  ## 即使任何节点都没有zone标签,也会找个节点让Pod运行起来。
            operator: In
            values: ["foo", "bar"]
        weight: 60

example pod亲和调度 (podAffinity):

表示一组pod更倾向与运行在一起,一般需要高效通信时需要将pod与pod运行在相近的位置。
第一个pod被调度到某一个节点,第二个pod会跟随第一个pod运行在同一个位置相近的节点上。
同一个位置的节点不一定是同一个节点,这个节点可能是同一个命名空间、网段、机柜、机房、甚至同一个区域的某一个节点上。
因为如果说,同一个节点上的资源不足时,pod被调度不到同一个节点,那么就会找与这个节点相近的某个节点上。
节点相近是指节点到节点的网络路程较近。
当然,若资源空间充足也可以实现让第二个Pod跟随第一个pod运行在同一个节点上。
可以根据topologyKey设定的标签来判定拥有哪些标签的节点属于同一位置

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  labels:
    app: myapp
    tier: fround
spec:
  containers:
  - name: myapp
    image: docker.io/nginx:latest
    imagePullPolicy: "IfNotPresent"
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp2
  labels:
    app: myapp
    tier: backend
spec:
  containers:
  - name: myapp2
    image: docker.io/nginx:latest
    imagePullPolicy: "IfNotPresent"
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:  #硬亲和性
      - labelSelector:
          matchExpressions:  ## #匹配pod的,而不是匹配节点
          - key: app   # 标签名
            operator: In  # 表示键与一组值的关系。有效的运算符有:In、NotIn、Exist、DoesNotExsit。GT和LT
            values:  # 若operator为In或NotIn则值必须为非空;若operator为Exists或DoesNotExist则值必须为空;若operator为Gt或Lt则值必须有一个元素。
            - "myapp"  # 标签值
        topologyKey: kubernetes.io/hostname   #节点标签(选取包含kubernetes.io/hostname标签的节点)
反亲和性podAntiAffinity
apiVersion: v1
kind: Pod
metadata:
  name: myapp-first
  labels:
    app: myapp
    tier: fround
spec:
  containers:
  - name: myapp
    image: docker.io/nginx:latest
    imagePullPolicy: "IfNotPresent"
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp-second
  labels:
    app: myapp
    tier: backend
spec:
  containers:
  - name: myapp2
    image: docker.io/nginx:latest
    imagePullPolicy: "IfNotPresent"
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - {key: app, operator: In, values: ["myapp", "foo"]}
        topologyKey: kubernetes.io/hostname  ## pod调度策略是podAntiAffinity反亲和性,所以myapp-first和myapp-second不能同时运行在标有kubernetes.io/hostname标签的节点上。
污点容忍度调度:Taint、toleration(nodes.spec.taints)(pod.spec.tolerations)

污点就是定义在节点上的键值属性数据:三类:1.标签。2.注解。3.污点。
其中,标签和注解可以在节点或pod上使用,而污点只能在节点上使用。
污点主要是让节点拒绝那些不能容忍自己污点的pod的。
而pod上可以定义容忍度,只要是节点上的污点是pod的容忍度的子集,则pod就可以运行在这个节点上。
taints是用在节点上的键值数据定义污点的,toleration是用在pod上的键值数据定义容忍度的。
污点:taints(nodes.spec.taints)
在节点上定义污点:

 nodes.spec.taints.
           - effect:  ##当pod不能容忍污点时采取的措施是什么。
               NoSchedule:  ##仅影响调度过程,对现存的pod不产生影响;
               PreferNoSchedule:  ##柔性版的NoSchedule
               NoExecute:   ##不仅影响调度,而且影响现存pod对象,不容忍的pod对象将被驱逐;
             key:      ##键
             timeAdded:
             value:    ##值

添加污点命令:kubectl taint node NODE_NAME KEY_1=VAL_1:TAINT_EFFECT_1 ... KEY_N=VAL_N:TAINT_EFFECT_N [options]
example: ]# kubectl taint node node1 node-type=production:NoSchedule
删除污点命令:kubectl taint node NODE_NAME KEY_NAME-

容忍度:toleration(pod.spec.tolerations)
在pod中定义容忍度:

pod.spec.tolerations.
         - effect:  ##同上
           key:     ##同上
           value:   ##同上
           operator:##有效的运算符是Exists、Equal。默认为Equal。这样pod就可以容忍某一特定类别的污点
           tolerationSeconds:##若不能容忍污点,多长时间后被驱逐。

example:

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  containers:
  - name: myapp
    image: docker.io/nginx
    imagePullPolicy: "IfNotPresent"
  tolerations:
  - effect: "NoSchedule"
    key: "node-type"
    operator: "Equal"
    value: "production"

example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: canary
  template:
    metadata:
      labels:
        app: myapp
        release: canary
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
      tolerations: 
        - key: "node-type"
          operator: "Exists" #表示只要节点匹配key: node-type,不管其value是什么,pod都能容忍节点上的污点
          value: ""
          effect: "NoSchedule"

参考文档:
https://www.cnblogs.com/kcxg/p/11119679.html
https://www.cnblogs.com/Smbands/p/10949478.html
http://blog.itpub.net/28916011/viewspace-2215522/

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐