更新时间:2024-12-04 GMT+08:00

调度策略(亲和与反亲和)

Kubernetes支持节点亲和与Pod亲和/反亲和。通过配置亲和与反亲和规则,可以允许您指定硬性限制或者偏好,例如将前台Pod和后台Pod部署在一起、某类应用部署到某些特定的节点、不同应用部署到不同的节点等等。

Kubernetes的亲和功能由节点和工作负载两种类型组成:

  • 节点亲和(nodeAffinity):类似于Pod中的nodeSelector字段,使用nodeSelector字段只会将Pod调度到指定标签的节点上,这与节点亲和类似,但节点亲和性的表达能力更强,并且允许指定优先选择的软约束。两种类型的节点亲和如下:
    • requiredDuringSchedulingIgnoredDuringExecution:必须满足的硬约束,即调度器只有在规则被满足的时候才能执行调度。此功能类似于nodeSelector, 但其语法表达能力更强,详情请参见节点亲和(nodeAffinity)
    • preferredDuringSchedulingIgnoredDuringExecution:尽量满足的软约束,即调度器会尝试寻找满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该Pod,详情请参见节点优先选择规则
  • 工作负载亲和(podAffinity)/工作负载反亲和(podAntiAffinity):基于已经在节点上运行的Pod标签来约束Pod可以调度到的节点,而不是基于节点上的标签。与节点亲和类似,工作负载亲和与反亲和也有requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution两种类型。

    工作负载亲和性和反亲和性需要一定的计算时间,因此在大规模集群中会显著降低调度的速度。在包含数百个节点的集群中,不建议使用这类设置。

您可以通过控制台创建上述亲和策略,详情请参见通过控制台配置负载亲和调度策略通过控制台配置节点亲和调度策略

通过控制台配置负载亲和调度策略

  1. 在创建工作负载时,在“高级设置”中找到“调度策略”。创建工作负载的步骤详情请参见创建工作负载
  2. 选择负载亲和调度的策略类型。

    • 不配置:不设置负载亲和策略。
    • 优先多可用区部署:该策略通过Pod自身反亲和实现,优先将工作负载的Pod调度到不同可用区的节点上。
    • 强制多可用区部署:该策略通过Pod自身反亲和实现,强制将工作负载的Pod调度到不同可用区,并且强制调度到不同节点上。使用该调度策略时,如果节点数小于实例数或节点资源不足,Pod将无法全部运行。
    • 自定义亲和策略:根据Pod标签实现灵活的调度策略,支持的调度策略类型请参见表1。选择合适的策略类型后,单击添加调度策略,参数详情请参见表2
      表1 负载亲和策略类型

      策略

      规则类型

      说明

      工作负载亲和性

      必须满足

      即硬约束,设置必须满足的条件,对应YAML定义中的requiredDuringSchedulingIgnoredDuringExecution字段。

      通过标签筛选需要亲和的Pod,如果满足筛选条件的Pod已经运行在拓扑域中的某个节点上,调度器会将本次创建的Pod强制调度到该拓扑域。

      说明:

      添加多条亲和性规则时,即设置多个标签筛选需要亲和的Pod,则本次创建的Pod必须要同时亲和所有满足标签筛选的Pod,即所有满足标签筛选的Pod要处于同一拓扑域中才可以调度。

      尽量满足

      即软约束,设置尽量满足的条件,对应YAML定义中的preferredDuringSchedulingIgnoredDuringExecution字段。

      通过标签筛选需要亲和的Pod,如果满足筛选条件的Pod已经运行在拓扑域中的某个节点上,调度器会将本次创建的Pod优先调度到该拓扑域。

      说明:

      添加多条亲和性规则时,即设置多个标签筛选需要亲和的Pod,则本次创建的Pod会尽量同时亲和多个满足标签筛选的Pod。但即使所有Pod都不满足标签筛选条件,也会选择一个拓扑域进行调度。

      工作负载反亲和性

      必须满足

      即硬约束,设置必须满足的条件,对应YAML定义中的requiredDuringSchedulingIgnoredDuringExecution字段。

      通过标签筛选需要反亲和的一个或多个Pod,如果满足筛选条件的Pod已经运行在拓扑域中的某个节点上,调度器不会将本次创建的Pod调度到该拓扑域。

      说明:

      添加多条反亲和性规则时,即设置多个标签筛选需要反亲和的Pod,则本次创建的Pod必须要同时反亲和所有满足标签筛选的Pod,即所有满足标签筛选的Pod所处的拓扑域都不会被调度。

      尽量满足

      即软约束,设置尽量满足的条件,对应YAML定义中的preferredDuringSchedulingIgnoredDuringExecution字段。

      通过标签筛选需要反亲和的一个或多个Pod,如果满足筛选条件的Pod已经运行在拓扑域中的某个节点上,调度器会将本次创建的Pod优先调度到其他拓扑域。

      说明:

      添加多条反亲和性规则时,即设置多个标签筛选需要反亲和的Pod,则本次创建的Pod会尽量同时反亲和多个满足标签筛选的Pod。但即使每个拓扑域都存在需要反亲和的Pod,也会选择一个拓扑域进行调度。

      表2 负载亲和/反亲和调度策略设置参数说明

      参数名

      参数描述

      权重

      仅支持在“尽量满足”策略中添加。权重的取值范围为1-100,调度器在进行调度时会将该权重加到其他优先级函数的评分上,最终将Pod调度到总分最大的节点上。

      命名空间

      指定调度策略生效的命名空间。

      拓扑域

      拓扑域(topologyKey)通过节点的标签先圈定调度的节点范围,例如标签指定为kubernetes.io/hostname,则根据标签值不同(标签值为节点名称)区分范围,不同名称的节点为不同的拓扑域,此时一个拓扑域中仅包含一个节点;如果指定标签为kubernetes.io/os,则根据标签值不同(标签值为节点的操作系统类型)来区分,不同操作系统的节点为不同的拓扑域,此时一个拓扑域中可能包含多个节点。

      根据拓扑域确定节点范围后,然后再选择策略定义的内容(通过标签名、操作符、标签值确定)进行调度,调度时最小单位为拓扑域。例如,某个拓扑域中的一个节点满足负载亲和性规则,则该拓扑域中的节点均可以被调度。

      标签名

      设置工作负载亲和/反亲和性时,填写需要匹配的工作负载标签。

      该标签可以使用系统默认的标签,也可以使用自定义标签。

      操作符

      可以设置四种匹配关系(In、NotIn、Exists、DoesNotExist)。

      • In:亲和/反亲和对象的标签在标签值列表(values字段)中。
      • NotIn:亲和/反亲和对象的标签不在标签值列表(values字段)中。
      • Exists:亲和/反亲和对象存在指定标签名。
      • DoesNotExist:亲和/反亲和对象不存在指定标签名。

      标签值

      设置工作负载亲和/反亲和性时,填写工作负载标签对应的标签值。

  3. 调度策略添加完成后,单击“创建工作负载”。

通过控制台配置节点亲和调度策略

  1. 在创建工作负载时,在“高级设置”中找到“调度策略”。创建工作负载的步骤详情请参见创建工作负载
  2. 选择节点亲和调度的策略类型。

    • 不配置:不设置节点亲和策略。
    • 指定节点调度:指定工作负载Pod部署的节点。若不指定,将根据集群默认调度策略随机调度。
    • 指定节点池调度:指定工作负载Pod部署的节点池。若不指定,将根据集群默认调度策略随机调度。
    • 自定义亲和策略:根据节点标签实现灵活的调度策略,支持的调度策略类型请参见表3。选择合适的策略类型后,单击添加调度策略,参数详情请参见表4。您也可以单击“指定节点”“指定可用区”通过控制台快速选择需要调度的节点或可用区。

      “指定节点”“指定可用区”本质也是通过标签实现,只是通过控制台提供了更为便捷的操作,无需手动填写节点标签和标签值。指定节点使用的是 kubernetes.io/hostname 标签,指定可用区使用的是 failure-domain.beta.kubernetes.io/zone 标签。

      表3 节点亲和性设置

      参数名

      参数描述

      必须满足

      即硬约束,设置必须要满足的条件,对应requiredDuringSchedulingIgnoredDuringExecution。

      添加多条“必须满足”规则时,只需要满足一条规则就会进行调度。

      尽量满足

      即软约束,设置尽量满足的条件,对应preferredDuringSchedulingIgnoredDuringExecution。

      添加多条“尽量满足”规则时,满足其中一条或者都不满足也会进行调度。

      表4 节点亲和性调度策略设置参数说明

      参数名

      参数描述

      标签名

      设置节点亲和性时,填写需要匹配的节点标签。

      该标签可以使用系统默认的标签,也可以使用自定义标签。

      操作符

      可以设置六种匹配关系(In、NotIn、Exists、DoesNotExist、Gt、Lt)。

      • In:亲和/反亲和对象的标签在标签值列表(values字段)中。
      • NotIn:亲和/反亲和对象的标签不在标签值列表(values字段)中。
      • Exists:亲和/反亲和对象存在指定标签名。
      • DoesNotExist:亲和/反亲和对象不存在指定标签名。
      • Gt:仅在节点亲和性中设置,调度节点的标签值大于列表值 (字符串比较)。
      • Lt:仅在节点亲和性中设置,调度节点的标签值小于列表值 (字符串比较)。

      标签值

      设置节点亲和性时,填写节点标签对应的标签值。

  3. 调度策略添加完成后,单击“创建工作负载”。

节点亲和(nodeAffinity)

工作负载节点亲和性规则通过节点标签实现。CCE集群中节点在创建时会自动添加一些标签,您可通过kubectl describe node命令查看,示例如下:

$ kubectl describe node 192.168.0.212
Name:               192.168.0.212
Roles:              <none>
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    failure-domain.beta.kubernetes.io/is-baremetal=false
                    failure-domain.beta.kubernetes.io/region=******
                    failure-domain.beta.kubernetes.io/zone=******
                    kubernetes.io/arch=amd64
                    kubernetes.io/availablezone=******
                    kubernetes.io/eniquota=12
                    kubernetes.io/hostname=192.168.0.212
                    kubernetes.io/os=linux
                    node.kubernetes.io/subnetid=fd43acad-33e7-48b2-a85a-24833f362e0e
                    os.architecture=amd64
                    os.name=EulerOS_2.0_SP5
                    os.version=3.10.0-862.14.1.5.h328.eulerosv2r7.x86_64

在工作负载调度中,常用的节点标签如下:

  • failure-domain.beta.kubernetes.io/region:表示节点所在的区域。
  • failure-domain.beta.kubernetes.io/zone:表示节点所在的可用区(availability zone)。
  • kubernetes.io/hostname:节点的hostname。

在创建工作负载时,Kubernetes提供了nodeSelector字段,设置该字段后可以让Pod只部署在具有特定标签的节点上。如下所示,Pod只会部署在拥有gpu=true这个标签的节点上。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeSelector:                 # 节点选择,当节点拥有gpu=true标签时才在节点上创建Pod
    gpu: true
...
通过节点亲和性规则配置,也可以做到同样的事情。相比nodeSelector的方式,节点亲和性规则看起来要复杂很多,但这种方式可以得到更强的表达能力,您可以使用spec.affinity.nodeAffinity字段设置节点亲和性。节点亲和性以下有两种规则:
  • requiredDuringSchedulingIgnoredDuringExecution:表示必须满足指定的规则才能将Pod调度到节点。
  • preferredDuringSchedulingIgnoredDuringExecution:表示将Pod调度到尽量满足对应规则的节点。如果找不到匹配的节点,调度器仍然会调度该Pod。

在上述节点亲和规则中,前半段requiredDuringScheduling或preferredDuringScheduling表示下面定义的规则必须强制满足(require)才会调度Pod到节点上。而后半段IgnoredDuringExecution表示如果节点标签在Kubernetes调度Pod后发生了变更,Pod仍将继续运行不会重新调度。但是如果该节点上的kubelet重启,kubelet会重新对节点亲和性规则进行校验,Pod仍会被调度至其他节点。

设置节点亲和性示例如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name:  gpu
  labels:
    app:  gpu
spec:
  selector:
    matchLabels:
      app: gpu
  replicas: 3
  template:
    metadata:
      labels:
        app:  gpu
    spec:
      containers:
      - image:  nginx:alpine
        name:  gpu
        resources:
          requests:
            cpu: 100m
            memory: 200Mi
          limits:
            cpu: 100m
            memory: 200Mi
      imagePullSecrets:
      - name: default-secret
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: gpu
                operator: In
                values:
                - "true"

本示例中,调度的节点必须包含一个键名为gpu的标签,且操作符operator的值为In,表示标签值需要在values的列表中,即节点gpu标签的键值为true。其他operator取值请参见操作符取值说明。需要说明的是并没有nodeAntiAffinity(节点反亲和),因为NotIn和DoesNotExist操作符可以提供相同的功能。

下面来验证这段规则是否生效,假设某集群有如下三个节点。

$ kubectl get node
NAME            STATUS   ROLES    AGE   VERSION                            
192.168.0.212   Ready    <none>   13m   v1.15.6-r1-20.3.0.2.B001-15.30.2
192.168.0.94    Ready    <none>   13m   v1.15.6-r1-20.3.0.2.B001-15.30.2   
192.168.0.97    Ready    <none>   13m   v1.15.6-r1-20.3.0.2.B001-15.30.2   

首先给192.168.0.212这个节点打上gpu=true的标签。

$ kubectl label node 192.168.0.212 gpu=true
node/192.168.0.212 labeled

$ kubectl get node -L gpu
NAME            STATUS   ROLES    AGE   VERSION                            GPU
192.168.0.212   Ready    <none>   13m   v1.15.6-r1-20.3.0.2.B001-15.30.2   true
192.168.0.94    Ready    <none>   13m   v1.15.6-r1-20.3.0.2.B001-15.30.2   
192.168.0.97    Ready    <none>   13m   v1.15.6-r1-20.3.0.2.B001-15.30.2   

创建这个Deployment,可以发现所有的Pod都部署在了192.168.0.212这个节点上。

$ kubectl create -f affinity.yaml 
deployment.apps/gpu created

$ kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE         
gpu-6df65c44cf-42xw4     1/1     Running   0          15s   172.16.0.37   192.168.0.212
gpu-6df65c44cf-jzjvs     1/1     Running   0          15s   172.16.0.36   192.168.0.212
gpu-6df65c44cf-zv5cl     1/1     Running   0          15s   172.16.0.38   192.168.0.212

节点优先选择规则

上面讲的requiredDuringSchedulingIgnoredDuringExecution是一种强制选择的规则,节点亲和还有一种优先选择规则,即preferredDuringSchedulingIgnoredDuringExecution,表示会根据规则优先选择哪些节点。

为演示这个效果,先为上面的集群添加一个SAS磁盘的节点,并打上DISK=SAS的标签,为另外三个节点打上DISK=SSD的标签。

$ kubectl get node -L DISK,gpu
NAME            STATUS   ROLES    AGE     VERSION                            DISK     GPU
192.168.0.100   Ready    <none>   7h23m   v1.15.6-r1-20.3.0.2.B001-15.30.2   SAS   
192.168.0.212   Ready    <none>   8h      v1.15.6-r1-20.3.0.2.B001-15.30.2   SSD      true
192.168.0.94    Ready    <none>   8h      v1.15.6-r1-20.3.0.2.B001-15.30.2   SSD   
192.168.0.97    Ready    <none>   8h      v1.15.6-r1-20.3.0.2.B001-15.30.2   SSD  

下面定义一个Deployment,要求Pod优先部署在SSD磁盘的节点上,可以像下面这样定义,使用preferredDuringSchedulingIgnoredDuringExecution规则,给SSD设置权重(weight)为80,而gpu=true权重为20,这样Pod就优先部署在SSD的节点上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name:  gpu
  labels:
    app:  gpu
spec:
  selector:
    matchLabels:
      app: gpu
  replicas: 10
  template:
    metadata:
      labels:
        app:  gpu
    spec:
      containers:
      - image:  nginx:alpine
        name:  gpu
        resources:
          requests:
            cpu:  100m
            memory:  200Mi
          limits:
            cpu:  100m
            memory:  200Mi
      imagePullSecrets:
      - name: default-secret
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 80 
            preference: 
              matchExpressions: 
              - key: DISK
                operator: In 
                values: 
                - SSD
          - weight: 20 
            preference: 
              matchExpressions: 
              - key: gpu
                operator: In 
                values: 
                - "true"

来看实际部署后的情况,可以看到部署到192.168.0.212(标签为DISK=SSD、gpu=true)这个节点上的Pod有5个,192.168.0.97(标签为DISK=SSD)上有3个,而192.168.0.100(标签为DISK=SAS)上只有2个。

这里您看到Pod并没有调度到192.168.0.94(标签为DISK=SSD)这个节点上,这是因为这个节点上部署了很多其他Pod,资源使用较多,所以并没有往这个节点上调度,这也侧面说明preferredDuringSchedulingIgnoredDuringExecution是优先规则,而不是强制规则。

$ kubectl create -f affinity2.yaml 
deployment.apps/gpu created

$ kubectl get po -o wide
NAME                   READY   STATUS    RESTARTS   AGE     IP            NODE         
gpu-585455d466-5bmcz   1/1     Running   0          2m29s   172.16.0.44   192.168.0.212
gpu-585455d466-cg2l6   1/1     Running   0          2m29s   172.16.0.63   192.168.0.97 
gpu-585455d466-f2bt2   1/1     Running   0          2m29s   172.16.0.79   192.168.0.100
gpu-585455d466-hdb5n   1/1     Running   0          2m29s   172.16.0.42   192.168.0.212
gpu-585455d466-hkgvz   1/1     Running   0          2m29s   172.16.0.43   192.168.0.212
gpu-585455d466-mngvn   1/1     Running   0          2m29s   172.16.0.48   192.168.0.97 
gpu-585455d466-s26qs   1/1     Running   0          2m29s   172.16.0.62   192.168.0.97 
gpu-585455d466-sxtzm   1/1     Running   0          2m29s   172.16.0.45   192.168.0.212
gpu-585455d466-t56cm   1/1     Running   0          2m29s   172.16.0.64   192.168.0.100
gpu-585455d466-t5w5x   1/1     Running   0          2m29s   172.16.0.41   192.168.0.212

上面这个例子中,对于节点排序优先级如下所示,有个两个标签的节点排序最高,只有SSD标签的节点排序第二(权重为80),只有gpu=true的节点排序第三,没有的节点排序最低。

图1 优先级排序顺序

工作负载亲和(podAffinity)

节点亲和的规则只能影响Pod和节点之间的亲和,Kubernetes还支持Pod和Pod之间的亲和,例如将应用的前端和后端部署在一起,从而减少访问延迟。Pod亲和同样有requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution两种规则。

对于工作负载亲和来说,使用requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution规则时, topologyKey字段不允许为空。

来看下面这个例子,假设有个应用的后端已经创建,且带有app=backend的标签。

$ kubectl get po -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP            NODE         
backend-658f6cb858-dlrz8   1/1     Running   0          2m36s   172.16.0.67   192.168.0.100

将前端frontend的pod部署在backend一起时,可以做如下Pod亲和规则配置。

apiVersion: apps/v1
kind: Deployment
metadata:
  name:   frontend
  labels:
    app:  frontend
spec:
  selector:
    matchLabels:
      app: frontend
  replicas: 3
  template:
    metadata:
      labels:
        app:  frontend
    spec:
      containers:
      - image:  nginx:alpine
        name:  frontend
        resources:
          requests:
            cpu:  100m
            memory:  200Mi
          limits:
            cpu:  100m
            memory:  200Mi
      imagePullSecrets:
      - name: default-secret
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname
            labelSelector:
              matchExpressions: 
              - key: app
                operator: In 
                values: 
                - backend

创建frontend然后查看,可以看到frontend都创建到跟backend一样的节点上了。

$ kubectl create -f affinity3.yaml 
deployment.apps/frontend created

$ kubectl get po -o wide
NAME                        READY   STATUS    RESTARTS   AGE     IP            NODE         
backend-658f6cb858-dlrz8    1/1     Running   0          5m38s   172.16.0.67   192.168.0.100
frontend-67ff9b7b97-dsqzn   1/1     Running   0          6s      172.16.0.70   192.168.0.100
frontend-67ff9b7b97-hxm5t   1/1     Running   0          6s      172.16.0.71   192.168.0.100
frontend-67ff9b7b97-z8pdb   1/1     Running   0          6s      172.16.0.72   192.168.0.100

这里有个topologyKey字段(用于划分拓扑域),意思是先圈定topologyKey指定的范围,当节点上的标签键、值均相同时会被认为同一拓扑域,然后再选择下面规则定义的内容。这里每个节点上都有kubernetes.io/hostname,所以看不出topologyKey起到的作用。

如果backend有两个Pod,分别在不同的节点上。

$ kubectl get po -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP            NODE         
backend-658f6cb858-5bpd6   1/1     Running   0          23m     172.16.0.40   192.168.0.97
backend-658f6cb858-dlrz8   1/1     Running   0          2m36s   172.16.0.67   192.168.0.100

给192.168.0.97和192.168.0.94打上prefer=true的标签。

$ kubectl label node 192.168.0.97 prefer=true
node/192.168.0.97 labeled
$ kubectl label node 192.168.0.94 prefer=true
node/192.168.0.94 labeled

$ kubectl get node -L prefer
NAME            STATUS   ROLES    AGE   VERSION                            PREFER
192.168.0.100   Ready    <none>   44m   v1.15.6-r1-20.3.0.2.B001-15.30.2   
192.168.0.212   Ready    <none>   91m   v1.15.6-r1-20.3.0.2.B001-15.30.2   
192.168.0.94    Ready    <none>   91m   v1.15.6-r1-20.3.0.2.B001-15.30.2   true
192.168.0.97    Ready    <none>   91m   v1.15.6-r1-20.3.0.2.B001-15.30.2   true

将podAffinity的topologyKey定义为prefer,则节点拓扑域的划分如图2所示。

      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: prefer
            labelSelector:
              matchExpressions: 
              - key: app
                operator: In 
                values: 
                - backend
图2 拓扑域示意图

调度时,会根据prefer标签划分节点拓扑域,本示例中192.168.0.97和192.168.0.94被划作同一拓扑域。如果当拓扑域中运行着app=backend的Pod,即使该拓扑域中并非所有节点均运行了app=backend的Pod(本例该拓扑域中仅192.168.0.97节点上存在app=backend的Pod),frontend同样会部署在此拓扑域中(这里的192.168.0.97或192.168.0.94)。

$ kubectl create -f affinity3.yaml 
deployment.apps/frontend created

$ kubectl get po -o wide
NAME                        READY   STATUS    RESTARTS   AGE     IP            NODE         
backend-658f6cb858-5bpd6    1/1     Running   0          26m     172.16.0.40   192.168.0.97
backend-658f6cb858-dlrz8    1/1     Running   0          5m38s   172.16.0.67   192.168.0.100
frontend-67ff9b7b97-dsqzn   1/1     Running   0          6s      172.16.0.70   192.168.0.97
frontend-67ff9b7b97-hxm5t   1/1     Running   0          6s      172.16.0.71   192.168.0.97
frontend-67ff9b7b97-z8pdb   1/1     Running   0          6s      172.16.0.72   192.168.0.97

工作负载反亲和(podAntiAffinity)

前面讲了Pod的亲和,通过亲和将Pod部署在一起,有时候需求却恰恰相反,需要将Pod分开部署,例如Pod之间部署在一起会影响性能的情况。

对于工作负载反亲和来说,使用requiredDuringSchedulingIgnoredDuringExecution规则时, Kubernetes默认的准入控制器 LimitPodHardAntiAffinityTopology要求topologyKey字段只能是kubernetes.io/hostname。如果您希望使用其他定制拓扑逻辑,可以更改或者禁用该准入控制器。

下面例子中定义了反亲和规则,这个规则表示根据kubernetes.io/hostname标签划分节点拓扑域,且如果该拓扑域中的某个节点上已经存在带有app=frontend标签的Pod,那么拥有相同标签的Pod将不能被调度到该拓扑域内的其他节点上。

apiVersion: apps/v1
kind: Deployment
metadata:
  name:   frontend
  labels:
    app:  frontend
spec:
  selector:
    matchLabels:
      app: frontend
  replicas: 5
  template:
    metadata:
      labels:
        app:  frontend
    spec:
      containers:
      - image:  nginx:alpine
        name:  frontend
        resources:
          requests:
            cpu:  100m
            memory:  200Mi
          limits:
            cpu:  100m
            memory:  200Mi
      imagePullSecrets:
      - name: default-secret
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - topologyKey: kubernetes.io/hostname    #节点拓扑域
            labelSelector:    #Pod标签匹配规则
              matchExpressions: 
              - key: app
                operator: In 
                values: 
                - frontend

创建并查看部署效果,示例中根据kubernetes.io/hostname标签划分节点拓扑域,在拥有kubernetes.io/hostname标签的节点中,每个节点的标签值均不同,因此一个拓扑域中只有一个节点。当一个拓扑域中(此处为一个节点)已经存在frontend标签的Pod时,该拓扑域不会被继续调度具有相同标签的Pod。本例中只有4个节点,因此还有一个Pod处于Pending状态无法调度。

$ kubectl create -f affinity4.yaml 
deployment.apps/frontend created

$ kubectl get po -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE         
frontend-6f686d8d87-8dlsc   1/1     Running   0          18s   172.16.0.76   192.168.0.100
frontend-6f686d8d87-d6l8p   0/1     Pending   0          18s   <none>        <none>
frontend-6f686d8d87-hgcq2   1/1     Running   0          18s   172.16.0.54   192.168.0.97 
frontend-6f686d8d87-q7cfq   1/1     Running   0          18s   172.16.0.47   192.168.0.212
frontend-6f686d8d87-xl8hx   1/1     Running   0          18s   172.16.0.23   192.168.0.94 

操作符取值说明

您可以使用操作符(operator字段)来设置使用规则的逻辑关系,operator取值如下:

  • In:亲和/反亲和对象的标签在标签值列表(values字段)中。
  • NotIn:亲和/反亲和对象的标签不在标签值列表(values字段)中。
  • Exists:亲和/反亲和对象存在指定标签名。
  • DoesNotExist:亲和/反亲和对象不存在指定标签名。
  • Gt:仅在节点亲和性中设置,调度节点的标签值大于列表值 (字符串比较)。
  • Lt:仅在节点亲和性中设置,调度节点的标签值小于列表值 (字符串比较)。