Kubernetes Pod调度说明 


简介

Scheduler 是 Kubernetes 的调度器,主要任务是把定义的Pod分配到集群的节点上,听起来非常简单,但要考虑需要方面的问题:

  • 公平:如何保证每个节点都能被分配到资源

  • 资源高效利用:集群所有资源最大化被使用

  • 效率:调度性能要好,能够尽快的对大批量的Pod完成调度工作

  • 灵活:允许用户根据自己的需求控制调度的流程

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

调度过程


调度流程:

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

Predicate有一系列的算法可以使用:

  • PodFitsResources:节点上剩余的资源是否大于 Pod 请求的资源

  • PodFitsHost:如果Pod指定了nodeName,检查节点名称是否和nodeName匹配

  • PodFitsHostPort:节点上已经使用的port是否和Pod申请的port冲突

  • PodSelectorMatches:过滤和Pod指定的 label 不匹配的节点

  • NoDiskConflict:已经 mount 的 volume 和 Pod 指定的volume不冲突,除非他们都是只读

如果在predicate过程中没有适合的节点,Pod会一直处于Pending状态,不断重新调度,直到有节点满足条件,经过这个步骤,如果多个节点满足条件,就会进入priority过程:按照优先级大小对节点排序,优先级由一系列键值对组成,键是该优先级的名称,值是它的权重,这些优先级选项包括:

  • LeastRequestedPriority:通过计算CPU和Memory的使用率来决定权重,使用率越低权重越高,换句话说,这个优先级倾向于资源使用率低的节点

  • BalanceResourceAllocation:节点上CPU和Memory使用率非常及接近,权重就越高,这个要和上边的一起使用,不可单独使用

  • ImageLocalityPriority:倾向于已经要使用镜像的节点,镜像的总大小值越大,权重越高

通过算法对所有的优先级项目和权重进行计算,得出最终的结果

自定义调度器

除了Kubernetes自带的调度器,也可以编写自己的调度器,通过spec.schedulername参数指定调度器的名字,可以为Pod选择某个调度器进行调度,比如下边的Pod选择my-scheduler进行调度,而不是默认的default-scheduler。

apiVersion: v1
kind: Pod
metadata:
  name: scheduler-test
  labels:
    name: example-scheduler
spec:
  schedulername: my-scheduler
  containers:
  - name: Pod-test
    image: nginx:v1

如何满足 Pod 与 Node 关系调度


Pod 与 Node 的关系调度又称之为 Node 亲和调度,主要给大家介绍两类使用方法。

NodeSelector

第一类是 NodeSelector,这是一类相对比较简单的玩法。比如说有个场景:必须要调度 Pod 到带了 k1: v1 标签的 Node 上,这时可以在 Pod 的 spec 中填写一个 nodeSelector 要求。

nodeSelector 其实是一个 map 结构,里面可以直接写上对 node 标签的要求,比如 k1: v1。这样我的 Pod 就会强制调度到带了 k1: v1 标签的 Node 上。

NodeAffinity

NodeSelector 是一个非常简单的玩法,但这个玩法有个问题:它是一个常规性调度,假如我想优先调度,就没法用 nodeSelector 来做。于是 Kubernetes 社区又新加了一个玩法,叫做 NodeAffinity。

它和 PodAffinity 有点类似,也提供了两类调度的策略:

  • 第一类是 required,必须调度到某一类 Node 上;
  • 第二类是 preferred,就是优先调度到某一类 Node 上。

它的基本语法和上文中的 PodAffinity 以及 PodAntiAffinity 也是类似的。在 Operator 上,NodeAffinity 提供了比 PodAffinity 更丰富的 Operator 内容。增加了 Gt 和 Lt,数值比较的玩法。当使用 Gt 的时候,values 只能填写数字。

调度亲和性  节点(Node)亲和性


之前的nodeselector只能是等于某个标签,同时如果标签不匹配就会处于pending状态,这就需要软性的条件,上面是两块都加上了意味着pod只能放置在标签具有gpu的节点上,并且值为nvdlia-tesla,另外更加倾向于标签group的节点

这里的权重值代表更加期望。

pod.spec.affinity.nodeAffinity

  • preferredDuringSchedulingIgnoredDuringExecution:软策略

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

  • requiredDuringSchedulingIgnoredDuringExecution:硬策略

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

 如果你还不太理解打个比方:

今天要分班了,我更加倾向于去张三老师的班级。这就是所谓的亲和性。如果我是Pod,张三老师所在的班级为Node,这就是所谓的节点亲和性。一种是必须要去张三老师带的班级(硬状态),还有一种是想去张三老师的班级但是不是必须的(软状态)。这就是一个软和硬的状态。软策略是想去,但是不去也可以,硬策略是非去不可。

requiredDuringSchedulingIgnoredDuringExecution硬策略

[root@k8s-master ~]# cat node-affinity-required.yaml 
apiVersion: v1
kind: Pod
metadata:  
  name: affinity  
  labels:   
    app: node-affinity-pod
spec:  
  containers:  
  - name: with-node-affinity    
    image: nginx  
    imagePullPolicy: IfNotPresent
  affinity:    
    nodeAffinity:      
      requiredDuringSchedulingIgnoredDuringExecution:        
        nodeSelectorTerms:       
        - matchExpressions:         
          - key: kubernetes.io/hostname        #键为节点名称       
            operator: NotIn                    #不是
            values:            
            - k8s-master                       #node节点名称
          - key: kubernetes.io/hostname        #键为节点名称
            operator: NotIn                    #不是
            values:
            - k8s-node1                        #node节点名称
[root@k8s-master ~]# kubectl apply -f node-affinity-required.yaml 
pod/affinity created、



      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:   #硬亲和
            nodeSelectorTerms:
            - matchExpressions:
              - key: gpu
                operator: Exists

以上策略表示:此Pod不要落在node名称为k8s-master和k8s-node1的节点上。

可以看到每个节点上都有一个kubernetes.io/hostname=xxxx,这里就是典型的标签的键值和键值

[root@k8s-master ~]# kubectl get node --show-labels
NAME         STATUS   ROLES    AGE   VERSION   LABELS
k8s-master   Ready    <none>   48d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux
k8s-node1    Ready    <none>   48d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2    Ready    <none>   48d   v1.18.3   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux
[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE         NOMINATED NODE   READINESS GATES
affinity                 1/1     Running   0          3m46s   10.244.2.6    k8s-node2    <none>    

可以看到这就是节点的硬策略,硬亲和性。

将yaml文件中,NotIn改为In

  affinity:    
    nodeAffinity:      
      requiredDuringSchedulingIgnoredDuringExecution:        
        nodeSelectorTerms:       
        - matchExpressions:         
          - key: kubernetes.io/hostname            
            operator: In            
            values:            
            - k8s-node3

[root@k8s-master ~]# kubectl apply -f node-affinity-required.yaml 
pod/affinity created
[root@k8s-master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
affinity                 0/1     Pending   0          14s

[root@k8s-master ~]# kubectl describe pod affinity
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.

目前只有三个节点,一个master 一个node1一个node2,策略中表示此Pod在node3这个节点上,Pod创建之后,因为并不存在node3节点,所以一直处于Pending状态(因为这是一个硬策略,如果不满足就不运行了)。

preferredDuringSchedulingIgnoredDuringExecution软策略

[root@k8s-master ~]# cat node-affinity-preferred.yaml 
apiVersion: v1
kind: Pod
metadata:  
  name: affinity-preferred  
  labels:   
    app: node-affinity-pod
spec:  
  containers:  
  - name: with-node-preferred    
    image: nginx  
    imagePullPolicy: IfNotPresent
  affinity:    
    nodeAffinity: 
      preferredDuringSchedulingIgnoredDuringExecution:     
      - weight: 100        #权重为100,软策略中权重越高匹配到的机会越大  
        preference:        #更偏向于  
          matchExpressions:          
          - key: kubernetes.io/hostname  #node名称
            operator: In   #等于,为      
            values:            
            - k8s-node1    #node真实名称
[root@k8s-master ~]# kubectl apply -f node-affinity-preferred.yaml 
pod/affinity-preferred created




      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:  #软亲和
          - weight: 60
            preference:
              matchExpressions:  #带gpu标签的加60权重
              - key: gpu
                operator: Exists
          - weight: 30
            preference:
              matchExpressions: #包含foo、bar标签的加30权重
              - key: region
                operator: In
                values: ["foo","bar"]

 - weight: 100这里给了权重,如果这里有多个软策略的话,软策略1和小红坐一起,那么这个权重为4 。策略2想和小丽做一起的权重为1。当有小红的情况下和小红坐一起的权重更高,所以选择小红,权重更大更加亲和,如果小红不在那么就选择小丽。

以上策略表示:此Pod更想落在node节点名称为k8s-node1的node中。

[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
affinity-preferred       1/1     Running   0          55s   10.244.1.6    k8s-node1    <none>           <none>

以上正常落在node1节点下。

更改一下策略,将node节点名称随便更改为不存在的node名称,例如k8s-node3

  affinity:    
    nodeAffinity: 
      preferredDuringSchedulingIgnoredDuringExecution:     
      - weight: 100        
        preference:          
          matchExpressions:          
          - key: kubernetes.io/hostname
            operator: In            
            values:            
            - k8s-node3

[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE         NOMINATED NODE   READINESS GATES
affinity-preferred       1/1     Running   0          4m25s   10.244.0.17   k8s-master   <none>           <none>

[root@k8s-master ~]# kubectl describe pod affinity-preferred 
Events:
  Type    Reason     Age        From                 Message
  ----    ------     ----       ----                 -------
  Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/affinity-preferred to k8s-master
  Normal  Pulling    2m         kubelet, k8s-master  Pulling image "nginx"
  Normal  Pulled     82s        kubelet, k8s-master  Successfully pulled image "nginx"
  Normal  Created    82s        kubelet, k8s-master  Created container with-node-preferred
  Normal  Started    82s        kubelet, k8s-master  Started container with-node-preferred

创建后,虽然它更想落在node3节点上,但因为没有这个节点,只好落在master节点中(软就是有我就去,没有也行,硬就是必须要有,没有就不去了)。

软硬策略合体

软硬可以在一起使用,先要满足硬策略以后才能去满足软策略。

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

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

键值运算关系

  • In:label 的值在某个列表里

  • NotIn:label 的值不在某个列表中

  • Gt:label 的值大于某个值

  • Lt:label 的值小于某个值

  • Exists:某个 label 存在

  • DoesNotExist:某个 label 不存在

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

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

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

调度策略匹配标签操作符拓扑域支持调度目标
nodeAffinity主机In,NotIn,Exists,DoesNotExists,Gt,Lt指定主机
podAffinityPodIn,NotIn,Exists,DoesNotExists,Gt,Ltpod与指定pod在一拓扑域
podAnitAffinityPodIn,NotIn,Exists,DoesNotExists,Gt,Ltpod与指定pod不在一拓扑域
[root@master ~]# kubectl label node node1 node-role.kubernetes.io/pg=
node/node1 labeled

[root@master ~]# kubectl get node 
NAME     STATUS   ROLES       AGE   VERSION
master   Ready    master      60d   v1.19.8
node1    Ready    pg,worker   60d   v1.19.8
node2    Ready    worker      60d   v1.19.8


[root@master ~]# kubectl label node node1  node-role.kubernetes.io/worker= node-role.kubernetes.io/worker=
error: 'node-role.kubernetes.io/worker' already has a value (), and --overwrite is false

设置节点亲和性,先打标签

[root@bots-hrx-ksm1 kubectl]# kubectl get node
NAME             STATUS   ROLES       AGE   VERSION
bots-hrx-ksw14   Ready    pg,worker   15h   v1.19.8
bots-hrx-ksw15   Ready    pg,worker   15h   v1.19.8
bots-hrx-ksw16   Ready    pg,worker   15h   v1.19.8

node-role.kubernetes.io/pg
node-role.kubernetes.io/worker

最后调整将所有组件放在一起


    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: node-role.kubernetes.io/pg
                    operator: In
                    values:
                      - ''
      tolerations:
        - key: node-role.kubernetes.io/pg
          operator: Exists

Logo

开源、云原生的融合云平台

更多推荐