站点图标 兰玉磊的个人博客

一文带你了解Kubernetes的调度策略

API Server接受客户端提交Pod对象创建请求后的操作过程中,有一个重要的步骤就是由调度器程序kube-scheduler从当前集群中选择一个可用的最佳节点来接收并运行它,通常是默认的调度器kube-scheduler负责执行此类任务。

对于每个待创建的Pod对象来说,调度过程通常分为两个阶段—》过滤—》打分,过滤阶段用来过滤掉不符合调度规则的Node,打分阶段建立在过滤阶段之上,为每个符合调度的Node进行打分,分值越高则被调度到该Node的机率越大。

Pod调度策略除了系统默认的kube-scheduler调度器外还有以下几种实现方式:

Pod调度之kube-scheduler

介绍

kube-scheduler是Kubernetes 集群的默认调度器,并且是集群控制面(master)的一部分。对每一个新创建的Pod或者是未被调度的Pod,kube-scheduler会选择一个最优的Node去运行这个Pod。

然而,Pod内的每一个容器对资源都有不同的需求,而且Pod本身也有不同的资源需求。因此,Pod在被调度到Node上之前,根据这些特定的资源调度需求,需要对集群中的Node进行一次过滤。

在一个集群中,满足一个Pod调度请求的所有Node称之为可调度节点。如果没有任何一个Node能满足Pod的资源请求,那么这个Pod将一直停留在未调度状态直到调度器能够找到合适的Node。

调度器先在集群中找到一个Pod的所有可调度节点,然后根据一系列函数对这些可调度节点打分,然后选出其中得分最高的Node来运行Pod。之后,调度器将这个调度决定通知给kube-apiserver,这个过程叫做绑定。

在做调度决定是需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、亲和以及反亲和要求、数据局域性、负载间的干扰等等。

调度流程

kube-scheduler 给一个 pod 做调度选择包含两个步骤:

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

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

整体流程如下图所示:

过滤阶段

在调度时的过滤阶段到底时通过什么规则来对Node进行过滤的呢?就是通过以下规则:

打分阶段

当过滤阶段执行后满足过滤条件的Node,将进行打分阶段。

调度示例

默认配置使用的就是kube-scheduler调度组件,我们下面例子启动三个Pod,看分别被分配到哪个Node。

创建资源配置清单。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: scheduler-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: scheduler-pod
  template:
    metadata:
      labels:
        app: scheduler-pod
    spec:
      containers:
      - image: busybox:latest
        name: scheduler-pod
        command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

使用kubectl创建资源对象。

kubectl apply -f scheduler-pod.yaml

查看被kube-scheduler自动调度的Pod,两个Pod在Node03上,一个在Node02上。

$ kubectl get pods -o wide | grep scheduler
scheduler-deploy-65d8f9c98-cqdm9    1/1     Running            0          111s    10.244.5.59   k8s-node03   <none>           <none>
scheduler-deploy-65d8f9c98-d4t9p    1/1     Running            0          111s    10.244.5.58   k8s-node03   <none>           <none>
scheduler-deploy-65d8f9c98-f8xxc    1/1     Running            0          111s    10.244.2.45   k8s-node02   <none>           <none>

我们查看一下Node资源的使用情况,Node01,可用内存2.7G。

Node02,可用内存5.8G。

Node03,可用内存5.6G。

所以默认的kube-scheduler调度策略经过了过滤和打分后,将以上三个Pod分布在Node2和Node3上。

Pod调度之nodeName

nodeNamed这种调度方式比较简单,我们可以指定Pod在哪台Node上进行运行,通过spec.nodeName参数来指定Node主机名称即可。

apiVersion: v1
kind: Pod
metadata:
  name: nodename-pod
spec:
#指定该Pod运行在k8s-node02节点上
  nodeName: k8s-node02
  containers:
  - image: busybox:latest
    name: nodename-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

Pod调度之nodeSelector

nodeSelector用于将Pod调度到匹配Label的Node上,所以要先给node打上标签,然后在Pod配置清单中选择指定Node的标签。

先给规划node用途,然后打标签,例如将两台node划分给不同团队使用。

k8s-node02给开发团队用,k8s-node03给大数据团队用。

添加标签

$ kubectl label nodes <node_name> key1=val1 key2=val2

查看标签

kubectl get nodes -o wide --show-labels

创建资源配置清单

$ cat nodeSelector-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nodeselector-pod
spec:
  nodeSelector:                      #指定标签选择器
    team: development                #label指定开发团队的label
  containers:
  - image: busybox:latest
    name: nodeselector-containers
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

创建Pod对象

kubectl apply -f nodeSelector-pod.yaml

查看pod被分配的Node

$ kubectl get pods -o wide | grep nodeselect
nodeselector-pod                    1/1     Running            0          49s     10.244.2.47   k8s-node02   <none>           <none>

删除Node标签,需知道,标签的删除不影响pod的运行

kubectl label nodes <node_name> key1-
kubectl label nodes <node_name> key2-

把Pod删除然后再次创建Pod,则会出现问题,找不到可调度的节点,事件表示:6个节点都不匹配 node selector。

$ kubectl describe pods/nodeselector-pod | grep -A 10 Events
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  default-scheduler  0/6 nodes are available: 6 node(s) didn't match node selector.

Pod调度之污点与容忍

污点(taint)是定义在Node之上的键值型的数据,用于让节点拒绝将Pod调度运行于其上,除非该Pod对象具有接纳Node污点的容忍度。而容忍度(tolerations)是定义在Pod对象的键值型属性数据,用于·配置其可容忍的Node污点,而且调度器仅能将Pod对象调度至其能够容忍该Node污点的Node之上。

如下图所示:

污点介绍及定义

污点类型介绍

污点定义在nodeSpec中,容忍度定义在PodSpec中,他们都是键值型数据,但又都额外支持一个效果(effect)标记,语法格式为 “key=value:effect” ,其中key和value的用法及格式与资源注解信息相似,而effect则用于定义对Pod对象的排斥等级,它主要包含以下三种排斥类型。

Master污点介绍

以kubeadm部署的kubernetes集群,其Master节点将自动添加污点信息以阻止不能容忍此污点的Pod对象调度至此节点,因此用户可以手动创建Pod来容忍Master的污点。

查看kubernetes集群中master节点的污点:

$ kubectl get pods -o wide | grep nodeselectnodeselector-pod
1/1     Running            0          49s     10.244.2.47   k8s-node02   <none>           <none>

不过,有些系统级别应用,如kube-proxy或者kube-flannel等也是以Pod形式运行在集群上,这些资源在创建时就添加上了相应的容忍度以确保他们被 DaemonSet 控制器创建时能够调度至 Master 节点运行一个实例。

查看系统级别Pod的容忍度

$ kubectl describe pods/etcd-k8s-master01 pods/kube-flannel-ds-amd64-2smzp -n kube-system | grep Tolerations
Tolerations:       :NoExecute
Tolerations:       :NoExecute
Tolerations:       :NoSchedule
Tolerations:       :NoSchedule

另外,这类Pod是构成Kubernetes系统的基础且关键的组件,它们甚至还定义了更大的容忍度,从下面某kube-flannel实例的容忍度定义来看,它还能容忍那些报告了磁盘压力或内存压力的节点,以及未就绪的节点和不可达的节点,以确保它们能在任何状态下正常调度至集群节点上运行。

kubectl describe pods kube-flannel-ds-amd64-2smzp -n kube-system
Tolerations:     :NoSchedule
                 node.kubernetes.io/disk-pressure:NoSchedule
                 node.kubernetes.io/memory-pressure:NoSchedule
                 node.kubernetes.io/network-unavailable:NoSchedule
                 node.kubernetes.io/not-ready:NoExecute
                 node.kubernetes.io/pid-pressure:NoSchedule
                 node.kubernetes.io/unreachable:NoExecute
                 node.kubernetes.io/unschedulable:NoSchedule
Events:          <none>

定义污点

定义污点语法

kubectl taint nodes <node-name> <key>=<value>:<effect>

key名称长度上线为253个字符,可以使用字母或者数字开头,支持字母、数字、连接符、点号、下划线作为key或者value。value最长是 63个字符。

污点通常用于描述具体的部署规划,它们的键名形式如 node-type、node-role、node-project、node-geo等。

添加污点,为k8s-node02添加污点,污点程度为NoSchedule,type=calculate为标签。

kubectl taint node k8s-node02 type=calculate:NoSchedule

查看污点

kubectl describe nodes k8s-node02 | grep Taints

这样的话我们创建Pod就不会被调度到我们打上污点的k8s-node02的节点上

删除污点,删除污点之需要指定标签的 key 及污点程度。

kubectl taint node k8s-node02 type:NoSchedule-

容忍度介绍及定义

Pod对象的容忍度可以通过其spec.tolerations字段进行添加,根据使用的操作符不同,主要有两种可用的形式:

容忍度所用到的参数tolerations,tolerations参数下的还有以下几个二级参数:

为Node打上不同的等级污点

kubectl taint nodes k8s-node01 nodes=gpu:NoSchedule
kubectl taint nodes k8s-node02 data=ssd:PreferNoSchedule
kubectl taint nodes k8s-node03 traffic=proxy:NoExecute

查看三个Node被打上的污点

kubectl describe nodes k8s-node01 k8s-node02 k8s-node03 | grep Taint
Taints:             nodes=gpu:NoSchedule
Taints:             data=ssd:PreferNoSchedule
Taints:             traffic=proxy:NoExecute

1、创建容忍NoSchedule级别污点的Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-noschedule
spec:
  containers:
  - name: gpu-container
    image: busybox:latest
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
  tolerations:
  - key: "nodes"                #指定污点的key
    operator: "Equal"           #Equal值表示我们指定的key必须要等于value
    value: "gpu"                #指定value
    effect: "NoSchedule"        #指定污点级别

查看调度结果

$ kubectl get pods -o wide | grep pod-noschedule
pod-noschedule                      1/1     Running   0          58s     10.244.3.62   k8s-node01   <none>           <none>

2、创建容忍PreferNoSchedule级别污点的Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-prefernoschedule
spec:
  containers:
  - name: ssd-container
    image: busybox:latest
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
  tolerations:
  - key: "data"
    operator: "Exists" #Exists参数,只判断key等于data是否存在,不需要关心value是什么
    effect: "PreferNoSchedule

查看Pod调度结果

$ kubectl get pods -o wide | grep pod-prefer
pod-prefernoschedule                1/1     Running   0          45s     10.244.2.82   k8s-node02   <none>           <none>

3、创建容忍NoExecute级别污点的Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-noexecute
spec:
  containers:
  - name: proxy-container
    image: busybox:latest
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
  tolerations:
  - key: "traffic"
    operator: "Equal"
    value: "proxy"
    effect: "NoExecute"                        #指定污点级别
    tolerationSeconds: 300                     #指定驱赶当前Node上Pod的延迟时间

查看调度结果

$ kubectl get pods -o wide | grep pod-noexecute
pod-noexecute                       1/1     Running   0          24s    10.244.5.89   k8s-node03   <none>           <none>

4、创建没有容忍度的Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-notolerations
spec:
  containers:
  - name: notolerations-container
    image: busybox:latest
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

查看调度结果

$ kubectl get pods -o wide | grep pod-notolerations
pod-notolerations                   1/1     Running       0          29s     10.244.2.83   k8s-node02   <none>           <none>

实践中,若集群中的一组机器专用于为运行非生产典型的容器应用而备置,而且它们可能随时按需上下线,那么就应该为其添加污点信息,以确保仅那些能够容忍此污点的非生产型Pod对象可以调度到其上,另外,某些有种特殊硬件的节点需要专用于运行一类有着SSD、或者GPU的设备,也应该为其添加污点信息以排除其它Pod对象。

Pod调度之节点亲和性调度

节点亲和性调度程序是用来确定Pod对象调度位置的一组规则,这些规则基于节点上的自定义标签和Pod对象上指定的标签选择器进行定义。

节点亲和性允许Pod对象定义针对一组可以调度于其上的节点的亲和性或反亲和性,不过,它无法具体到某个特定的节点。例如将Pod调度至有着CPU的节点或者一个可用区域内的节点之上。定义节点亲和性规则时有两种类型的节点亲和性规则:

定义节点亲和性规则的关键点有两个:

不过,如 requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution名字中的后半段字符串IgnoredDuringExecution隐藏的意义所指,在Pod资源基于节点亲和性规则调度至某节点之后,节点标签发生了改变而不在符合此类节点亲和性规则时,调度器不会将Pod对象从此节点移除,因为它仅对新建的Pod对象生效。

pods.spec.affinity.nodeAffinity

节点硬亲和性

节点硬亲和性类似于Pod对象使用nodeSelector属性可以基于节点标签匹配的方式将Pod对象调度至某一个节点之上。不过它仅能基于简单的等值关系定义标签选择器,而nodeAffinity中支持使用matchExpressions属性构建更为复杂的标签选择机制。

节点硬亲和性参数解析:

为Node打上标签

#node01打两个标签ssd=true及zone=foo
$ kubectl label node k8s-node01 ssd=true zone=foo

#node02打一个标签zone=foo
$ kubectl label node k8s-node02 zone=foo

#node03打两个标签ssd=true zone=bar
$ kubectl label node k8s-node03 ssd=true zone=bar

创建Pod资源配置清单,下面Pod资源清单中,该Pod将被绑定到标签key为zone,value为foo的Node上,符合该规则有两个Node,分别是Node01和Node02,下面资源配置清单中只创建了一个Pod,可以来观察下该Pod会被调度至Node01还是Node02。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: required-nodeaffinity-deploy
  labels:
    type: required-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: required-nodeaffinity-pod
  template:
    metadata:
      labels:
        app: required-nodeaffinity-pod
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - { key: zone, operator: In, values: ["foo"] }
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "tail -f /etc/passwd"]

查看Pod被调度至哪台Node,如下结果可以看到三个Pod都被调度到了Node01上。

$ kubectl apply -f required-nodeAffinity-pod.yaml

kubectl get pods -o wide | grep requ
required-nodeaffinity-deploy-55448998fd-hm9ww   0/1     ContainerCreating   0          7s      <none>        k8s-node01   <none>           <none>
required-nodeaffinity-deploy-55448998fd-pkwph   0/1     ContainerCreating   0          7s      <none>        k8s-node01   <none>           <none>
required-nodeaffinity-deploy-55448998fd-z94v2   0/1     ContainerCreating   0          7s      <none>        k8s-node01   <none>           <none>

创建Pod资源清单2,如下value有两个值,两个值之间是或关系,可以调度到key为zone,values为foo或者bar标签的Node上, 下面配置清单中有两个key,两个key之间是与关系,第二个key在第一个key的基础上,Node还有有标签key为ssd,values无需关心,因为使用操作符Exists。下面的配置清单中,只能调度到标签key为zone,values为foo或者bar以及key为ssd的Node上,满足此需求的Node有Node01和Node03。

$ cat required-nodeAffinity-pod.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: required-nodeaffinity-deploy
  labels:
    type: required-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: required-nodeaffinity-pod
  template:
    metadata:
      labels:
        app: required-nodeaffinity-pod
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - { key: zone, operator: In, values: ["foo","bar"] }        #foo和bar之间是或关系
              - { key: ssd, operator: Exists, values: [] }                #两个matchExpressions之间是与关系
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "tail -f /etc/passwd"]

查看Pod调度结果,如下三个Pod都被调度在Node03节点。

$ kubectl apply -f required-nodeAffinity-pod.yaml

kubectl get pods -o wide | grep required-nodeaffinity
required-nodeaffinity-deploy-566678b9d8-c84nd   1/1     Running   0          65s     10.244.5.97   k8s-node03   <none>           <none>
required-nodeaffinity-deploy-566678b9d8-pn27p   1/1     Running   0          65s     10.244.5.95   k8s-node03   <none>           <none>
required-nodeaffinity-deploy-566678b9d8-x8ttf   1/1     Running   0          65s     10.244.5.96   k8s-node03   <none>           <none>

节点软亲和性

节点软亲和性为节点选择机制提供了一种柔性控逻辑,当调度的Pod对象不再是”必须”,而是“应该”放置于某些特性节点之上,当条件不满足时,它也能够接受编排于其它不符合条件的节点之上,另外,它还为每种倾向性提供了weight属性以便用户定义其优先级,取值范围是1-100,数字越大优先级越高。

节点软亲和性参数解析:

创建Pod资源配置清单,如下示例中,Pod模版定义了Node软亲和性运行在标签key为zone和values为foo或bar上,以及key为ssd(值无需担心是什么)的Node之上,符合以下需求的是Node01和Node03,但是如下第一个条件key为zoo的权重为60,而key为ssd的为30,所以第一个条件的权重要比第二个条件的权重高一倍,我们下面运行了3个Pod,调度器应该会在Node01上分配两个Pod,Node03上分配1个Pod。

$ cat preferred-nodeAffinitt-pod.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: preferred-nodeaffinity-deploy
  labels:
    type: preferred-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: preferred-nodeaffinity-pod
  template:
    metadata:
      labels:
        app: preferred-nodeaffinity-pod
    spec:
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 60
            preference:
              matchExpressions:
              - { key: zone, operator: In, values: ["foo","bar"] }
          - weight: 30
            preference:
              matchExpressions:
              - { key: ssd, operator: Exists, values: [] }
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "tail -f /etc/passwd"]

查看调度结果

$ kubectl get pods -o wide | grep preferred

preferred-nodeaffinity-deploy-5bf4699fd9-pcxvz   1/1     Running   0          5m32s   10.244.5.98    k8s-node03   <none>           <none>
preferred-nodeaffinity-deploy-5bf4699fd9-phm8b   1/1     Running   0          5m32s   10.244.3.106   k8s-node01   <none>           <none>
preferred-nodeaffinity-deploy-5bf4699fd9-xf87j   1/1     Running   0          5m32s   10.244.3.105   k8s-node01   <none>           <none>

创建标签key为app,值为proxy或者web的Pod

$ cat app_proxy.yaml

apiVersion: v1
kind: Pod
metadata:
  name: app-proxy-pod
  labels:
    app: proxy
spec:
  containers:
  - name: app-proxy
    image: busybox:latest
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

查看调度结果

$ kubectl apply -f app_proxy.yaml

#具备标签key为app,values的Pod被调度到了node03上
$ kubectl get pods -owide | grep app-proxy
app-proxy-pod                                    1/1     Running   0          42s     10.244.5.102   k8s-node03   <none>           <none>

#如下podAffinity硬亲和性调度也被调度到此Node
$ kubectl get pods -owide | grep podaff
podaffinity-required-pod                         1/1     Running   0          2m30s   10.244.5.103   k8s-node03   <none>           <none>

Pod调度之Pod亲和性调度

podAffinity介绍

出于高效通信的需求,我们要把几个Pod对象运行在比较近的位置,例如APP Pod与DB Pod,我们的APP Pod需要连接DB Pod,这个时候就需要把这两个Pod运行在较近的位置以便于网络通信,一般可以按照区域、机房、地区等来划分。像这种类型就属于Pod的亲和性调度。但是有时候出于安全或者分布式考虑,也有可能将Pod运行在不同区域、不同机房,这个时候Pod的关系就是为反亲和性。

podAffinity也被分为硬亲和性和软亲和性,其原理与Node中的硬亲和性及软亲和性一致。

硬亲和性(required):硬亲和性实现的是强制性规则,它是Pod调度时必须要满足的规则,而在不存在满足规则的Node时,Pod对象会被置为Pending状态。

软亲和性(preferred):软亲和性规则实现的是一种柔性调度限制,它倾向于将Pod对象运行于某类特定节点之上,而调度器也将尽量满足此需求,但在无法满足需求时它将退而求其次地选择一个不匹配规则的节点之上。

定义Pod亲和性规则的关键点有两个:

Pod反亲和硬亲和性

requiredDuringSchedulingIgnoredDuringExecution属性进行定义,Pod硬亲和性使用topologyKey参数来指定要运行在具备什么样标签key的Node上,然后再通过labelSelector选择你想关联Pod的标签,可能有点绕,下面看示例应该就明白了。

Pod硬亲和性参数解析:

为Node打上不同地区的标签,node01标签为beijing node02标签为shanghai node03标签为shenzhen。

kubectl label node k8s-node01 zone=beijing
kubectl label node k8s-node02 zone=shanghai
kubectl label node k8s-node03 zone=shenzhen

创建资源配置清单,下面清单中,Pod首先会选择标签key为zone的Node,我们上面做了三个Node标签key都为zone,匹配之后,开始在标签key为zone的Node上寻找标签key为app,values为proxy或者web的Pod,然后与其运行在那台Node之上。

$ cat podaffinity-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: podaffinity-required-pod
spec:
  containers:
  - name: nginx-containers
    image: nginx:latest
  affinity:
    podAffinity:
    #硬亲和性
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          #选择标签key为app,values为proxy或者web的Pod,然后与其运行在同一个Node上
          - { key: app, operator: In, values: ["proxy","web"] }
          #选择key为zone的Node
        topologyKey: zone

查看调度结果,如下可以看到,三台Node上没有标签key为app,values为proxy或者web的Pod,我们采用的是硬亲和性,所以该Pod一直为Pending状态。

$ kubectl apply -f podaffinity-pod.yaml

$ kubectl get pods -o wide | grep podaffinity
podaffinity-required-pod                         0/1     Pending   0          3m43s   <none>         <none>       <none>           <none>

创建标签key为app,值为proxy或者web的Pod

$ cat app_proxy.yaml

apiVersion: v1
kind: Pod
metadata:
  name: app-proxy-pod
  labels:
    app: proxy
spec:
  containers:
  - name: app-proxy
    image: busybox:latest
    command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]

查看调度结果

$ kubectl apply -f app_proxy.yaml

#具备标签key为app,values的Pod被调度到了node03上
$ kubectl get pods -owide | grep app-proxy
app-proxy-pod                                    1/1     Running   0          42s     10.244.5.102   k8s-node03   <none>           <none>

#如下podAffinity硬亲和性调度也被调度到此Node
$ kubectl get pods -owide | grep podaff
podaffinity-required-pod                         1/1     Running   0          2m30s   10.244.5.103   k8s-node03   <none>           <none>

Pod软亲和性

preferredDuringSchedulingIgnoredDuringExecution属性进行定义,Pod软亲和性使用podAffinityTerm属性来挑选Pod标签,当调度的Pod对象不再是”必须”,而是“应该”放置于某些特性节点之上,当条件不满足时,它也能够接受编排于其它不符合条件的节点之上,另外,它还为每种倾向性提供了weight属性以便用户定义其优先级,取值范围是1-100,数字越大优先级越高。

Pod软亲和性参数解析:

创建资源配置清单

下面创建一个Pod软亲和性资源配置清单,定义了两组亲和性判断机制,一个是选择cache Pod所在节点的zone标签,并赋予权重为80,另一个是选择db Pod所在的zone标签,权重为20,调度器首先会将Pod调度到具有zone标签key的Node上,然后将多数Pod调度到具备标签为app=cache的Pod同节点,其次调度到具备标签app=db的Pod同节点。

如果Node上的Pod都具备app=cache和app=db,那么根据Pod软亲和性策略,调度器将退而求其次的将Pod调度到其它Node,如果甚至连Node都具备标签zone键,那么根据软亲和策略,调度器还是会退而求其次的将Pod调度到不存在zone键的Node上。

$ cat podaffinity-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: podaffinity-perferred-pod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
    spec:
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 80
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - { key: app, operator: In, values: ["cache"] }
              topologyKey: zone
          - weight: 20
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - { key: app, operator: In, values: ["db"] }
              topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "tail -f /etc/passwd" ]

查看调度结果

# Pod调度结果为node01两个,node03一个
$ kubectl get pods -o wide | grep podaffinity-perferred-pod
podaffinity-perferred-pod-7cddc8c964-5tfr2       1/1     Running   0          12m    10.244.5.106   k8s-node03   <none>           <none>
podaffinity-perferred-pod-7cddc8c964-kqsmk       1/1     Running   0          12m    10.244.3.109   k8s-node01   <none>           <none>
podaffinity-perferred-pod-7cddc8c964-wpqqw       1/1     Running   0          12m    10.244.3.110   k8s-node01   <none>           <none>

# 以下三个Node都具备标签键为zone,但是这三个Node上没有Pod标签为app=cache及app=db,所以上面的调度策略在选择Pod标签的时候进行退步才得以将Pod调度到Node01和Node03
k8sops@k8s-master01:~/manifests/pod$ kubectl get nodes -l zone -L zone
NAME         STATUS   ROLES    AGE   VERSION   ZONE
k8s-node01   Ready    <none>   28d   v1.18.2   beijing
k8s-node02   Ready    <none>   28d   v1.18.2   shanghai
k8s-node03   Ready    <none>   29d   v1.18.2   shenzhen

Pod调度之Pod反亲和性调度

podAntiAffinity介绍

Pod反亲和性podAntiAffinity用于定义Pod对象的亲和约束,Pod反亲和性与Pod亲和性相反,Pod亲和性是将有密切关联的Pod运行到同一平面、同一个区域或者同一台机器上,而反亲和性是将Pod运行在不同区域、不同机器上,Pod反亲和性调度一般用于分散同一类应用的Pod对象等。

podAntiAffinity也被分为硬亲和性和软亲和性,其原理与Node中的硬亲和性及软亲和性一致。

pods.spec.affinity.podAntiAffinity

podAntiAffinity示例

创建资源配置清单

以下创建了4个Pod,自身标签为app=myapp,使用Pod反亲和的硬亲和性,需要运行在具备标签key为zone的Node上,然后不运行在具备标签为app=myapp的Pod同台Node上,我们下面启动了4个Pod,一共有三个Node,前三个Pod都会被分别调度到不同的三台Node上(因为采用的是反亲和性,还是硬性,所以相同标签的Pod不会调度到同一台Node),最后一个Pod将无家可归,最后无法调度。

$ cat podAntiAffinity-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: podantiaffinity-perferred-pod
spec:
  replicas: 4
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: myapp
      labels:
        app: myapp
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - { key: app, operator: In, values: ["myapp"] }
            topologyKey: zone
      containers:
      - name: myapp
        image: busybox:latest
        command: ["/bin/sh", "-c", "tail -f /etc/passwd" ]

创建Pod对象

kubectl apply -f podAntiAffinity-deploy.yaml

查看调度结果

$ kubectl get pods -L app -l app=myapp -o wide
NAME                                             READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES   APP
podantiaffinity-perferred-pod-6576cf75c8-5bw9f   0/1     Pending   0          80s   <none>         <none>       <none>           <none>            myapp
podantiaffinity-perferred-pod-6576cf75c8-bxp4k   1/1     Running   0          80s   10.244.2.88    k8s-node02   <none>           <none>            myapp
podantiaffinity-perferred-pod-6576cf75c8-d2fcm   1/1     Running   0          80s   10.244.5.107   k8s-node03   <none>           <none>            myapp
podantiaffinity-perferred-pod-6576cf75c8-dghr9   1/1     Running   0          80s   10.244.3.111   k8s-node01   <none>           <none>            myapp

查看Node标签

$ kubectl get nodes -L zone -l zone
NAME         STATUS   ROLES    AGE   VERSION   ZONE
k8s-node01   Ready    <none>   28d   v1.18.2   beijing
k8s-node02   Ready    <none>   28d   v1.18.2   shanghai
k8s-node03   Ready    <none>   29d   v1.18.2   shenzhen
退出移动版