k8s调度策略总结

社区K8s云原生

0.前言

在k8s中,调度是指调度器将pod放置到合适的node节点上,之后node节点上的kubelet就可以运行该pod了。调度器通过监测机制来发现集群中新创建且未被调度到节点的pod,并且调度器会分析node现有的资源,以及用户定义的调度策略,然后在执行调度,以此保证pod的正常运行。

1.kube-scheduler描述

kube-scheduler是k8s集群中默认的调度器,是集群控制面的重要部分,kube-scheduler做调度时通常需要考虑的因素有:单独和整体的资源请求、硬件/软件/策略限制、 亲和以及反亲和要求、数据局部性、负载间的干扰等等。
kube-scheduler给一个Pod做调度通常包括两个步骤:
(1)过滤
(2)打分
过滤阶段会将所有满足 Pod 调度需求的节点选出来。例如,PodFitsResources 过滤函数会检查候选节点的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个节点列表,里面包含了所有可调度节点。通常情况下,这个节点列表包含不止一个节点。如果这个列表是空的,代表这个 Pod 不可调度。
在打分阶段,调度器会为 Pod 从所有可调度节点中选取一个最合适的节点。 根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。
最后,kube-scheduler 会将 Pod 调度到得分最高的节点上。如果存在多个得分最高的节点,kube-scheduler 会从中随机选取一个。
kube-scheduler支持以下两种方式配置调度器的过滤和打分行为:
(1)调度策略 允许你配置过滤所用的 断言(Predicates) 和打分所用的 优先级(Priorities);
(2)调度配置 允许你配置实现不同调度阶段的插件, 包括:QueueSort、Filter、Score、Bind、Reserve、Permit 等等。 接下来,我们看下如何基于污点、亲和性和反亲和性等策略将pod调度到指定的node上。

2.node节点选择

2.1 指定node名称

nodeSelector提供了一种非常简单的方式将pod调度到指定的node,我们来测试一下。 (1)创建声明文件

cd /data/affinity/node
cat node-name-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-node
  labels:
    app: nginx-node
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-node
  template:
    metadata:
      labels:
        app: nginx-node
    spec:
      nodeName: k8s-node1
      containers:
      - name: nginx
        image: docker.m.daocloud.io/library/nginx
        ports:
        - containerPort: 8090

(2)创建deployment

kubectl apply -f node-name-test.yaml

(3)查看pod的运行情况

kubectl get pod -o wide|grep nginx

picture.image

可以看到pod运行在了k8s-node1节点上。

2.2 nodeSelector方式

(1)创建声明文件

cd /data/affinity/node
cat node-selector-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-selector-test
  labels:
    app: nginx-selector-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-selector
  template:
    metadata:
      labels:
        app: nginx-selector
    spec:
      nodeSelector:
        app/env: dev
      containers:
      - name: nginx-selector-pod
        image: docker.m.daocloud.io/library/nginx
        ports:
        - containerPort: 8090

(2)给node打标签

kubectl label nodes k8s-node2 app/env=dev

(3)创建deployment

kubectl apply -f node-selector-test.yaml

(4)查看pod的运行情况

kubectl get pod -o wide|grep nginx

picture.image

可以看到pod运行在了k8s-node2节点上。

3.node亲和性

在k8s集群中,目前有两种类型的节点亲和性:
(1)requiredDuringSchedulingIgnoredDuringExecution:硬亲和性,也即node必须满足条件pod才可以调度到该node上;
(2)preferredDuringSchedulingIgnoredDuringExecution:软亲和性,pod会优先调度到满足条件的node上,如果找不到满足条件的,也可以调度到其他节点。
节点亲和性语法支持以下运算符:In,NotIn,Exists,DoesNotExist,Gt,Lt。
运算符含义:
In:label的值在某个列表中;
NotIn:label的值不在某个列表中;
Gt:label的值大于某个值;
Lt:label的值小于某个值;
Exists:某个label存在;
DoesNotExist:某个label不存在。
可以使用NotIn和DoesNotExist实现节点的反亲和行为。 下面用几个实例来验证一下node的亲和性。

3.1 准备工作

(1)给node节点添加标签,用来标明节点的存储特性和cpu核数

kubectl label nodes k8s-node1 disk-type=ssd --overwrite
kubectl label nodes k8s-node1 cpu-num=12
kubectl label nodes k8s-node2 disk-type=sata
kubectl label nodes k8s-node2 cpu-num=24

(2)查看node标签

kubectl get node -o wide --show-labels

picture.image 可以看到,标签添加成功。

3.2 node硬亲和性

(1)创建声明文件:

cd /data/affinity/node
cat node_required_affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-deploy
  labels:
    app: node-affinity-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: node-affinity-pod
  template:
    metadata:
      labels:
        app: node-affinity-pod
    spec:
      containers:
      - name: node-affinity-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              # 表示node标签存在 disk-type=ssd 或 disk-type=sas
              - key: disk-type
                operator: In
                values:
                - ssd
                - sas
              # 表示node标签存在 cpu-num且值大于6
              - key: cpu-num
                operator: Gt
                values:
                - "6"

(2)创建deployment

kubectl apply -f node-required-affinity.yaml

(3)查看pod运行情况

kubectl get pod -o wide|grep affinity

picture.image 可以看到所有的pod都运行在k8s-node1上,因为只有node1满足pod调度的硬性条件。

3.3 node软亲和性

(1)创建声明文件

cd /data/affinity/node
cat node-required-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-deploy
  labels:
    app: node-affinity-deploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: node-affinity-pod
  template:
    metadata:
      labels:
        app: node-affinity-pod
    spec:
      containers:
      - name: node-affinity-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              # 表示node标签存在 disk-type=ssd 或 disk-type=sas
              - key: disk-type
                operator: In
                values:
                - ssd
                - sas
          - weight: 50
            preference:
              matchExpressions:
              # 表示node标签存在 cpu-num且值大于16
              - key: cpu-num
                operator: Gt
                values:
                - "16"

(2)创建deployment

kubectl apply -f node-preferre-affinity.yaml

(3)查看pod运行情况

kubectl get pod -o wide |grep affinity

picture.image 可以看到pod优先选择运行在k8s-node2上面了,但当k8s-node2出现资源不够等情况时,pod也是可以调度到k8s-node1的。

3.4 node软硬亲和性

(1)创建声明文件

cd /data/affinity/node
cat node-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-deploy
  labels:
    app: node-affinity-deploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: node-affinity-pod
  template:
    metadata:
      labels:
        app: node-affinity-pod
    spec:
      containers:
      - name: node-affinity-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              # 表示node标签存在 cpu-num且值大于10
            - matchExpressions:
              - key: cpu-num
                operator: Gt
                values:
                - "10"
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 50
            preference:
              matchExpressions:
                # 表示node标签存在 disk-type=ssd 或 disk-type=sas
              - key: disk-type
                operator: In
                values:
                - ssd
                - sas

(2)创建deployment

kubectl apply -f node-affinity.yaml

(3)查看pod运行情况

kubectl get pod -o wide |grep affinity

picture.image 根据声明文件的定义,k8s-node1和k8s-node2两个节点都是满足调度要求的,但是pod会优先调度到k8s-node1上。

4.pod亲和性/反亲和性示例

与节点亲和性一样,Pod亲和性/反亲和性都有两种类型,称为requiredDuringSchedulingIgnoredDuringExecution和 preferredDuringSchedulingIgnoredDuringExecution,分别表示“硬”与“软”要求。对于硬要求,如果不满足则pod会一直处于Pending状态。Pod的亲和性与反亲和性是基于Node节点上已经运行pod的标签(而不是节点上的标签)决定的,用于控制哪些节点必须部署在一起,哪些节点不能部署在一起。
Pod亲和性/反亲和性语法支持以下运算符:
(1)In
(2)NotIn
(3)Exists
(4)DoesNotExist

4.1 准备工作

(1)删除之前的标签

kubectl label nodes k8s-node1 cpu-num-
kubectl label nodes k8s-node1 disk-type-
kubectl label nodes k8s-node2 cpu-num-
kubectl label nodes k8s-node2 disk-type-

(2)创建新标签

kubectl label nodes k8s-master business=www
kubectl label nodes k8s-master disk-type=ssd
kubectl label nodes k8s-master db=redis

kubectl label nodes k8s-node1 business=www
kubectl label nodes k8s-node1 disk-type=sata
kubectl label nodes k8s-node1 db=redis

kubectl label nodes k8s-node2 business=www
kubectl label nodes k8s-node2 disk-type=ssd
kubectl label nodes k8s-node2 db=etcd

(3)查看node标签

kubectl get node -o wide --show-labels

picture.image 可以看到三个节点的标签都创建完成了。

4.2 pod硬亲和性

(1)创建一个带标签pod的声明文件

cd /data/affinity/pod
cat pod-proxy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: proxy
  labels:
     app: proxy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: proxy
  template:
    metadata:
      labels:
        app: proxy
        version: v1
    spec:
      containers:
      - name: proxy-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80

(2)创建deployment

kubectl apply -f pod-proxy.yaml

(3)查看proxy pod运行情况

kubectl get pod -o wide --show-labels |grep proxy

picture.image 可以看到proxy运行在k8s-node2上,并且带有标签app=proxy,version=v1

(4)硬亲和性deployment声明文件

cd /data/affinity/pod
cat pod-required-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-deploy
  labels:
    app: pod-affinity-deploy
spec:
  replicas: 6
  selector:
    matchLabels:
      app: pod-affinity-pod
  template:
    metadata:
      labels:
        app: pod-affinity-pod
    spec:
      containers:
      - name: pod-affinity-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              # 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
              matchExpressions:
              - key: app
                operator: In
                values:
                - proxy
            # 拓扑域  若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
            # 请对比如下两个不同的拓扑域,Pod的调度结果
            topologyKey: disk-type

(6)创建deployment

kubectl apply -f pod-required-affinity.yaml

(7)查看pod运行情况

kubectl get pod -o wide |grep affinity

picture.image 可以看到pod运行在k8s-master和k8s-node2两个节点,运行在k8s-node2节点很好理解,因为pod的亲和性缘故,前面部署的proxy pod运行在k8s-node2上。那么pod又为何会运行在k8s-master节点呢,这里就必须提到一个很重要的概念,叫拓扑域,当两个node有标签相等的时候,我们称这两个节点属于一个拓扑域,上面k8s-master和k8s-node2中disk-type标签值相等,所以这两个节点属于一个拓扑域,而pod会被调用到同一个拓扑域里,所以pod也会被调用到k8s-master节点。

4.3 pod软亲和性

(1)创建声明文件

cd /data/affinity/pod
cat pod-preferred-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-deploy
  labels:
     app: pod-affinity-deploy
spec:
  replicas: 6
  selector:
    matchLabels:
      app: pod-affinity
  template:
    metadata:
      labels:
        app: pod-affinity
    spec:
      containers:
      - name: affinity-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                # 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
                matchExpressions:
                - key: version
                  operator: In
                  values:
                  - v1
                  - v2
              # 拓扑域  若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
              topologyKey: disk-type

(2)创建deployment

kubectl apply -f pod-preferred-affinity.yaml

(3)查看pod运行状态

kubectl get pod -o wide |grep affinity

picture.image 可以看到pod优先被调度到了k8s-master和k8s-node2上面。

4.4 pod硬反亲和性

(1)创建声明文件

cd /data/affinity/pod
cat pod-required-anti-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-deploy
  labels:
     app: pod-anti-affinity-deploy
spec:
  replicas: 6
  selector:
    matchLabels:
      app: pod-anti-affinity
  template:
    metadata:
      labels:
        app: pod-anti-affinity
    spec:
      containers:
      - name: pod-anti-affinity-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              # 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
              matchExpressions:
              - key: app
                operator: In
                values:
                - proxy
            # 拓扑域  若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
            topologyKey: disk-type

(2)创建deployment

kubectl apply -f pod-required-anti-affinity.yaml

(3)查看pod运行情况

kubectl get pod -o wide|grep anti

picture.image 可以看到由于反亲和性的存在,pod被调度到了k8s-node1节点上。

4.5 pod亲和性与反亲和性联合使用

(1)创建声明文件

cd /data/affinity/pod
cat pod-affinity-all.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-all-deploy
  labels:
     app: pod-affinity-all-deploy
spec:
  replicas: 6
  selector:
    matchLabels:
      app: pod-affinity-all
  template:
    metadata:
      labels:
        app: pod-affinity-all
    spec:
      containers:
      - name: pod-affinity-all-container
        image: docker.m.daocloud.io/library/nginx
        imagePullPolicy: IfNotPresent
        ports:
          - containerPort: 80
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              # 由于是Pod亲和性/反亲和性;因此这里匹配规则写的是Pod的标签信息
              matchExpressions:
              - key: app
                operator: In
                values:
                - proxy
            # 拓扑域  若多个node节点具有相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
            topologyKey: disk-type
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: version
                  operator: In
                  values:
                  - v1
                  - v2
              topologyKey: db

(2)创建deployment

kubectl apply -f pod-affinity-all.yaml

(3)查看pod运行情况

kubectl get pod -o wide|grep aff

picture.image 可以看到pod运行在k8s-master节点,我们来分析一下原因,首先看亲和性,pod应该被调度到k8s-master和k8s-node2节点。再看反亲和性pod不应该被调用到k8s-node2节点,所以最终所有节点都被调k8s-master节点。

5.污点和容忍

在k8s中污点(Taints)是node上的一个属性,可以让pod不能调度到带污点的节点上,甚至可以让带污点的节点驱逐node上原有的pod。当然也可以给pod设置容忍属性(Tolerations),让pod忽略node上带的污点,从而将pod调度到该node上。通常情况下,Taints和Tolerations是配合使用的。之前实验中pod可以被调度到master节点,也是因为我没有在master节点上设置污点。
注意
(1)一个 node 可以有多个污点;
(2)一个 pod 可以有多个容忍;
(3)如果一个 node 有多个污点,且 pod 上也有多个容忍,只要 pod 中容忍能包含 node 上设置的全部污点,就可以将 pod 调度到该 node 上;
(4)kubernetes 执行多个污点和容忍方法类似于过滤器。

5.1 污点

(1)查看污点

kubectl get nodes k8s-master -o go-template={{.spec.taints}}

(2)设置污点 污点的effect可以取以下三个值:
1)PreferNoSchedule:尽量不要调度。
2)NoSchedule:一定不能被调度。
3)NoExecute:不仅不会调度, 还会驱逐 Node 上已有的 Pod。
设置污点的命令如下:

kubectl taint node k8s-master key1=value1:NoSchedule
kubectl taint node k8s-master key2=value2:PreferNoSchedule
kubectl taint node k8s-master key3=value3:NoExecute

(3)删除污点 指定key和effect

kubectl taint node k8s-master key1:PreferNoSchedule-

只指定key

kubectl taint node k8s-master key2-

5.2 容忍

(1)在k8s-node2上创建污点

kubectl taint k8s-node2 key1=value:NoSchedule
kubectl taint k8s-node2 key2=value:NoExecute

(2)创建声明文件

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: docker.m.daocloud.io/library/nginx
        ports:
        - containerPort: 80
      tolerations:
      - key: "key1"
        operator: "Equal"
        value: "value"
        effect: "NoSchedule"
      - key: "key2"
        operator: "Equal"
        value: "value"
        effect: "NoExecute"

(3)创建deployment

kubectl apply -f nginx-deployment-tole.yaml

(4)查看pod运行状态

kubectl get pod -o wide |grep nginx

可以看到即使node上有污点,pod依然可以调度到上面。

6.总结

kube-scheduler可以按照k8s预定的算法实现pod的自动调度,同时配合亲和性/反亲和性以及污点等属性,用户可以自行决定pod调度到哪个节点上。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论