
# 分组Pod故障迁移
#### 功能介绍
在数据中心分布式AI训练/推理场景下，作业实例具有强协同依赖性、长时间运行性和资源独占性（如NPU、GPU）等特点，而数据中心很有可能出现节点宕机、网络分区等故障。当集群出现故障并直接影响某些Pod运行时，重启整个作业是代价高昂的，有可能导致整个作业一直处于运行、失败、重启的循环中，永远无法顺利完成；而只迁移这些受直接影响的Pod，虽然代价较小，但却有可能导致这些Pod与同组内的其他Pod割裂，出现运行信息过时、拓扑约束失效等情况，致使整个分组处于占用资源、但未正常运行的状态，造成集群资源浪费。
为此，Volcano进一步提出了故障场景下支持分组Pod迁移的能力，即当检测到集群出现故障导致某些Pod被驱逐或失活时，主动迁移整个分组的Pod到别的适合的节点上，以加快作业恢复速度，避免浪费集群资源。
以下图场景为例，在由8个空闲节点、4个一层HyperNode、2个二层HyperNode和1个三层HyperNode组成的集群中，运行着一个Volcano Job。该Job包含两个分组，每个分组都被约束调度到同一个一层HyperNode下，其中分组1由Pod0和Pod1组成，运行在节点node0和node1上；分组2由Pod2和Pod3组成，运行在节点node2和node3上。此时，node3节点失效，导致其上的Pod3失活。
图1不支持分组Pod迁移能力   
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002564243289.png "点击放大")
- 若不支持分组Pod迁移能力，则新建的Pod3将因为不存在满足网络拓扑约束的节点而一直处于Pending状态，导致Pod2占用node2节点资源，但却不能正常运行，造成集群资源浪费。或者，用户需重启整个Job，导致Pod0和Pod1也需要重新运行，作业恢复成本较大。
- 若支持分组Pod迁移能力，则可主动中止同一分组内的Pod2，并将新创建的Pod2和Pod3统一调度到HyperNode3下，即节点node6和node7上，从而使整个作业高效恢复运行。
图2支持分组Pod迁移能力   
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000002563601767.png "点击放大")
为此，Volcano Job在作业生命周期管理策略中提供了RestartPartition动作，允许在发生特定事件时重启或迁移整个分组。例如，在Job中添加如下配置，表示在发生Pod驱逐、Pod失败时重启、迁移该Pod所在的整个分组。
```
spec:
  policies:
  - action: RestartPartition
    event: PodEvicted
  - action: RestartPartition
    event: PodFailed
```
#### 前提条件
集群中已安装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名匹配，不支持修改
   ...
   ```
   
   
4. 以下图中的多级网络拓扑结构为例，创建8个节点，并执行如下命令，为它们添加如下网络拓扑标签。 
   本示例仅用于演示，实际创建的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-2****   1      test-tier-1   2           8s
   hypernode-testtopology-tier1-4****   1      test-tier-1   2           8s
   hypernode-testtopology-tier1-j****   1      test-tier-1   2           8s
   hypernode-testtopology-tier1-l****   1      test-tier-1   2           8s
   hypernode-testtopology-tier2-m****   2      test-tier-2   2           8s
   hypernode-testtopology-tier2-n****   2      test-tier-2   2           8s
   hypernode-testtopology-tier3-c****   3      test-tier-3   2           8s
   ```
   
   
6. 创建一个Volcano Job工作负载，包含4个Pod，每个Pod独占一个节点的所有NPU资源，并配置分组策略将这4个Pod均分为2组，每个分组最高亲和到tier=1的HyperNode上。此外，配置事件为PodEvicted、动作为RestartPartition的生命周期管理策略，已启用分组Pod故障迁移能力。示例如下所示。 
   ```
   apiVersion: batch.volcano.sh/v1alpha1
   kind: Job
   metadata:
     name: network-topology-job
     namespace: default
   spec:
     schedulerName: volcano
     policies:
     - action: RestartPartition   # 发生PodEvicted事件时，重启该pod所在的整个分组pod
       event: PodEvicted
     minAvailable: 4         # gang调度条件，需要满足至少4个pod可调度成功
     tasks:
       - replicas: 4 # 一共4个实例
         name: t0
         partitionPolicy:          # 配置分组亲和调度策略
           totalPartitions: 2      # 将当前task内的pod分为2组
           partitionSize: 2        # 每个分组包含2个pod
           networkTopology:        # 配置分组级网络拓扑约束
             mode: hard            # 该网络拓扑约束为硬约束，必须满足
             highestTierAllowed: 1 # 最高亲和到tier=1的HyperNode
         template:
           spec:
             containers:
               - name: container1
                 image: nginx:latest
                 resources:
                   requests:
                     huawei.com/ascend-1980: 16 # 每个pod独占一个节点的NPU资源
                   limits:
                     huawei.com/ascend-1980: 16
   ```
   
   
7. 执行以下命令，查看调度结果。此时，第一组Pod已被调度至HyperNode0的node0和node1上，第二组Pod已被调度至HyperNode1的node2和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          6s    172.19.1.36    192.168.5.17    <none>           <none>
   network-topology-job-t0-1   1/1     Running   0          6s    172.19.2.143   192.168.5.224   <none>           <none>
   network-topology-job-t0-2   1/1     Running   0          6s    172.19.3.19    192.168.5.239   <none>           <none>
   network-topology-job-t0-3   1/1     Running   0          6s    172.19.1.162   192.168.5.235   <none>           <none>
   ```
   
   
8. 执行以下命令，驱逐node3节点上的Pod，模拟节点故障场景。 
   ```
   kubectl drain 192.168.5.239 --ignore-daemonsets
   ```
   
   
9. 重新检查调度结果。此时，第二个分组的两个Pod已被调度到HyperNode3的node6和node7上，保持了与分组内其他Pod的网络拓扑一致性。 
   ```
   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          2m51s   172.19.1.29    192.168.5.17    <none>           <none>
   network-topology-job-t0-1   1/1     Running   0          2m51s   172.19.2.136   192.168.5.224   <none>           <none>
   network-topology-job-t0-2   1/1     Running   0          80s     172.19.3.143   192.168.5.95    <none>           <none>
   network-topology-job-t0-3   1/1     Running   0          82s     172.19.4.12    192.168.5.83    <none>           <none>
   ```
   
   
10. 执行以下命令，恢复node3节点。 
    ```
    kubectl uncordon 192.168.5.239
    ```
    
    
 
