Kubernetes Scheduler调度详解

1.Kubernetes Scheduler简介

Kubernetes 调度器(Scheduler)是Kubernetes的核心组件;用户或者控制器创建Pod之后,调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod。调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。调度器会依据下文的调度原则来做出调度选择。

kube-scheduler 给一个 pod 做调度选择包含两个步骤:过滤、打分

过滤阶段:会将所有满足 Pod 调度需求的 Node 选出来。例如,PodFitsResources 过滤函数会检查候选 Node 的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个 Node 列表,里面包含了所有可调度节点;通常情况下,这个 Node 列表包含不止一个 Node。如果这个列表是空的,代表这个 Pod 不可调度。

 
打分阶段:调度器会为 Pod 从所有可调度节点中选取一个最合适的 Node,根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。最后,kube-scheduler 会将 Pod 调度到得分最高的 Node 上。如果存在多个得分最高的 Node,kube-scheduler 会从中随机选取一个。

 

我们可以使用多种规则比如:

  • 设置cpu、内存的使用要求;
  • 增加node的label,并通过pod.Spec.NodeSelector进行强匹配;
  • 直接设置pod的nodeName,跳过调度直接下发。

注:k8s 1.2加入了一个实验性的功能:affinity。意为亲和性。这个特性的设计初衷是为了替代nodeSelector,并扩展更强大的调度策略。

 

 

2.Scheduler工作原理

  • 节点预选(Predicate):排除完全不满足条件的节点,如内存大小,端口等条件不满足。
  • 节点优先级排序(Priority):根据优先级选出最佳节点
  • 节点择优(Select):根据优先级选定节点

Kubernetes Scheduler调度详解

  1. 首先用户通过 Kubernetes 客户端 Kubectl 提交创建 Pod 的 Yaml 的文件,向Kubernetes 系统发起资源请求,该资源请求被提交到

  2. Kubernetes 系统中,用户通过命令行工具 Kubectl 向 Kubernetes 集群即 APIServer 用 的方式发送“POST”请求,即创建 Pod 的请求。

  3. APIServer 接收到请求后把创建 Pod 的信息存储到 Etcd 中,从集群运行那一刻起,资源调度系统 Scheduler 就会定时去监控 APIServer

  4. 通过 APIServer 得到创建 Pod 的信息,Scheduler 采用 watch 机制,一旦 Etcd 存储 Pod 信息成功便会立即通知APIServer,

  5. APIServer会立即把Pod创建的消息通知Scheduler,Scheduler发现 Pod 的属性中 Dest Node 为空时(Dest Node=””)便会立即触发调度流程进行调度。

  6. 而这一个创建Pod对象,在调度的过程当中有3个阶段:节点预选、节点优选、节点选定,从而筛选出最佳的节点

    • 节点预选:基于一系列的预选规则对每个节点进行检查,将那些不符合条件的节点过滤,从而完成节点的预选

    • 节点优选:对预选出的节点进行优先级排序,以便选出最合适运行Pod对象的节点

    • 节点选定:从优先级排序结果中挑选出优先级最高的节点运行Pod,当这类节点多于1个时,则进行随机选择

 

3.Pod调度

Kubernetes中,Pod通常是容器的载体,一般需要通过Deployment、DaemonSet、RC、Job等对象来完成一组Pod的调度与自动控制功能。

3.1 Depolyment/RC自动调度

Deployment或RC的主要功能之一就是自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维持用户指定的副本数量。
示例
[root@uk8s-m-01 study]# vi nginx-deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment-01
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

[root@uk8s-m-01 study]# kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment-01   3/3     3            3           30s
[root@uk8s-m-01 study]# kubectl get rs
NAME                             DESIRED   CURRENT   READY   AGE
nginx-deployment-01-5754944d6c   3         3         3       75s
[root@uk8s-m-01 study]# kubectl get pod | grep nginx
nginx-deployment-01-5754944d6c-hmcpg   1/1     Running     0          84s
nginx-deployment-01-5754944d6c-mcj8q   1/1     Running     0          84s
nginx-deployment-01-5754944d6c-p42mh   1/1     Running     0          84s

 

3.2 NodeSelector定向调度

当需要手动指定将Pod调度到特定Node上,可以通过Node的标签(Label)和Pod的nodeSelector属性相匹配。

# kubectl label nodes <node-name> <label-key>=<label-value>
node节点创建对应的label后,可通过在定义Pod的时候加上nodeSelector的设置实现指定的调度。
示例
[root@uk8s-m-01 study]# kubectl label nodes 172.24.9.14 speed=io
node/172.24.9.14 labeled
[root@uk8s-m-01 study]# vi nginx-master-controller.yaml
kind: ReplicationController
metadata:
  name: nginx-master
  labels:
    name: nginx-master
spec:
  replicas: 1
  selector:
    name: nginx-master
  template:
    metadata:
      labels:
        name: nginx-master
    spec:
      containers:
      - name: master
        image: nginx:1.7.9
        ports:
        - containerPort: 80
      nodeSelector:
        speed: io

[root@uk8s-m-01 study]# kubectl create -f nginx-master-controller.yaml
[root@uk8s-m-01 study]# kubectl get pods -o wide
NAME                READY   STATUS    RESTARTS    AGE    IP            NODE
nginx-master-7fjgj  1/1     Running   0           82s    172.24.9.71   172.24.9.14
提示:可以将集群中具有不同特点的Node贴上不同的标签,实现在部署时就可以根据应用的需求设置NodeSelector来进行指定Node范围的调度。
注意:若在定义Pod中指定了NodeSelector条件,但集群中不存在符合该标签的Node,即使集群有其他可供使用的Node,Pod也无法被成功调度。

 

3.3 NodeAffinity亲和性调度

亲和性调度机制极大的扩展了Pod的调度能力,主要增强功能如下:
  • 更具表达力,即更精细的力度控制;
  • 可以使用软限制、优先采用等限制方式,即调度器在无法满足优先需求的情况下,会使用其他次条件进行满足;
  • 可以依据节点上正在运行的其他Pod的标签来进行限制,而非节点本身的标签,从而实现Pod之间的亲和或互斥关系。
 
目前有两种节点亲和力表达:
  • requiredDuringSchedulingIgnoredDuringExecution:硬规则,必须满足指定的规则,调度器才可以调度Pod至Node上(类似nodeSelector,语法不同)。
  • preferredDuringSchedulingIgnoredDuringExecution:软规则,优先调度至满足的Node的节点,但不强求,多个优先级规则还可以设置权重值。
  • IgnoredDuringExecution指:如果一个Pod所在的节点在Pod运行期间标签发生了变化,不再符合该Pod的节点亲和性需求,则系统将忽略Node上Label的变化,该Pod能继续在该节点运行。
示例:条件1:只运行在amd64的节点上;尽量运行在ssd节点上。
 
[root@uk8s-m-01 study]# vi nodeaffinity-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/arch
            operator: In
            values:
            - amd64
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  containers:
  - name: with-node-affinity
    image: gcr.azk8s.cn/google_containers/pause:2.0
NodeAffinity操作语法;In、NotIn、Exists、DoesNotExist、Gt、Lt。NotIn和DoesNotExist可以实现互斥功能。
NodeAffinity规则设置注意事项:
  • 若同时定义nodeSelector和nodeAffinity,则必须两个条件都满足,Pod才能最终运行指定在Node上;;
  • 若nodeAffinity指定多个nodeSelectorTerms,则只需要其中一个能够匹配成功即可;
  • 若nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod。

 

3.4 PodAffinity亲和性调度

PodAffinity根据节点上正在运行的Pod标签而不是Node标签来判断和调度,要求对节点和Pod两个条件进行匹配。
规则描述为:若在具有标签X的Node上运行了一个或多个符合条件Y的Pod,则Pod应该(或者不应该)运行在这个Node上。
X通常为Node节点的机架、区域等概念,Pod是属于某个命名空间,所以条件Y表达的是一个或全部命名空间中的一个Label Selector。
Pod亲和性定义与PodSpec的affinity字段下的podAffinity字段里,互斥性定义于同一层次的podAntiAffinity子字段中。
举例:
[root@uk8s-m-01 study]# vi nginx-flag.yaml    #创建名为pod-flag,带有两个标签的Pod
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: gcr.azk8s.cn/google_containers/pause:2.0
[root@uk8s-m-01 study]# vi nginx-affinity-in.yaml    #创建定义标签security=S1,对应如上Pod “Pod-flag”。
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: kubernetes.io/hostname
  containers:
  - name: with-pod-affinity
    image: gcr.azk8s.cn/google_containers/pause:2.0

[root@uk8s-m-01 study]# kubectl create -f nginx-affinity-in.yaml
[root@uk8s-m-01 study]# kubectl get pods -o wide

Kubernetes Scheduler调度详解

提示:由上Pod亲和力可知,两个Pod处于同一个Node上。

 
[root@uk8s-m-01 study]# vi nginx-affinity-out.yaml    #创建不能与参照目标Pod运行在同一个Node上的调度策略
apiVersion: v1
kind: Pod
metadata:
  name: anti-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - nginx
        topologyKey: kubernetes.io/hostname
  containers:
  - name: anti-affinity
    image: gcr.azk8s.cn/google_containers/pause:2.0

[root@uk8s-m-01 study]# kubectl get pods -o wide    #验证

 

3.5 Taints和Tolerations(污点和容忍)

Taint:使Node拒绝特定Pod运行;
Toleration:为Pod的属性,表示Pod能容忍(运行)标注了Taint的Node。
Taint语法:$ kubectl taint node node1 key=value:NoSchedule
解释:为node1加上一个Taint,该Taint的键为key,值为value,Taint的效果为NoSchedule。即除非特定声明可以容忍此Taint,否则不会调度至node1上。
Toleration示例:
  1 tolerations:
  2 - key: "key"
  3   operator: "Equal"
  4   value: "value"
  5   effect: "NoSchedule"

  1 tolerations:
  2 - key: "key"
  3   operator: "Exists"
  4   effect: "NoSchedule"
注意:Pod的Toleration声明中的key和effect需要与Taint的设置保持一致,并且满足以下条件:
  • operator的值是Exists(无须指定value);
  • operator的值是Equal并且value相等;
  • 空的key配合Exists操作符能够匹配所有的键和值;
  • 空的effect匹配所有的effect。
 
若不指定operator,则默认值为Equal。
taint说明:系统允许在同一个Node上设置多个taint,也可以在Pod上设置多个toleration。Kubernetes调度器处理多个taint和toleration的逻辑顺序:首先列出节点中所有的taint,然后忽略pod的toleration能够匹配的部分,剩下的没有忽略掉的taint就是对pod的效果。以下是几种特殊情况:
  • 若剩余的taint中存在effect=NoSchedule,则调度器不会把该Pod调度到这一节点上;
  • 若剩余的taint中没有NoSchedule效果,但有PreferNoSchedule效果,则调度器会尝试不把这个Pod指派到此节点;
  • 若剩余taint的效果有NoSchedule,并且这个Pod已经在该节点上运行,则会被驱逐,若没有在该节点上运行,也不会再被调度到该节点上。
示例:
$ kubectl taint node node1 key=value1:NoSchedule
$ kubectl taint node node1 key=value1:NoExecute
$ kubectl taint node node1 key=value2:NoSchedule
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
释义:此Pod声明了两个容忍,且能匹配Node1的taint,但是由于没有能匹配第三个taint的toleration,因此此Pod依旧不能调度至此Node。若该Pod已经在node1上运行了,那么在运行时设置了第3个taint,它还能继续在node1上运行,这是因为Pod可以容忍前两个taint。
通常,若node加上effect=NoExecute的taint,那么该Node上正在运行的所有无对应toleration的Pod都会被立刻驱逐,而具有相应toleration的Pod则永远不会被驱逐。同时,系统可以给具有NoExecute效果的toleration加入一个可选的tolerationSeconds字段,表明Pod可以在taint添加到Node之后还能在此Node运行多久。 
tolerations:
- key: "key1"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
  tolerationSeconds: 3600

 释义:若Pod正在运行,所在节点被加入一个匹配的taint,则这个pod会持续在该节点运行3600s后被驱逐。若在此期限内,taint被移除,则不会触发驱逐事件。

Taints和Tolerations常用场景:
  • 独占节点:
给特定的节点运行特定应用。
$ kubectl taint nodes 【nodename】 dedicated=groupName:NoSchedule
同时在Pod中设置对应的toleration配合,带有合适toleration的Pod允许同时使用其他节点一样使用有taint的节点。
  • 具有特殊硬件设备的节点
集群中部分特殊硬件(如安装了GPU),则可以把不需要占用GPU的Pod禁止在此Node上调度。
 
  1 $ kubectl taint nodes 【nodename】 special=true:NoSchedule
  2 $ kubectl taint nodes 【nodename】 special=true:PreferNoSchedule
  • 定义Pod驱逐行为
NoExecute的taint对节点上正在运行的Pod有以下影响:
    • 没有设置toleration的pod会被立刻驱逐;
    • 配置了对应toleration的pod,若没有为tolerationSeconds赋值,则会一直保留在此节点中;
    • 配置了对应toleration的pod,且为tolerationSeconds赋值,则在指定时间后驱逐。
 

Kubernetes Scheduler调度详解

上一篇:When are you supposed to use escape instead of encodeURI / encodeURIComponent?


下一篇:监听中sqlnet.ora的作用