# 设置工作负载亲和/反亲和调度（podAffinity/podAntiAffinity）
工作负载亲和/反亲和调度是Kubernetes提供的任务调度方式，可以使用工作负载作为亲和对象，灵活地将新建的工作负载调度到与其相关或无关的节点上，可以有效地提高集群的利用率。
例如，通信频繁的前端应用Pod和后端应用Pod可优先调度到同一个节点或同一个可用区，减少网络延迟。工作负载亲和/反亲和的示意如下：
1. 首先，拓扑域（根据topologyKey划分）通过节点的标签和标签值划分节点范围，将节点分为不同的拓扑域。 例如，topologyKey为prefer，表示可以通过节点标签prefer划分拓扑域。拓扑域1的范围为带有prefer=true标签的节点，拓扑域2的范围为带有prefer=false标签的节点，拓扑域3的范围为不带prefer标签的节点。
   
2. 然后，根据工作负载标签名、操作符、标签值确定需要亲和/反亲和的工作负载对象。 例如，标签选择器筛选出需要亲和/反亲和的对象为带有app=backend的工作负载。
   
3. 最后，调度器选择目标工作负载所处的拓扑域进行亲和性调度，或者选择不存在目标工作负载的拓扑域进行反亲和性调度。 示例中，带有app=backend的工作负载在拓扑域1中，因此，亲和app=backend工作负载在调度时，可以调度到拓扑域1中。同理，反亲和app=backend工作负载在调度时，只能调度到拓扑域2或3中。
   
图1工作负载亲和/反亲和示意图   
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000001865968034.png "点击放大")
#### 配置负载亲和/反亲和调度策略
您可以通过不同的方式配置负载亲和/反亲和调度策略，将Pod调度到满足条件的节点。
- [通过控制台配置]
  本文示例中，集群内已创建后端应用的工作负载，且带有app=backend的标签，您可以通过该标签进行工作负载亲和/反亲和调度，将新创建的前端应用（标签为app=frontend）和后端应用（标签为app=backend）部署在同一节点上，即拓扑域为kubernetes.io/hostname。由于每个节点的hostname不同，因此使用kubernetes.io/hostname划分拓扑域时，每个拓扑域中仅1个节点，在工作负载亲和时可实现调度到同一节点。
  1. 登录[CCE控制台](https://console.huaweicloud.com/cce2.0/?#/cce/cluster/list)。
  
  2. 单击集群名称进入集群，在左侧选择"工作负载"，在右上角单击"创建工作负载"。
  
  3. 在创建工作负载时，在"高级配置"中找到"调度策略"，选择负载亲和调度的策略类型，本示例中选择**自定义亲和策略** 。创建工作负载的其余步骤详情请参见[创建工作负载](https://support.huaweicloud.com/usermanual-cce/cce_10_0673.html)。
     
     ![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002032873176.png "点击放大")
     
     表1调度策略类型 
     | 参数     | 参数说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | 示例           |
     |:---|:---|:---|
     | 负载亲和 | - **不配置**：不设置负载亲和策略。  - **优先多可用区部署** ：该策略通过Pod自身反亲和实现，并以可用区作为拓扑域，可**优先**将工作负载的Pod调度到不同可用区的节点上。  - **强制多可用区部署** ：该策略通过Pod自身反亲和实现，并以可用区作为拓扑域，可**强制**将工作负载的Pod调度到不同可用区的节点上。使用该调度策略时，如果节点数小于实例数或节点资源不足，Pod将无法全部运行。  - **自定义亲和策略** ：根据Pod标签实现灵活的调度策略，通过配置调度策略，您可以将工作负载（Pod）强制或优先部署至指定的可用区或节点；也可利用标签选择器自定义亲和与反亲和规则，灵活控制Pod必须、优先调度至（或避开）符合条件的节点。 支持的调度策略类型请参见[表2]。选择合适的策略类型后，可以添加相应的调度策略，参数详情请参见[表3]。    | **自定义亲和策略** |
        
     
     
  
  4. 选择合适的负载亲和性规则，并单击![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002033425240.png "点击放大")，添加相应的调度策略。本示例中在**工作负载亲和性 \> 必须满足** 的类别下添加调度策略，表示节点上必须已经运行了指定标签的工作负载才可以调度本次创建的工作负载。
     
      表2负载亲和策略类型 
     | 策略       | 规则类型 | 说明                                                                                                                                                                                                                                                                                                                   | 示例       |
     |:---|:---|:---|:---|
     | 工作负载亲和性    | 必须满足  | 即硬约束，设置必须满足的条件，对应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，也会选择一个拓扑域进行调度。 | -      |
        
     
     
  
  5. 在右侧弹出窗口中单击"添加策略"，设置节点标签筛选规则。 
      表3负载亲和/反亲和调度策略设置参数说明 
     | 参数  | 参数说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | 示例                        |
     |:---|:---|:---|
     | 权重  | 仅支持在"尽量满足"策略中添加。权重的取值范围为1-100，调度器在进行调度时会将该权重视为一个附加的评分项，并将其与节点的其他优先级函数评分相加。最终，调度器会将Pod调度到总分最大的节点上。                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | -                          |
     | 命名空间  | 指定调度策略生效的命名空间。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | **default**              |
     | 拓扑域  | 拓扑域（topologyKey）通过节点的标签和标签值先圈定调度的节点范围，然后再通过标签名、操作符、标签值确定亲和/反亲和的对象，最后根据目标对象所处的拓扑域进行调度。 - 如果标签指定为kubernetes.io/hostname，此时标签值为k8s节点名称，则将不同名称的节点划分为不同的拓扑域，由于k8s节点名称不可重复，此时一个拓扑域中仅包含一个节点，因此可以实现单个节点级别的负载亲和性调度。  - 如果指定标签为kubernetes.io/os，此时标签值为节点的操作系统类型，则将不同操作系统的节点划分为不同的拓扑域，此时一个拓扑域中可能包含多个节点，因此可以将多个节点作为一个整体进行负载亲和性调度。 例如，某个拓扑域中的一个节点上运行着满足负载亲和性规则的Pod，则该拓扑域中的节点均可以被调度。    | **kubernetes.io/hostname** |
     | 标签名 | 设置工作负载亲和/反亲和性时，填写需要匹配的工作负载标签。 该标签可以使用系统默认的标签，也可以使用自定义标签。                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | **app**                  |
     | 操作符  | 可以设置四种匹配关系（In、NotIn、Exists、DoesNotExist）。 - In：亲和/反亲和对象的标签在标签值列表（values字段）中。  - NotIn：亲和/反亲和对象的标签不在标签值列表（values字段）中。  - Exists：亲和/反亲和对象存在指定标签名。  - DoesNotExist：亲和/反亲和对象不存在指定标签名。                                                                                                     | **In**                   |
     | 标签值  | 设置工作负载亲和/反亲和性时，填写工作负载标签对应的标签值。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | **backend**                 |
        
     
     
  
  6. 调度策略添加完成后，单击"创建工作负载"。
  
  7. 验证Pod全部运行在目标节点上。 
     1. 在集群控制台左侧导航栏中选择"工作负载"。
     
     2. 单击工作负载名称，进入详情页面，查看实例列表，验证新建的Pod和已有的backend Pod运行在同一节点上。 ![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002033424668.png "点击放大")
        
        
     
     
     
     
   
- [通过YAML配置]
  - 工作负载亲和性 Kubernetes支持Pod和Pod之间的亲和，例如将应用的前端和后端部署在一起，从而减少访问延迟。
    假设有个应用的后端已经创建，且带有app=backend的标签。您可以使用.spec.affinity.podAffinity字段来设置工作负载亲和性，将前端Pod（标签为app=frontend）和后端Pod（标签为app=backend）部署在一起。
    ```
    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: prefer    # 根据节点标签划分拓扑域，示例中prefer为自定义标签
                labelSelector:  # 根据工作负载标签选择满足条件的工作负载
                  matchExpressions: # 工作负载标签匹配规则
                  - key: app # 工作负载标签的键为app
                    operator: In # 表示存在values列表中的值即满足规则
                    values: # 工作负载标签的值列表
                    - backend
              preferredDuringSchedulingIgnoredDuringExecution:    # 表示尽量满足的调度策略
              - weight: 100  # 使用尽量满足策略时可设置优先级，取值为1-100，数值越大优先级越高
                podAffinityTerm:  # 使用尽量满足策略时的亲和项
                  topologyKey: topology.kubernetes.io/zone   # 根据节点标签划分拓扑域，以节点的可用区为粒度
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - backend
    ```
    上述示例中的工作负载调度时，**必须满足** 规则会根据prefer标签划分节点拓扑域，如果当拓扑域中某个节点运行着后端Pod（标签为app=backend），即使该拓扑域中并非所有节点均运行了后端Pod，前端Pod（标签为app=frontend）同样会部署在此拓扑域中。而**尽量满足**规则根据topology.kubernetes.io/zone划分拓扑域，以节点的可用区为粒度进行调度，表示尽量将前后端部署至同一可用区的节点。
    ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/note_3.0-zh-cn.png)
    对于工作负载亲和来说，使用requiredDuringSchedulingIgnoredDuringExecution和preferredDuringSchedulingIgnoredDuringExecution规则时， topologyKey字段不允许为空。
    topologyKey字段用于划分拓扑域，当节点上存在topologyKey字段指定的标签，且键、值均相同时，这些节点会被认为属于同一拓扑域，然后调度器会根据工作负载的标签选择需要调度的拓扑域。一个拓扑域中可能包含多个节点，当拓扑域中的一个节点上运行了满足标签选择规则的工作负载时，则该拓扑域中的节点均可以被调度。
    例如，当topologyKey的标签为topology.kubernetes.io/zone时，表示以节点的可用区作为拓扑域，工作负载在部署时会以可用区为粒度进行调度。
    
  
  - 工作负载反亲和性 在某些情况下，需要将Pod分开部署，例如Pod之间部署在一起会影响性能的情况。
    假设有个应用的前端已经创建，且带有app=frontend的标签。您可以使用.spec.affinity.podAntiAffinity字段来设置工作负载反亲和性，将各个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:  # 工作负载标签的键为app
                  - key: app  # 工作负载标签的键为app
                    operator: In  # 表示存在values列表中的值即满足规则
                    values:  # 工作负载标签的值列表
                    - frontend
              preferredDuringSchedulingIgnoredDuringExecution:    # 表示尽量满足的调度策略
              - weight: 100  # 使用尽量满足策略时可设置优先级，取值为1-100，数值越大优先级越高
                podAffinityTerm:  # 使用尽量满足策略时的亲和项
                  topologyKey: topology.kubernetes.io/zone   # 根据节点标签划分拓扑域，以节点的可用区为粒度
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - frontend
    ```
    以上示例中定义了反亲和规则，**必须满足** 的规则表示根据kubernetes.io/hostname标签划分节点拓扑域。由于拥有kubernetes.io/hostname标签的节点中，每个节点的标签值均不同，因此一个拓扑域中只有一个节点。当一个拓扑域中（此处为一个节点）已经存在frontend标签的Pod时，该拓扑域不会被继续调度具有相同标签的Pod。而**尽量满足**规则根据topology.kubernetes.io/zone划分拓扑域，以节点的可用区为粒度进行调度，表示尽量将Pod分布至不同可用区的节点。
    ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/note_3.0-zh-cn.png)
    对于工作负载反亲和来说，使用requiredDuringSchedulingIgnoredDuringExecution规则时， Kubernetes默认的准入控制器 LimitPodHardAntiAffinityTopology要求topologyKey字段只能是kubernetes.io/hostname。如果您希望使用其他定制拓扑逻辑，可以更改或者禁用该准入控制器。
    
   
#### 相关文档
- [设置节点亲和调度（nodeAffinity）](https://support.huaweicloud.com/usermanual-cce/cce_10_0892.html)
- [设置指定节点调度（nodeSelector）](https://support.huaweicloud.com/usermanual-cce/cce_10_0891.html)
- [如何让多个Pod均匀部署到各个节点上](https://support.huaweicloud.com/cce_faq/cce_faq_00260.html)
 
