如何根据Cluster节点故障自动恢复业务
AI服务器单点硬件故障不可避免,在大规模算力使用场景下,资源池规模越大存在硬件故障的可能性越高。当发生硬件故障时可能会影响节点上服务的正常运行。
Lite Cluster节点巡检到故障后,会上报故障信息到k8s节点和AOM云服务,详情请见Cluster节点故障上报。ModelArts提供故障感知、通知能力,配合业务在原生k8s中进行快速恢复。
针对故障节点信息,自动恢复业务的解决方案大致流程如下:
步骤1:获取故障节点信息(以Watch k8s节点方式为例)
自动恢复业务后,处理故障节点请参见如何定位和处理Cluster资源池节点故障。
如果是GPU资源池会同时安装node-agent组件和gpu device-plugin组件。如果是NPU资源池则是会同时安装node-agent组件和npu device-plugin组件。
节点故障上报如图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资源池节点故障。
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{}{} }