文档首页/ AI开发平台ModelArts/ 故障排除/ Lite Cluster/ 如何根据Cluster节点故障自动恢复业务
更新时间:2025-07-31 GMT+08:00
分享

如何根据Cluster节点故障自动恢复业务

AI服务器单点硬件故障不可避免,在大规模算力使用场景下,资源池规模越大存在硬件故障的可能性越高。当发生硬件故障时可能会影响节点上服务的正常运行。

Lite Cluster节点巡检到故障后,会上报故障信息到k8s节点和AOM云服务,详情请见Cluster节点故障上报。ModelArts提供故障感知、通知能力,配合业务在原生k8s中进行快速恢复。

针对故障节点信息,自动恢复业务的解决方案大致流程如下:

步骤1:获取故障节点信息(以Watch k8s节点方式为例)

步骤2:用Taint隔离集群中的故障节点

步骤3:重新下发业务

自动恢复业务后,处理故障节点请参见如何定位和处理Cluster资源池节点故障

Cluster节点故障上报

对于ModelArts Lite资源池,每个节点会以DaemonSet方式部署node-agent组件。

如果是GPU资源池会同时安装node-agent组件和gpu device-plugin组件。如果是NPU资源池则是会同时安装node-agent组件和npu device-plugin组件。

节点故障上报如图1所示。

图1 节点故障上报

故障感知:device-plugin负责xpu卡的故障巡检,node-agent负责AI运行环境的巡检。 device-plugin组件会从驱动侧获取芯片故障并实时上报可用卡数到K8S NodeAllocatable,同时,对使用NPU卡的k8s Pod,device-plugin会自动为其挂载npu状态文件可用于pod内进行健康检查。node-agent组件则是通过巡检脚本收集各故障信息,同时将检测结果汇聚并写到K8S NodeCondition中。

故障通知:node-agent收集到巡检结果后,会周期性上报节点故障指标到AOM服务,配置及时通知方法可参见如何定位和处理Cluster资源池节点故障

K8S Node Allocatable以及Condition详情可参考Kubernetes节点状态

K8S NodeCondition样例:
{
"type": "NT_NPU_CARD_LOSE",
"status": "False",
"lastHeartbeatTime": null,
"lastTransitionTime": "2024-09-27T10: 45: 55Z",
"reason": "os_task_name:npu-card-lose",
"message": "ok"
}

status有3种值:False、True以及Unknown。以上述为例,当status为True时,代表节点发生了Type类型为“NT_NPU_CARD_LOSE”的故障,所有故障类型定义请参见如何定位和处理Cluster资源池节点故障

步骤1:获取故障节点信息

图2 获取故障节点信息

可通过Kubernetes API访问集群,然后获取集群中Nodes的详情,Go客户端样例如下:

package main

import (
    "context"
    "fmt"
    "time"

    "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)
func main() {
    // 通常可以使用clientcmd.RecommendedHomeFile,值是$HOME/.kube/config,根据自身情况可以修改
    config, _ := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
    // 创建 clientset 客户端
    clientset, _ := kubernetes.NewForConfig(config)
    // 创建一个 watcher
    watcher, err := clientset.CoreV1().Nodes().Watch(context.TODO(), metav1.ListOptions{})
    if err != nil {
       fmt.Printf("Failed to create watcher: %v\n", err)
    }
    defer watcher.Stop()
    termCh := make(chan struct{}, 1)
    // 事件驱动获取节点故障信息
    go func() {
       for {
          select {
          case <-termCh:
             return
          case event, ok := <-watcher.ResultChan():
             if !ok {
                fmt.Printf("Failed to get watcher chan: %v\n", err)
                return
             }
             node := event.Object.(*v1.Node)
             fmt.Printf("Event type: %v, Node name: %s\n", event.Type, node.Name)
             for _, v := range node.Status.Conditions {
                fmt.Printf("Node Condition Type: %v, Type Status: %v\n", v.Type, v.Status)
             }
          }
       }
    }()
    // 或者全量获取Nodes
    nodes, _ := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
    fmt.Printf("There are %d nodes in the cluster\n", len(nodes.Items))
    time.Sleep(10 * time.Second)
    // 定义结束信号
    termCh <- struct{}{}
}

步骤2:用Taint隔离集群中的故障节点

图3 用Taint隔离集群中的故障节点

Taint相关资料可参考污点和容忍度

步骤3:重新下发业务

图4 重新下发业务

当业务为训练作业时,可参考设置断点续训练

相关文档