1. 简介

Scheduler 是 Kubernetes 的调度器,主要任务是把定义的Pod分配到集群的节点上,要考虑以下几点:

  • 公平:如何保证每个节点都能分配到资源;
  • 资源高效利用:集群所有资源最大化被使用;
  • 效率:调度性能要好,能够尽快的对大批量的Pod完成调度工作;
  • 灵活:允许用户根据自己的需求控制调度的流程;

Scheduler 是作为单独的服务运行的,启动之后会一直监听API Server,获取podSpec.NodeName 为空的 Pod,对每个 Pod 都会创建一个buiding,表明该 Pod 应该放在哪个节点上。


2. 调度过程

  1. 首先过滤掉不满足条件的节点,这个过程称为 predicate;
  2. 然后对通过的节点按照优先级的顺序,这个是 priority;
  3. 最后从中选择优先级最高的节点。
  4. 如果中间有任何一步报错,则直接返回错误信息。

2.1 Predicate算法:

  • PodFitsResources: 节点剩余资源是否大于Pod请求的资源
  • PodFitsHost: 如果Pod指定了nodeName,检查节点名称是否和nodeName匹配
  • PodFitsHostPort: 节点上已经使用的port是否和Pod申请的port冲突
  • PodSelectorMatches: 过滤和Pod指定的label不匹配的节点
  • NoDiskConfict: 已经mount的volume和Pod指定的volume不冲突,除非它们都是只读

如果在predicate过程中没有适合的节点,Pod会一直处理Pending状态,不断重新调度,直到有节点满足条件,经过这个步骤,如果有多节点满足条件,就会进入priority过程;

priority过程, 按照优先级大小对节点排序,优先级由一系列键值对组成,键是该优先级的名称,值是它的权重,优先级选项包括:

  • LeastRequestedPriority: 通过计算CPU和Memory的使用率来决定权重,使用率越低权重越高
  • BalanceResourceAllocation: 节点上CPU和Memory使用率的相近程度,比如:Node1的CPU和Memory使用率一个是48%一个50%,Node2的CPU和Memory使用率都是50%,则选择Node2
  • ImageLocalityPriority: 倾向于已经要使用镜像的节点,镜像的总大小值越大,权重越高

3. 自定义调度器

除了Kubernetes自带的调度器,也可以编写自己的调度器,通过spec.schedulername参数指定调度器的名字,可以为Pod选择某个调度器进行调度。

比如下边的Pod选择my-scheduler进行调度,而不是默认的default-scheduler

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: scheduler-test
  5. labels:
  6. name: example-scheduler
  7. spec:
  8. schedulername: my-scheduler
  9. containers:
  10. - name: Pod-test
  11. image: nginx:v1

Kubernetes的调度方式有以下几种:

  • 亲和性:包括Node的亲和性和Pod的亲和性
  • 污点(Taint)和容忍(Toleration)
  • 固定调度策略

4. 亲和性

4.1 节点(Node)亲和性

pod.spec.affinity.nodeAffinity

  • preferredDuringSchedulingIgnoreDuringExecution: 软策略

    • 软策略是偏向于,更想(不)落在某个节点上,但如果没有,落在其他节点也可以
  • requiredDuringSchedulingIgnoredDuringExecution: 硬策略

    • 硬策略是必须(不)落在指定的节点上,如果不符合条件,则一直处于Pending状态

4.1.1 硬策略-演示

apiVersion: v1
kind: Pod
metadata:
  name: affinity-required
  labels:
    app: node-affinity-pod
spec:
  containers:
  - name: with-node-required
    image: nginx:1.2.1
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoreDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname     // 节点名称
            operator: NotIn     // 不是
            values:
            - testcentos7       // node节点名称

以上策略表示:此Pod不要落在node名称为testcentos7的节点上

4.1.2 软策略-演示

apiVersion: v1
kind: Pod
metadata:
  name: affinity-required
  labels:
    app: node-affinity-pod
spec:
  containers:
  - name: with-node-required
    image: nginx:1.2.1
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoreDuringExecution:
      - weight: 100     // 权重为100,软策略中权重越高匹配到机会越大
        preference:     // 更偏向于
          matchExpressions:
          - key: kubernetes.io/hostname
            operator: In
            values:
            - testcentos7

以上策略表示:此Pod更想落在node节点为testcentos7的node中

4.1.3 软硬策略合体

apiVersion: v1
kind: Pod
metadata:
  name: affinity-required
  labels:
    app: node-affinity-pod
spec:
  containers:
  - name: with-node-required
    image: nginx:1.2.1
    imagePullPolicy: IfNotPresent
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoreDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/hostname
            operator: NotIn
            values:
            - k8s-node2
      preferredDuringSchedulingIgnoreDuringExecution:
      - weight: 1
        preference:
        matchExpressions:
        - key: source
          operator: In
          values:
          - hello

软硬结合达到一个更为准确的node选择,以上文件意思为此Pod必须不存在k8s-node2节点中,其他的节点都可以,但最好落在label中source的值为hello的节点中

4.1.4 键值运算关系

  • In: label的值在某个列表里
  • NotIn: label的值不在某个列表中
  • Gt: label的值大于某个值
  • Lt: label的值小于某个值
  • Exists: 某个label存在
  • DoseNotExist: 某个label不存在

注意:如果nodeSelectorTerms下面有多个选项,满足任何一个条件就可以了;如果matchExpressions有多个选项,则必须满足这些条件才能正常调度

4.2 Pod亲和性

pod.spec.affinity.podAffinity/podAntiAffinity

  • preferredDuringSchedulingIgnoreDuringExecution: 软策略

    • 软策略是偏向于,更想(不)落在某个节点上,但如果没有,落在其他节点也可以
  • requiredDuringSchedulingIgnoredDuringExecution: 硬策略

    • 硬策略是必须(不)落在指定的节点上,如果不符合条件,则一直处于Pending状态

4.2.1 Pod硬策略

apiVersion: v1
kind: Pod
metadata:
  name: affinity-required
  labels:
    app: pod-3
spec:
  containers:
  - name: with-pod-required
    image: nginx:1.2.1
    imagePullPolicy: IfNotPresent
  affinity:
    podAffinity:        // 在同一域下
    requiredDuringSchedulingIgnoreDuringExecution:
    - labelSelector:
        matchExpressions:
        - key: app        // 标签key
          operator: In
          values:
          - nginx         // 标签value
      topologyKey: kubernetes.io/hostname   // 域的标准为node节点的名称

以上文件策略为:此Pod必须要和包含label为app:nginx的pod在同一node下

将podAffinity改为podAnitAffinity,使它们不再用于node节点下

apiVersion: v1
kind: Pod
metadata:
  name: required-pod2
  labels:
    app: pod-3
spec:
  containers:
  - name: with-pod-required
    image: nginx:1.2.1
    imagePullPolicy: IfNotPresent
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - nginx
        topologyKey: kubernetes.io/hostname

此策略表示,必须要和label为app:nginx的pod在不用的node节点上

4.2.2 Pod软策略

apiVersion: v1
kind: Pod
metadata:
  name: affinity-required
  labels:
    app: pod-3
spec:
  containers:
  - name: with-pod-required
    image: nginx:1.2.1
    imagePullPolicy: IfNotPresent
  affinity:
    podAntiAffinity:        // 不在同一域下
    preferredDuringSchedulingIgnoreDuringExecution:
    - weight: 1
      podAffinityTerm:
        labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - pod-2   
      topologyKey: kubernetes.io/hostname

软策略和硬策略的方法基本类似,只是添加了权重,表示更喜欢而已,也可以接受其他。

4.2 亲和性/反亲和性调度策略比较:

调度策略 匹配标签 操作符 拓扑域支持 调度目标
nodeAffinity 主机 In, NotIn, Exists, DoseNotExists, Gt, Lt 指定主机
podAffinity Pod In, NotIn, Exists, DoseNotExists, Gt, Lt pod与指定pod在一拓扑域
podAnitAffinity Pod In, NotIn, Exists, DoseNotExists, Gt, Lt pod与指定pod不在一拓扑域

5. 污点(Taint)和容忍(Toleration)

节点亲和性,是Pod的一种属性(偏好或硬性要求),它使Pod被吸引到一类特定的节点,Taint则相反,它使节点能够 排斥 一类特定的Pod。

Taint与Toleration相互配合,可以用来避免Pod被分配到不合适的节点上,每个节点上都可以应用一个或两个taint,这表示对那些不能容忍这些污点的pod是不会被该节点接受的,如果将toleration应用于pdo,则表示这些pod可以(但不要求)被调度到具有匹配taint节点上。

5.1 污点(Taint)

5.1.1 污点的组成

使用kubectl taint命令可以给某个node节点设置污点,Node被设置上污点之后就和Pod之间存在了一种相斥的关系,可以让Node拒绝Pod的调度执行,甚至将已经存在的Pod驱逐出去

每个污点的组成如下:

key=value:effect

每个污点有一个key和value作为污点标签,其中value可以为空,effect描述污点的作用,当前effect支持如下三个选项:

  • NoSchedule: 表示k8s不会将Pod调度到具有该污点的Node上;
  • PreferNoSchedule:表示k8s将尽量避免将Pod调度到具有该污点的Node上;
  • NoExecute:表示k8s将不会将Pod调度到具有该污点的Node上,同时会将Node上已有的Pod驱逐出去

5.1.2 污点的设置、查看和去除

k8s的master节点本身就带有effect类型为NoSchedule的污点;具体查看:

kubectl describe node centos8

Taints:   node-role.kubernetes.io/master:NoSchedule

设置污点:

kubectl taint nodes [node name] key1=value:NoSchedule

去除污点:

kubectl taint nodes [node name] key1:NoSchedule-

5.2 容忍(Toleration)

设置了污点的Node将根据taint的effect:NoSchedule、PreferNoSchedule、NoExecute和Pod之间产生互斥的关系,Pod将在一定程度上不会被调度到Node上。但我们可以在Pod上设置容忍(Toleration),该Pod将可以在有相对应污点的Node中存在。

容忍的策略规则如下:

Pod.spec.tolerations

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
  tolerationSeconds: 3600
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
- key: "key2"
  operator: "Exists"
  effect: "NoSchedule"
  • 其中key、value、effect要与Node中的taint保持一致
  • operator是的值为Exists将会忽略value的值,值为Equal时则必须匹配相同的value
  • tolerationSeconds用于描述当Pod需要驱逐时可以在Node上继续保留运行的时间
  1. 当不指定key时,表示容忍所有污点的key:
tolerations:
- operator: "Exists"
  effect: "NoSchedule"
  1. 当不指定effect值时,表示容忍所有的污点类型
tolerations:
- key: "key1"
  value: "value1"
  operator: "Exists"