NVSentinel 数据流梳理

原文参考:https://docs.nvidia.com/nvsentinel/architecture/data-flow

一、整体架构

1. 设计思路

从公开文档和仓库结构看,NVSentinel 的主链路是事件驱动的:节点侧组件先把异常整理成 HealthEventplatform-connectors 负责入库和同步 Kubernetes 状态,后面的隔离、驱逐、修复模块再从事件存储里异步消费。MongoDB Change Streams 在这里承担的是事件分发和状态流转的作用。

主链路可以按下面理解:

  1. 节点上的采集器(生产者)通过本地 gRPC 把故障事件报上来;
  2. Platform Connectors 负责集中接收、做格式校验,然后持久化到存储层;
  3. 集群控制面的各个模块订阅数据库变更流,异步消费处理故障;
  4. 隔离、驱逐、修复这些动作主要通过 Kubernetes API 执行。

2. Preflight 预检(Pod 准入检查)

这是一条独立的链路,不走 gRPC 上报:

  • 以业务 Pod 的 Init 容器形式运行,属于一次性的临时进程;
  • 校验失败时,通过 Unix 域套接字把事件发给本机的 Platform Connector,后面就走正常入库流程了;
  • 校验没问题就不产生任何事件。多节点协同校验靠 Gang 发现 + ConfigMap 配置来实现。

3. HealthEvent 数据模型

HealthEvent 是主数据模型。GPU、网卡、系统日志、云厂商维护等事件都会尽量归一到这个结构里,方便后续模块用同一套规则处理。

主要字段

分类 字段 说明
来源信息 version、agent、componentClass、nodeName 采集组件名称、硬件类型、故障节点名
健康状态 checkName、isFatal、isHealthy、message 检测项、是否致命、硬件健康状态、可读的故障描述
故障处置 recommendedAction、errorCode、entitiesImpacted 推荐的修复动作、故障错误码、受影响的资源(GPU/NODE/POD/NIC)
元数据 metadata、generatedTimestamp 自定义 KV(硬件 UUID、驱动版本、故障严重度)、事件生成时间
行为控制 quarantineOverrides、drainOverrides 全局开关:跳过节点隔离、跳过 Pod 驱逐

RecommendedAction 枚举值

NONE=0            无需操作
COMPONENT_RESET=2 组件重置
CONTACT_SUPPORT=5 联系技术支持
RESTART_VM=15     重启虚拟机
RESTART_BM=24     重启裸金属服务器
REPLACE_VM=25     更换虚拟机/硬件
UNKNOWN=99        未知处置方案

Entity 子结构

用来描述故障关联的资源:

  • entityType:资源类型 GPU / NODE / POD / NIC
  • entityValue:资源的唯一标识(GPU UUID、网卡 PCI 地址、节点名)

二、组件分类和部署形态

从 Helm chart 组织方式看,组件基本按独立工作负载部署。下面按运行位置分成三类:节点侧 DaemonSet、集群控制面 Deployment、存储层 StatefulSet。具体语言实现随版本可能变化,这里不把语言作为判断重点。

节点侧 DaemonSet(每台 GPU 节点一个 Pod,常驻采集)

只在 GPU/计算节点上部署,本地采集硬件数据,通过本机 gRPC 把事件报给本地的 Platform Connectors。

Chart 名称 运行载体 进程形态 主要作用
gpu-health-monitor DaemonSet 独立 Pod、常驻进程 对接 DCGM 采集 GPU 温度、功耗、ECC、XID、降频等健康信号,旁路输出 Prometheus 指标
syslog-health-monitor DaemonSet 独立 Pod、常驻进程 读节点 journal 日志,抓 GPU XID/SXID、PCI 总线失联这类报错
nic-health-monitor DaemonSet 独立 Pod、常驻进程 监控网卡、RDMA、PCIe 网络链路、硬件丢包故障,生成网卡类 HealthEvent
metadata-collector DaemonSet 独立 Pod、常驻进程 采集 GPU UUID、NVSwitch、网卡 PCI 拓扑等硬件元数据
platform-connectors DaemonSet 独立 Pod、gRPC 服务 本机事件入口:收本地事件、格式校验、写存储,并同步 K8s NodeCondition/Event
preflight Init 容器模板 业务 Pod 内一次性临时进程 Pod 启动前做硬件预检,异常通过 Unix Socket 报给本地 Platform Connectors

集群控制面 Deployment(集中运行)

这类组件运行在集群控制面,订阅 MongoDB Change Streams 来消费故障事件,负责隔离、驱逐、修复、分析和状态同步等逻辑。

故障处理模块

Chart 名称 运行载体 主要作用
fault-quarantine Deployment 根据 CEL 规则判断是否 cordon 节点,必要时加 taint,并记录隔离状态
node-drainer Deployment 感知已隔离节点,按 namespace 和 GPU 粒度策略调用 K8s Eviction API 驱逐 Pod
fault-remediation Deployment 根据故障的 recommendedAction 创建 RebootNode 运维 CRD,下发硬件修复工单
health-events-analyzer Deployment 对事件做时间窗口和模式分析,必要时升级故障或生成推荐动作
labeler Deployment 自动给 K8s 节点打 GPU 驱动、DCGM、硬件型号标签
event-exporter Deployment 消费事件流,向外推送告警到第三方平台(HTTP/Webhook)
kubernetes-object-monitor Deployment 监听 K8s Node/Pod/CRD 资源变更,补充集群资源状态事件
slurm-drain-monitor Deployment K8s+Slurm 异构集群双向同步:NVSentinel 隔离节点自动 drain Slurm 节点、Slurm 手动驱逐同步告警到 NVSentinel
csp-health-monitor Deployment 轮询公有云 API,获取 VM 硬件维护、迁移计划并生成维护事件

运维执行模块

Chart 名称 运行载体 主要作用
janitor Deployment CRD 控制器,监听 RebootNode 工单,下发硬件重启/更换指令
janitor-provider Deployment 底层驱动抽象层,对接 IPMI、裸金属机房、公有云硬件 API,屏蔽底层差异

集群内置服务

Chart 名称 运行载体 主要作用
incluster-file-server Deployment 集群静态配置服务,下发 CEL 隔离规则、故障码映射、告警模板

存储层 StatefulSet(持久化事件和日志)

Chart 名称 运行载体 用途
mongodb-store StatefulSet(默认 3 副本) 生产环境常用主存储,持久化 HealthEvent,提供 Change Streams
k8s-datastore StatefulSet 轻量测试存储,不依赖 MongoDB,用 K8s 自定义 CRD 存事件
postgresql StatefulSet 辅助存储,存事件分析日志、Janitor 运维操作记录

三、端到端数据流(以 GPU 致命 XID 故障为例)

下面以一个会触发节点级处理的 GPU 故障为例。实际是否隔离、驱逐、修复,还要看部署侧规则和 dry-run 配置。

  1. GPU Health Monitor(DaemonSet 节点进程)通过 DCGM 检测到双比特 ECC 或高风险 XID,生成 HealthEvent;
  2. 事件通过本地 gRPC 推送到本机的 Platform Connectors;
  3. Platform Connectors 做完事件格式校验,把事件写入 mongodb-store,同时调 K8s API 更新本节点的 NodeCondition 或 Kubernetes Event;
  4. MongoDB 写入数据后生成 Change Stream 变更流,所有集群控制面模块异步订阅消费;
  5. fault-quarantine 模块读到致命故障事件后,匹配 CEL 隔离规则,调 K8s API 对节点执行 cordon,按配置决定是否加 taint,并更新事件的隔离状态;
  6. node-drainer 模块感知到节点已隔离后,按 namespace、GPU-only 或 partial-drain 策略调用 K8s Eviction 接口,更新驱逐状态;
  7. fault-remediation 模块读到事件的推荐修复动作(REPLACE_VM),创建 RebootNode 自定义资源 CRD 写入 K8s;
  8. janitor 控制器监听到 CRD 资源,调 janitor-provider 硬件驱动层,执行服务器重启/硬件更换操作;
  9. health-events-analyzer、event-exporter 同步消费事件流:前者做事件模式分析和指标输出,后者推送故障告警到外部系统;
  10. 旁路链路:GPU Monitor 独立输出硬件指标到 Prometheus,不占用故障事件主链路。

其他场景

  • 网卡故障:nic-health-monitor 的流程和 GPU 监控类似,生成 NIC 类型的 HealthEvent,后面的隔离、驱逐、修复链路按同一套框架处理;
  • Slurm 异构集群:节点完成隔离驱逐后,slurm-drain-monitor 自动调 Slurm 接口排空对应的计算节点;
  • Pod 预检失败:Preflight Init 容器校验异常,通过 Unix Socket 直连本机 Platform Connectors 上报,后面的入库、处理流程和硬件故障一致;
  • 非致命告警:只生成 K8s Warning 事件,不触发节点封锁、Pod 驱逐。

四、数据转换和通信规范

数据格式转换

  1. gRPC Protobuf(HealthEvent)→ MongoDB BSON:保留事件主体,额外补创建时间、事件处置状态字段;
  2. MongoDB ChangeStream → 业务模块:解析 fullDocument 提取 HealthEvent 核心结构体;
  3. HealthEvent → K8s Node Condition:提取故障名称、致命标记、故障描述,映射为节点健康状态;
  4. HealthEvent → RebootNode CRD:提取故障节点、推荐修复动作,生成运维工单资源。

通信协议

  1. gRPC(HTTP/2 + Protobuf)

    • 服务端:每节点的 Platform Connectors,默认端口 50051,支持 TLS 加密;
    • 客户端:gpu/nic/syslog-health-monitor、metadata-collector;
    • 用途:节点侧采集组件上报硬件故障事件。
  2. MongoDB Change Streams

    • 连接目标:mongodb-store 集群;
    • 消费方:集群控制面处理模块;
    • 作用:让隔离、驱逐、修复、分析等模块用同一份事件流异步推进。
  3. Kubernetes API

    • 鉴权:各模块独立的 ServiceAccount + RBAC 权限;
    • 操作资源:core/v1 Node/Pod、policy/v1 驱逐 API、自定义 RebootNode CRD。
  4. Unix 域套接字

    • 只有 Preflight Init 容器用,本地上报预检异常,没有网络开销。

五、整体流程图

mermaid diagram

六、设计上的几个特点

  1. 组件边界比较清楚:采集、入库、隔离、驱逐、修复、分析分别由不同组件承担,模块间主要靠事件存储衔接。
  2. HealthEvent 是主接口:新增故障来源时,优先把事件适配到 HealthEvent,而不是让后续模块直接理解原始日志或指标。
  3. Kubernetes 写路径分层:Platform Connectors 写 NodeCondition/Event,Fault Quarantine 写 cordon/taint,Node Drainer 写 Eviction,Remediation 写维护 CR。
  4. 事件可回放和可审计:MongoDB 保存事件和处置状态,模块重启后可以继续从事件状态推进。
  5. 动作强依赖配置:是否 taint、是否 drain、是否 partial drain、是否触发 repair,都不是固定行为,需要看 Helm values 和规则文件。
  6. 底层恢复是外部边界:NVSentinel 负责派发恢复请求和同步状态,真正的重启、换机、GPU reset 仍由 janitor-provider 或外部系统完成。

七、常见问题答疑

1. gpu-health-monitor 这些采集组件如何判断什么时候生成 HealthEvent?

结论:HealthEvent 由采集到的原始信号触发,但最终字段取值还要经过错误码映射、严重程度判断和去重状态处理。

gpu-health-monitor 为例,它作为 DaemonSet 周期性连接本节点或集群内的 DCGM 服务,轮询 DCGM health checks。DCGM 返回 ECC、温度、PCIe、Power、InfoROM、NVLink/NVSwitch 等健康异常后,gpu-health-monitor 会把 DCGM 结果映射成 HealthEvent,填充 checkNameisFatalisHealthyerrorCoderecommendedActionentitiesImpactedmetadata 等字段。

常见触发来源主要有几类:

  • DCGM health watch 返回异常,例如 ECC 错误、NVLink down、PCIe 错误、温度或功耗异常;
  • syslog-health-monitor 从 journal 或 dmesg 里匹配到 XID/SXID、GPU fallen off bus、PCIe AER、驱动崩溃等日志模式;
  • nic-health-monitor 检测到网卡、RDMA、链路或硬件丢包异常;
  • csp-health-monitor 轮询到云厂商维护、迁移或实例硬件风险事件;
  • kubernetes-object-monitor 通过 CEL 规则识别到 K8s 资源状态异常;
  • preflight 准入检查失败,通过本机 socket 上报一次性异常事件;
  • 健康恢复时,组件也可能产生 isHealthy=true 的恢复类事件,用于清除 NodeCondition 或解除隔离状态。

这里要注意两点:第一,DCGM error code 到 recommendedAction 的映射通常由映射文件维护,例如 DCGM_FR_FAULTY_MEMORY 可能映射为 CONTACT_SUPPORT,轻微温度降频可能映射为 NONE;第二,采集组件会维护 entity-level 状态缓存,避免同一故障在单次启动周期内反复上报。

2. Platform Connectors 会标记 GPU 不健康吗?和 fault-quarantine 是否冲突?

结论:会标记不健康状态,但它标的是 NodeCondition 或 Kubernetes Event,不是直接做节点隔离。

platform-connectors 收到 HealthEvent 后,会做格式校验、元数据增强、override 规则处理,然后分别写入事件存储和 Kubernetes。它在 Kubernetes 侧主要更新 NodeCondition 或创建 Kubernetes Event,用于表达“节点为什么不健康”,但不负责执行节点隔离。

一般行为可以按严重程度理解:

  • isFatal=true:更新 Kubernetes NodeCondition,将对应 condition 的 status 置为 True
  • isHealthy=true:更新 Kubernetes NodeCondition,将对应 condition 的 status 置为 False,表示该检查恢复;
  • isFatal=false:通常创建 Kubernetes Event,用于记录 warning 或 transient issue,而不是持久化为故障 condition。

一个 NodeCondition 例子如下:

status:
  conditions:
    - type: GpuMemoryError
      status: "True"
      reason: HardwareFailure
      message: "[DCGM_FR_FAULTY_MEMORY] GPU memory failure detected on GPU 0 - RecommendedAction: RESTART_VM"
      lastTransitionTime: "2025-11-06T10:00:00Z"

这和 fault-quarantine 不冲突,两者职责不同:

  • platform-connectors:表达健康事实,写 HealthEventNodeCondition 或 Kubernetes Event;
  • fault-quarantine:消费事件和 Node 上下文,根据 CEL 规则决定是否 cordon、是否加 taint
  • node-drainer:在节点被隔离后,按策略驱逐已有业务 Pod。

所以两者不冲突。Platform Connectors 说明“为什么不健康”,Fault Quarantine 决定“是否禁止继续调度”,Node Drainer 再决定“是否把已有负载迁走”。

3. fault-quarantine 的 CEL 规则在哪里配置?常见规则和处理结果是什么?

结论:规则通常在 Helm values 里的 fault-quarantine.ruleSets 配置,生产环境一般会用 values overlay 覆盖默认规则。

配置例子如下:

fault-quarantine:
  ruleSets:
    - version: "1"
      name: "GPU fatal error ruleset"
      priority: 100
      match:
        all:
          - kind: "HealthEvent"
            expression: "event.agent == 'gpu-health-monitor' && event.componentClass == 'GPU' && event.isFatal == true"
          - kind: "Node"
            expression: "!('k8saas.nvidia.com/ManagedByNVSentinel' in node.metadata.labels && node.metadata.labels['k8saas.nvidia.com/ManagedByNVSentinel'] == 'false')"
      cordon:
        shouldCordon: true
      taint:
        key: "nvidia.com/gpu-error"
        value: "fatal"
        effect: "NoSchedule"

CEL 规则可以同时访问 event 和 Kubernetes node 对象,因此不只能写“fatal 就隔离”,也可以按节点 label、资源池、GPU 型号、维护窗口等上下文过滤。

常见规则不需要很多,生产上反而建议从少量高置信度规则开始:

  • GPU Health Monitor 上报 fatal GPU event,执行 cordon;
  • Syslog Health Monitor 上报 fatal XID/SXID 或 GPU fallen off bus,执行 cordon;
  • errorCode 区分高风险和低风险错误,例如排除某些已知误报 XID;
  • 对 warning 或 degraded 状态只加 PreferNoSchedule,不立即强隔离;
  • 启用 circuit breaker,避免规则误判导致短时间大面积节点下线。

规则命中后的处理结果主要是:

  • cordon:设置 node.spec.unschedulable=true,阻止新 Pod 调度到该节点;
  • 可选 taint:例如 nvidia.com/gpu-xid-error=true:NoSchedule
  • 添加 label 或 annotation,记录隔离原因、时间、执行组件;
  • 更新事件存储中的隔离状态,供后续 node-drainerfault-remediation 消费;
  • 健康恢复后,如果相关检查都恢复,可自动 uncordon 并清理 taint、label、annotation。

4. node-drainer 是驱逐整机所有负载,还是可以精确到某些 GPU?

结论:默认更接近“按节点驱逐 eligible pods”,但配置满足条件时可以做到 GPU-only 或按故障 GPU 做 partial drain。

node-drainer 的基本逻辑是:监听已经被 quarantine 的节点,按 namespace 策略筛选 Pod,然后调用 Kubernetes Eviction API。系统命名空间和 DaemonSet 通常会被跳过,业务命名空间按配置选择不同驱逐模式。

配置例子如下:

node-drainer:
  partialDrainEnabled: true
  drainGPUPods: false
  systemNamespaces: "^(nvsentinel|kube-system|gpu-operator|gmp-system|network-operator)$"
  userNamespaces:
    - name: "training"
      mode: "DeleteAfterTimeout"
    - name: "inference-*"
      mode: "Immediate"
    - name: "*"
      mode: "AllowCompletion"

namespace 层面的驱逐模式有三种:

  • Immediate:立即通过 Eviction API 驱逐,适合无状态服务;
  • AllowCompletion:等待 Pod 按自身 terminationGracePeriodSeconds 优雅退出;
  • DeleteAfterTimeout:等待训练任务 checkpoint,超过 deleteAfterTimeoutMinutes 后强制删除。

真正影响驱逐范围的是下面三个开关或条件:

  1. 整节点 eligible pods 驱逐:默认模式,驱逐该节点上配置命名空间内的业务 Pod;
  2. 只驱逐 GPU Pod:设置 drainGPUPods: true 后,只驱逐请求 GPU 资源的 Pod,CPU-only 工作负载保留;
  3. 局部 GPU 驱逐:设置 partialDrainEnabled: true,且事件是 COMPONENT_RESET,并且 entitiesImpacted 中包含具体 GPU UUID 时,可以只驱逐使用该故障 GPU 的 Pod。

局部 GPU 驱逐依赖 metadata-collector 维护 Pod 到 GPU UUID 的分配关系,通常通过 Pod annotation 表达,例如:

annotations:
  dgxc.nvidia.com/devices: '{"devices":{"nvidia.com/gpu":["GPU-123"]}}'

这里最容易踩坑的是 partial drain 和 remediation 动作不一致。如果 COMPONENT_RESET 最后实际做的是整机 reboot,那么只驱逐部分 Pod 就不安全,因为没被驱逐的 Pod 也会被重启影响。节点级动作如 RESTART_VMRESTART_BMREPLACE_VM 通常应执行整节点 drain。

5. health-events-analyzer 的具体原理是什么?

结论:它不是采集入口,也不直接做 cordon、drain 或创建维修 CRD。它的价值在于把低层级事件关联起来,识别更高置信度的故障模式。

核心流程是:

  1. 从 MongoDB Change Streams 监听新 HealthEvent,或按时间窗口查询历史事件;
  2. 加载分析规则,历史实现通常使用 TOML 配置和 MongoDB Aggregation Pipeline;
  3. 按 node、GPU UUID、errorCode、agent、时间窗口等字段聚合事件;
  4. 判断是否满足某个故障模式;
  5. 命中后生成新的健康事件、升级严重程度,或补充更明确的 recommendedAction
  6. 通过 publisher 把分析结果重新发布回事件链路,供 fault-quarantinenode-drainerfault-remediationevent-exporter 消费。

适合交给 Analyzer 的场景,一般是“单条事件不足以下判断,但组合或重复出现就有意义”的问题:

  • XID Burst:同一节点或同一 GPU 在短时间内多次出现 XID;
  • ECC 累计升级:单次可纠正 ECC 只记录,时间窗口内反复出现则升级;
  • 驱动异常关联:driver crash、GPU 不可访问、Pod 失败、NodeNotReady 在短时间内组合出现;
  • 局部 GPU 故障判断:只影响单张 GPU 时倾向局部 reset 和 partial drain;
  • 云维护事件关联:云厂商维护窗口叠加节点负载状态,提前触发隔离或迁移建议。

当前公开信息显示,Analyzer 历史上和 MongoDB Pipeline 绑定较强。后续是否已经切到 CEL 或数据库无关规则,需要以目标版本源码为准;从设计上看,更合理的是“Store Client 取候选事件 + 规则引擎在应用层判断”。

6. remediation 恢复后,集群和负载怎么恢复?

结论:恢复后分两条线看。NVSentinel 负责节点健康闭环和重新入池;业务负载能不能恢复到原进度,主要看 Kubernetes 控制器、训练调度系统和业务 checkpoint。

节点恢复的大致流程是:

  1. fault-remediation 根据 recommendedAction 创建维护类 CR,例如 RebootNodeTerminateNode 或自定义 RecoveryRequest
  2. janitor 或外部恢复系统 watch 该 CR,执行节点重启、GPU reset、实例替换、裸金属维修或调用外部 IMX-CM 等动作;
  3. 恢复系统把结果写回 CR status.conditions,例如 NodeReady=TrueRecoveryCompleted=True
  4. 节点恢复后,gpu-health-monitorsyslog-health-monitor 等继续采集健康状态;
  5. 如果检查恢复,采集组件产生 isHealthy=true 的恢复类 HealthEvent
  6. platform-connectors 根据恢复事件把相关 NodeCondition 置为 False 或清除故障状态;
  7. fault-quarantine 确认相关健康检查都恢复后,执行 uncordon,移除 quarantine 相关 taint、label、annotation;
  8. 节点重新变为可调度,回到资源池。

因此,节点能否重新入池不能只看 remediation CR 完成,还要看后续健康事件是否确认恢复,以及 quarantine 状态是否解除。

负载恢复则主要由 Kubernetes 原生控制器或上层调度系统完成:

  • Deployment / ReplicaSet:Pod 被驱逐后自动在健康节点创建新副本;
  • StatefulSet:按 StatefulSet 语义重建对应 Pod;
  • Job / CronJob:是否重试取决于 backoffLimit、restart policy 和任务状态;
  • Kubeflow、Volcano、Run:ai、Ray、MPI Operator、Slurm 等训练调度系统:根据各自语义重新排队、重启或恢复作业;
  • 裸 Pod:被驱逐后不会自动恢复。

如果是长时间训练任务,能不能续跑不由 NVSentinel 保证,而取决于业务是否有 checkpoint、任务控制器是否支持重试、调度系统是否会重新提交任务。

对于不同恢复动作,后续行为也不同:

  • RESTART_VMRESTART_BMREPLACE_VM:通常整节点 drain,修复后节点重新入池,原负载由调度系统在任意健康节点重建;
  • COMPONENT_RESET:如果启用 partial drain 且事件包含故障 GPU UUID,可以只驱逐使用该 GPU 的 Pod,reset 后节点继续可用;
  • 外部恢复系统介入:NVSentinel 通过 CR status 和后续健康事件确认结果,不直接执行底层根因定位和维修动作。

从系统边界看,NVSentinel 负责“发现故障、隔离节点、驱逐风险负载、派发恢复请求、确认节点恢复并重新入池”。业务是否恢复到原进度,属于 workload controller、训练框架和 checkpoint 机制的职责。