
# 多级网络拓扑装箱调度
#### 功能介绍
通过多级网络拓扑亲和调度和分组亲和调度能力，Volcano已有效缓解了数据中心分布式AI训练/推理作业的网络通信性能问题。然而，随着各种新工作负载的不断到来和旧工作负载的陆续结束，各HyperNode的资源碎片化问题日趋严重，即集群虽有充足的可用资源，但却分散在不同HyperNode中，导致对通信性能要求高的新作业不能被调度，或只能被分散调度，降低集群资源使用效率。传统的装箱调度算法主要考虑节点级资源占用情况，通过将任务分配到尽可能少的节点上，有效缓解节点的资源碎片化，但却不能缓解HyperNode的资源碎片化问题。
为此，Volcano进一步提出了多级网络拓扑装箱调度能力，在调度时考虑各层HyperNode的资源占用情况，并将工作负载调度至综合资源占用率较高的HyperNode，以缓解HyperNode级资源碎片化问题，提升整体资源使用效率。
具体而言，在调度具有网络拓扑约束的工作负载时，调度器会优先将其调度到当前资源占用率较高的HyperNode，以提高整体资源利用率。如下图所示，假设集群内有2个tier=1的HyperNode，其中HyperNode1已有部分资源被其他任务占用。此时下发一个Volcano Job，均分成2组，每个分组配置网络拓扑约束只能部署在1层的HyperNode内。
- 不使用装箱调度时：分组1调度至HyperNode1，分组2调度至HyperNode2。
- 使用装箱调度时：分组1和分组2都会优先调度到HyperNode1上。此时，HyperNode2可作为一个完整的空闲HyperNode供其他任务使用。
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002533403372.png "点击放大")
此外，针对没有网络拓扑约束的工作负载，调度器还提供了多层级HyperNode级装箱调度能力，即优先将这些工作负载调度到所在各层级HyperNode当前资源利用率较高的节点。如下图所示，假设集群内有8个节点、7个HyperNode，其中节点node0、node2和node4上的资源均被其他任务占用，而tier=1、tier=2、tier=3的HyperNode分别有4个、2个和1个。此时，下发一个由两个Pod组成、无网络拓扑约束的Volcano Job，于是：
- 若不启用该能力：这两个Pod可能被调度到node1、node3、node5、node6和node7中的任意两个节点，可能导致其它更大、有网络拓扑约束的任务无法调度。
- 若启用了该能力：这两个Pod将被优先调度到node1和node3。此时，HyperNode3、HyperNode5有更多资源，可以供其它更大、有网络拓扑约束的任务使用。
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002564163245.png "点击放大")
为使用此功能，需Volcano启用network-topology-aware插件，并配置如下参数。其中，fading参数需大于等于0，否则将被设置为默认值0.8。
- 当fading为0时，各层HyperNode的装箱权重均为0。
- 当fading大于0且小于1时，低层HyperNode的装箱权重大于高层HyperNode。
- 当fading为1时，各层HyperNode的装箱权重均为1。
- 当fading大于1，高层HyperNode的装箱权重大于低层HyperNode。
```
...
default_scheduler_conf:
  tiers:
    - plugins:
        - name: network-topology-aware # 添加本行内容，以启用网络拓扑感知调度
          arguments:
            weight: 10                       # 本插件整体策略权重，可不填，默认为1
            hypernode.binpack.cpu: 5         # CPU资源HyperNode级装箱权重，可不填，默认为1
            hypernode.binpack.memory: 5      # 内存资源HyperNode级装箱权重，可不填，默认为1
            hypernode.binpack.resources: nvidia.com/gpu, huawei.com/ascend-1980      # 装箱策略需要考虑的自定义资源名，可不填，默认为无
            hypernode.binpack.resources.nvidia.com/gpu: 10                  # 自定义资源“nvidia.com/gpu”的HyperNode级装箱权重，可不填，默认为1
            hypernode.binpack.resources.huawei.com/ascend-1980: 10         # 自定义资源“huawei.com/ascend-1980”的HyperNode级装箱权重，可不填，默认为1
            hypernode.binpack.normal-pod.enable: true      # 针对无网络拓扑约束Pod的多层级HyperNode装箱调度能力开关，可不填，默认为true
            hypernode.binpack.normal-pod.fading: 0.8       # 无网络拓扑约束Pod在计算各层级HyperNode装箱分数时，第tier层的权重为math.Pow(fading, tier-1)，可不填，默认为0.8
...
```
#### 前提条件
集群中已安装Volcano调度器插件，且插件版本在1.21.1及以上。
#### 约束与限制
- 网络拓扑感知调度暂不支持抢占。当资源紧张时，调度器不会主动抢占已分配给Pod的资源。
- HyperJob工作负载不支持网络拓扑感知调度。即使在HyperJob的JobTemplate中配置了networkTopology或分组亲和策略，这些配置将被忽略且不会生效。
- HyperNode自动发现功能不支持多种网络拓扑结构混部的场景（例如，A2与A3拓扑类型共存于同一集群）。
- 应尽量避免在运行中变更网络拓扑配置项，因为这可能导致集群内HyperNode结构重建，进而引发Volcano调度器进程的负载波动或调度异常。
- 若在调度过程中，集群网络拓扑结构或配置项发生变更，Volcano无法保证调度结果的正确性。
 
#### 使用示例
1. 登录[CCE控制台](https://console.huaweicloud.com/cce2.0/?#/cce/cluster/list)，单击集群名称进入集群。
2. 单击左侧导航栏的"配置中心"，切换至"调度配置"页面，选择Volcano调度器找到对应的"专家模式"，单击"开始使用"。 
   ![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002519785283.png "点击放大")
   
   
   
   
   
3. 进入CCE专家模式配置页面，在YAML配置文件中，修改default_controller_conf配置为如下内容，以启用HyperNode自动发现功能。其中配置参数，请根据实际需求调整。 
   ```
   ...
   default_controller_conf:
     networkTopologyDiscovery:
       - source: label # HyperNode自动发现插件的名称，不可修改
         enabled: true # HyperNode自动发现插件开关，true表示开启，false表示关闭（原有的HyperNode实例不会删除）
         config:
           networkTopologyTypes:
             testtopology:                         # 网络拓扑层级关系的名称，最大限制20字符；当前仅支持配置一组
               - nodeLabel: test-tier-3            # 第3层网络拓扑对应的节点标签名，对应HyperNode tier=3
               - nodeLabel: test-tier-2            # 第2层网络拓扑对应的节点标签名，对应HyperNode tier=2
               - nodeLabel: test-tier-1            # 第1层网络拓扑对应的节点标签名，对应HyperNode tier=1
               - nodeLabel: kubernetes.io/hostname # 第0层网络拓扑对应的节点标签名，即节点层级，仅支持根据Node名匹配，不支持修改
   ...
   ```
   同时，在default_scheduler_conf配置中添加名为network-topology-aware的插件，如下所示。其配置参数，请根据实际需求调整。
   ```
   ...
   default_scheduler_conf:
     actions: allocate, backfill, preempt
     configurations: ''
     metrics:
       interval: 30s
       type: ''
     tiers:
       - plugins:
           - name: priority
           - enableJobStarving: false
             enablePreemptable: false
             enableReclaimable: false
             name: gang
           - name: conformance
           - enableHierarchy: true
             name: capacity
       - plugins:
           - enablePreemptable: false
             enableReclaimable: false
             name: drf
           - name: predicates
           - name: nodeorder
       - plugins:
           - name: cce-gpu-topology-predicate
           - name: cce-gpu-topology-priority
           - name: xgpu
           - name: network-topology-aware # 添加本行内容，以启用网络拓扑感知调度
             arguments:
               weight: 10                  # 本插件整体策略权重，默认为1
               hypernode.binpack.cpu: 5    # CPU资源HyperNode级装箱权重，默认为1
               hypernode.binpack.memory: 5 # 内存资源HyperNode级装箱权重，默认为1
               hypernode.binpack.resources: nvidia.com/gpu, huawei.com/ascend-1980 # 装箱策略需要考虑的自定义资源名，默认为无
               hypernode.binpack.resources.nvidia.com/gpu: 10                      # “nvidia.com/gpu”的HyperNode级装箱权重，默认无
               hypernode.binpack.resources.huawei.com/ascend-1980: 10              # “huawei.com/ascend-1980”的HyperNode级装箱权重，默认无
               hypernode.binpack.normal-pod.enable: true     # 针对无网络拓扑约束Pod的多层级HyperNode装箱调度能力开关，默认为true
               hypernode.binpack.normal-pod.fading: 0.8      # 无网络拓扑约束Pod在计算各层级HyperNode装箱分数时，第tier层的权重为math.Pow(fading, tier-1)，默认为0.8
       - plugins:
           - name: nodelocalvolume
           - name: nodeemptydirvolume
           - name: nodeCSIscheduling
           - name: networkresource
   ...
   ```
   
   
4. 以下图中的多级网络拓扑结构为例，创建8个节点，并执行如下命令，为它们添加如下网络拓扑标签。 
   ![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002563669205.png "点击放大")
   本示例仅用于演示，实际创建的HyperNode应依据集群的真实拓扑。此外，对于没有网络拓扑关系的普通节点，不应添加与网络拓扑相关的节点标签，否则可能导致创建的HyperNode不符合预期。
   ```
   kubectl label node 192.168.5.17  test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode0 # node0
   kubectl label node 192.168.5.224 test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode0 # node1
   kubectl label node 192.168.5.235 test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode1 # node2
   kubectl label node 192.168.5.239 test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode1 # node3
   kubectl label node 192.168.5.240 test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode2 # node4
   kubectl label node 192.168.5.251 test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode2 # node5
   kubectl label node 192.168.5.83  test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode3 # node6
   kubectl label node 192.168.5.95  test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode3 # node7
   ```
   表1节点、IP与多级HyperNode归属映射表 
   | 节点名   | IP地址          | 标签（Labels）                                                           |
   |:---|:---|:---|
   | node0 | 192.168.5.17  | test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode0 |
   | node1 | 192.168.5.224 | test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode0 |
   | node2 | 192.168.5.235 | test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode1 |
   | node3 | 192.168.5.239 | test-tier-3=HyperNode6 test-tier-2=HyperNode4 test-tier-1=HyperNode1 |
   | node4 | 192.168.5.240 | test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode2 |
   | node5 | 192.168.5.251 | test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode2 |
   | node6 | 192.168.5.83  | test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode3 |
   | node7 | 192.168.5.95  | test-tier-3=HyperNode6 test-tier-2=HyperNode5 test-tier-1=HyperNode3 |
      
   
   
5. 执行以下命令，查询Volcano自动发现的HyperNode。 
   ```
   kubectl get hypernodes
   ```
   返回信息如下所示。
   ```
   NAME                                      TIER   TIERNAME      NODECOUNT   AGE
   hypernode-testtopology-tier1-5****   1      test-tier-1   2           9s
   hypernode-testtopology-tier1-7****   1      test-tier-1   2           10s
   hypernode-testtopology-tier1-g****   1      test-tier-1   2           10s
   hypernode-testtopology-tier1-l****   1      test-tier-1   2           9s
   hypernode-testtopology-tier2-5****   2      test-tier-2   2           9s
   hypernode-testtopology-tier2-t****   2      test-tier-2   2           10s
   hypernode-testtopology-tier3-c****   3      test-tier-3   2           10s
   ```
   
   
6. 创建3个Pod工作负载，指定将其调度到node0、node2和node4上，并占据这些节点的所有NPU资源。示例如下。 
   ```
   apiVersion: v1
   kind: Pod
   metadata:
     name: node-pod-1
   spec:
     containers:
     - name: nginx
       image: nginx:latest
       resources:
         requests:
           huawei.com/ascend-1980: 16
         limits:
           huawei.com/ascend-1980: 16
     nodeName: 192.168.5.17 # node0
   ---
   apiVersion: v1
   kind: Pod
   metadata:
     name: node-pod-2
   spec:
     containers:
     - name: nginx
       image: nginx:latest
       resources:
         requests:
           huawei.com/ascend-1980: 16
         limits:
           huawei.com/ascend-1980: 16
     nodeName: 192.168.5.235 # node2
   ---
   apiVersion: v1
   kind: Pod
   metadata:
     name: node-pod-3
   spec:
     containers:
     - name: nginx
       image: nginx:latest
       resources:
         requests:
           huawei.com/ascend-1980: 16
         limits:
           huawei.com/ascend-1980: 16
     nodeName: 192.168.5.240 # node4
   ```
   
   
7. 创建一个Volcano Job工作负载，包含2个Pod，每个Pod独占一个节点的NPU资源，且无网络拓扑约束。示例如下所示。 
   ```
   apiVersion: batch.volcano.sh/v1alpha1
   kind: Job
   metadata:
     name: network-topology-job
     namespace: default
   spec:
     schedulerName: volcano
     tasks:
       - replicas: 2
         name: t0
         template:
           spec:
             containers:
               - name: container1
                 image: nginx:latest
                 resources:
                   requests:
                     huawei.com/ascend-1980: 16
                   limits:
                     huawei.com/ascend-1980: 16
   ```
   
   
8. 查看调度结果。该示例中，节点node0、node2和node4的资源均已被占用，因此，两个Pod分别被调度到节点node1和node3。 
   ```
   kubectl get pod -o wide
   ```
   返回信息如下所示。
   ```
   NAME                        READY   STATUS    RESTARTS   AGE   IP             NODE            NOMINATED NODE   READINESS GATES
   network-topology-job-t0-0   1/1     Running   0          5s    172.19.2.132   192.168.5.224   <none>           <none>
   network-topology-job-t0-1   1/1     Running   0          5s    172.19.3.4     192.168.5.239   <none>           <none>
   node-pod-1                  1/1     Running   0          16s   172.19.1.25    192.168.5.17    <none>           <none>
   node-pod-2                  1/1     Running   0          16s   172.19.1.143   192.168.5.235   <none>           <none>
   node-pod-3                  1/1     Running   0          16s   172.19.2.4     192.168.5.240   <none>           <none>
   ```
   
   
 
