22.资源调度器与调度机制

前言

在Kubernetes中,调度是指确保Pod与Node节点的匹配度,以便Kubelet可以运行它们。

1、调度概述

在kubernetes集群的概念中,调度主要是做两件事情:

  • 监听新创建,且未被分配至任何node节点的pod资源
  • 根据机身集群的计算资源、规则、匹配度等情况,为新的pod资源进行调度

2、调度流程

调度流程

如图所示,调度流程可分为两大块:

Filtering和Scoring

1、Filtering

容器调度 开启cpuset 容器资源调度_优先级

预选阶段,也称为predicate。
会按照预选过滤器,首先把不符合要求的node节点直接剔除在外。
这一阶段为可行性筛选。
通常,会有不止一个。 如果筛选列表为空,则该Pod尚无法完成调度。

如图所示,经过Filtering阶段,node01和node03已经标红并剔除在外。

常用的预选过滤器,我整理了一个表格供大家参考,所有过滤器可移步Filtering大全

预选过滤器名

说明

PodFitsHostPorts

检查node节点是否有可用端口

PodFitsHost

检查pod中是否通过hostname定义了node

PodFitsResources

检查标签选择器和node节点标签是否匹配

NoVolumeZoneConflict

检查节点上Pod请求的卷是否可用

NoDiskConflict

检查pod请求的volume是否已经挂载

MaxCSIVolumeCount

评估应该新增多少个CSI卷,以及是否超出配置的限制

CheckNodeMemoryPressure

如果节点处于内存压力,不会将pod调度至此

CheckNodePIDPressure

如果节点pid稀缺,不会将pod调度至此

CheckNodeDiskPressure

如果节点磁盘存在压力,不会将pod调度至此

CheckNodeCondition

检查节点是否具备调度的基础条件,如文件系统是否可用,网络是否可用或者kubelet是否就绪,否则不会将pod调度至此

PodToleratesNodeTaints

检查pod的容忍度是否能容忍节点的污点

CheckVolumeBinding

评估Pod是否适合其所需的容量,这适用于绑定和未绑定的PVC

2、Scoring

打分阶段,也称为priority。
通过优选函数对节点进行打分,从而决定调度策略。
这一阶段为优选阶段,筛选哪个节点更优。

如图所示,经过Scoring阶段,只剩下node04和node06了。

常用的优选过滤器,我也整理了一个表格供大家参考,所有过滤器可移步Scoring大全。

优选过滤器名

说明

SelectorSpreadPriority

标签优先级计算

InterPodAffinityPriority

pod亲和度计算

LeastRequestedPriority

倾向于调度至请求较少、资源更空闲的节点上

MostRequestedPriority

倾向于调度至请求较多、资源更紧凑的节点上

RequestedToCapacityRatioPriority

使用默认资源打分函数进行优选计算

BalancedResourceAllocation

倾向于调度至资源平衡的节点

NodePreferAvoidPodsPriority

通过scheduler.alpha.kubernetes.io/preferAvoidPods注释,避免pod间调度至同一个node节点

NodeAffinityPriority

node亲和度计算

TaintTolerationPriority

根据节点上无法忍受的污点数量,为所有节点准备优先级列表

ImageLocalityPriority

倾向于本地已有pod缓存的节点

ServiceSpreadingPriority

对于给定服务,此策略旨在确保该服务的Pod在不同的节点上运行

CalculateAntiAffinityPriorityMap

此策略有助于实现pod的反亲和力

EqualPriorityMap

所有节点给定一个相同的权重

3、InterPodAffinityPriority与NodeAffinityPriority

在优选过滤器中,我提到过两个过滤器InterPodAffinityPriority和NodeAffinityPriority,他们是十分重要的,常常也会理解出现偏差。
1、InterPodAffinityPriority

我们称之为pod亲和度,pod对pod的亲和性,表示是否愿意与相关pod调度至一个区域(可以是node、机架、也可以是机房)

如何定义同一区域,则需要使用topologyKey(v1.16特性)进行标识,下一章节我会着重讲解的。

同理,还会有pod反亲和,这个你应该很好理解了。

除此之外,pod亲和度分为以下两种:

  • 硬亲和(required):使用requiredDuringSchedulingIgnoredDuringExecution字段定义,如果条件都不满足,则不调度,Pod对象的状态会一直是Pending状态
  • 软亲和(preferred):使用preferredDuringSchedulingIgnoredDuringExecution字段定义,如果条件都不满足,也会从中按照打分,“勉为其难”的选择一个进行调度

pod亲和度的API文档:

kubectl explain pod.spec.affinity.podAffinity

pod反亲和度的API文档:

kubectl explain pod.spec.affinity.podAntiAffinity

2、NodeAffinityPriority

我们称之为node亲和度,pod对node的亲和性,表示是否愿意调度到某个node上,其也细分为以下两种:

  • 硬亲和(required):使用requiredDuringSchedulingIgnoredDuringExecution字段定义,如果条件都不满足,则不调度,Pod对象的状态会一直是Pending状态
  • 软亲和(preferred):使用preferredDuringSchedulingIgnoredDuringExecution字段定义,如果条件都不满足,也会从中按照打分,“勉为其难”的选择一个进行调度

node亲和度的api文档:

kubectl explain pod.spec.affinity.nodeAffinity

4、示例:PodAffinity

上面,我提到过pod亲和度的定义,我们再来回顾一下:

pod亲和度,pod对pod的亲和性,表示是否愿意与相关pod调度在一个区域(可以是node、机架、也可以是机房),如何定义同一区域,则需要使用topologyKey(v1.16特性)进行标识。

我们先一起来看一下这个例子:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-with-preferred-pod-affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
    spec:
      affinity:
        podAffinity:           #pod亲和性
          preferredDuringSchedulingIgnoredDuringExecution:    #软亲和
          - weight: 80
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["db"]}
              topologyKey: rack
          - weight: 20
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - {key: app, operator: In, values: ["db"]}
              topologyKey: zone
      containers:
      - name: myapp
        image: nginx

上面这个例子表示:

1、定义了一个pod软亲和:preferredDuringSchedulingIgnoredDuringExecution
2、软亲和条件有2个,app标签中是否存在值为db的pod资源,但优先度不同;前者80,后者只有20
3、同一位置指标的定义为topologyKey,一个为rack,另一个为zone

可能你还是有点困惑,这里对于调度的情况我再来归纳一下:

  • 该deployment定义了3个pod,其更愿意调度至存在app=db标签的pod的同一个机柜(rack),其次之选则是会选择调度至存在app=db标签的pod的同一个区域(zone)。
  • 此时他们对同一位置的解释是截然不同的,现在你是否理解了呢?

5、示例:NodeAffinity

这里,我们也再次回顾一下概念,增强记忆。

node亲和度,pod对node的亲和性,表示是否愿意调度到某个node上,也细分为硬亲和和软亲和。

我们现在直接来看下面这个例子,其中:

1、定义了一个软亲和:preferredDuringSchedulingIgnoredDuringExecution
2、表示最期望调度至zone=foo的节点上
3、次期望调度至存在ssd标签的节点

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy-with-node-affinity
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp-pod
      labels:
        app: myapp
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:         #软亲和
          - weight: 60
            preference:
              matchExpressions:
              - {key: zone, operator: In, values: ["foo"]}
          - weight: 30
            preference:
              matchExpressions:
              - {key: ssd, operator: Exists, values: []}
      containers:
      - name: myapp
        image: nginx

6、表达式的优先级辨析

我们在实际使用过程中,很可能会使用多个表达式来限制调度的期望,可能是多个标签的过滤、也可能是多个表达式的拼接。
那么这些表达式的优先级是怎么样的呢,如果你没搞明白直接操作,会带来意想不到的后果的!(不建议)

我们先来看一个demo:

apiVersion: v1
kind: Pod
metadata:
  name: required-nodeAffinity-pod-or
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:           #硬亲和
        nodeSelectorTerms:
        - matchExpressions:               #matchExpressions级别是或关系(or)
          - {key: zone, operator: In, values: ["foo"]}   我们是(and)与关系,需要都满足
          - {key: zone, operator: In, values: ["bar"]}   我们是(and)与关系,需要都满足
        - matchExpressions:               #matchExpressions级别是或关系(or)
          - {key: ssd, operator: Exists, values: []}
  containers:
  - name: myapp
    image: nginx
    resources:
      requests:
        cpu: 6
        memory: 20Gi

你可以看到,这是一个多条件的硬亲和例子,对于其中的优先级是这样的:

key级别:是与关系(and)
matchExpressions级别:是或关系(or)