NVSentinel 数据流梳理
原文参考:https://docs.nvidia.com/nvsentinel/architecture/data-flow
一、整体架构
1. 设计思路
从公开文档和仓库结构看,NVSentinel 的主链路是事件驱动的:节点侧组件先把异常整理成 HealthEvent,platform-connectors 负责入库和同步 Kubernetes 状态,后面的隔离、驱逐、修复模块再从事件存储里异步消费。MongoDB Change Streams 在这里承担的是事件分发和状态流转的作用。
主链路可以按下面理解:
- 节点上的采集器(生产者)通过本地 gRPC 把故障事件报上来;
- Platform Connectors 负责集中接收、做格式校验,然后持久化到存储层;
- 集群控制面的各个模块订阅数据库变更流,异步消费处理故障;
- 隔离、驱逐、修复这些动作主要通过 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 配置。
- GPU Health Monitor(DaemonSet 节点进程)通过 DCGM 检测到双比特 ECC 或高风险 XID,生成 HealthEvent;
- 事件通过本地 gRPC 推送到本机的 Platform Connectors;
- Platform Connectors 做完事件格式校验,把事件写入 mongodb-store,同时调 K8s API 更新本节点的 NodeCondition 或 Kubernetes Event;
- MongoDB 写入数据后生成 Change Stream 变更流,所有集群控制面模块异步订阅消费;
- fault-quarantine 模块读到致命故障事件后,匹配 CEL 隔离规则,调 K8s API 对节点执行 cordon,按配置决定是否加 taint,并更新事件的隔离状态;
- node-drainer 模块感知到节点已隔离后,按 namespace、GPU-only 或 partial-drain 策略调用 K8s Eviction 接口,更新驱逐状态;
- fault-remediation 模块读到事件的推荐修复动作(REPLACE_VM),创建 RebootNode 自定义资源 CRD 写入 K8s;
- janitor 控制器监听到 CRD 资源,调 janitor-provider 硬件驱动层,执行服务器重启/硬件更换操作;
- health-events-analyzer、event-exporter 同步消费事件流:前者做事件模式分析和指标输出,后者推送故障告警到外部系统;
- 旁路链路:GPU Monitor 独立输出硬件指标到 Prometheus,不占用故障事件主链路。
其他场景
- 网卡故障:nic-health-monitor 的流程和 GPU 监控类似,生成 NIC 类型的 HealthEvent,后面的隔离、驱逐、修复链路按同一套框架处理;
- Slurm 异构集群:节点完成隔离驱逐后,slurm-drain-monitor 自动调 Slurm 接口排空对应的计算节点;
- Pod 预检失败:Preflight Init 容器校验异常,通过 Unix Socket 直连本机 Platform Connectors 上报,后面的入库、处理流程和硬件故障一致;
- 非致命告警:只生成 K8s Warning 事件,不触发节点封锁、Pod 驱逐。
四、数据转换和通信规范
数据格式转换
- gRPC Protobuf(HealthEvent)→ MongoDB BSON:保留事件主体,额外补创建时间、事件处置状态字段;
- MongoDB ChangeStream → 业务模块:解析 fullDocument 提取 HealthEvent 核心结构体;
- HealthEvent → K8s Node Condition:提取故障名称、致命标记、故障描述,映射为节点健康状态;
- HealthEvent → RebootNode CRD:提取故障节点、推荐修复动作,生成运维工单资源。
通信协议
-
gRPC(HTTP/2 + Protobuf)
- 服务端:每节点的 Platform Connectors,默认端口 50051,支持 TLS 加密;
- 客户端:gpu/nic/syslog-health-monitor、metadata-collector;
- 用途:节点侧采集组件上报硬件故障事件。
-
MongoDB Change Streams
- 连接目标:mongodb-store 集群;
- 消费方:集群控制面处理模块;
- 作用:让隔离、驱逐、修复、分析等模块用同一份事件流异步推进。
-
Kubernetes API
- 鉴权:各模块独立的 ServiceAccount + RBAC 权限;
- 操作资源:core/v1 Node/Pod、policy/v1 驱逐 API、自定义 RebootNode CRD。
-
Unix 域套接字
- 只有 Preflight Init 容器用,本地上报预检异常,没有网络开销。
五、整体流程图

六、设计上的几个特点
- 组件边界比较清楚:采集、入库、隔离、驱逐、修复、分析分别由不同组件承担,模块间主要靠事件存储衔接。
- HealthEvent 是主接口:新增故障来源时,优先把事件适配到 HealthEvent,而不是让后续模块直接理解原始日志或指标。
- Kubernetes 写路径分层:Platform Connectors 写 NodeCondition/Event,Fault Quarantine 写 cordon/taint,Node Drainer 写 Eviction,Remediation 写维护 CR。
- 事件可回放和可审计:MongoDB 保存事件和处置状态,模块重启后可以继续从事件状态推进。
- 动作强依赖配置:是否 taint、是否 drain、是否 partial drain、是否触发 repair,都不是固定行为,需要看 Helm values 和规则文件。
- 底层恢复是外部边界: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,填充 checkName、isFatal、isHealthy、errorCode、recommendedAction、entitiesImpacted 和 metadata 等字段。
常见触发来源主要有几类:
- 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:更新 KubernetesNodeCondition,将对应 condition 的status置为True;isHealthy=true:更新 KubernetesNodeCondition,将对应 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:表达健康事实,写HealthEvent、NodeCondition或 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-drainer、fault-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后强制删除。
真正影响驱逐范围的是下面三个开关或条件:
- 整节点 eligible pods 驱逐:默认模式,驱逐该节点上配置命名空间内的业务 Pod;
- 只驱逐 GPU Pod:设置
drainGPUPods: true后,只驱逐请求 GPU 资源的 Pod,CPU-only 工作负载保留; - 局部 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_VM、RESTART_BM、REPLACE_VM 通常应执行整节点 drain。
5. health-events-analyzer 的具体原理是什么?
结论:它不是采集入口,也不直接做 cordon、drain 或创建维修 CRD。它的价值在于把低层级事件关联起来,识别更高置信度的故障模式。
核心流程是:
- 从 MongoDB Change Streams 监听新
HealthEvent,或按时间窗口查询历史事件; - 加载分析规则,历史实现通常使用 TOML 配置和 MongoDB Aggregation Pipeline;
- 按 node、GPU UUID、errorCode、agent、时间窗口等字段聚合事件;
- 判断是否满足某个故障模式;
- 命中后生成新的健康事件、升级严重程度,或补充更明确的
recommendedAction; - 通过 publisher 把分析结果重新发布回事件链路,供
fault-quarantine、node-drainer、fault-remediation和event-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。
节点恢复的大致流程是:
fault-remediation根据recommendedAction创建维护类 CR,例如RebootNode、TerminateNode或自定义RecoveryRequest;janitor或外部恢复系统 watch 该 CR,执行节点重启、GPU reset、实例替换、裸金属维修或调用外部IMX-CM等动作;- 恢复系统把结果写回 CR
status.conditions,例如NodeReady=True、RecoveryCompleted=True; - 节点恢复后,
gpu-health-monitor、syslog-health-monitor等继续采集健康状态; - 如果检查恢复,采集组件产生
isHealthy=true的恢复类HealthEvent; platform-connectors根据恢复事件把相关NodeCondition置为False或清除故障状态;fault-quarantine确认相关健康检查都恢复后,执行 uncordon,移除 quarantine 相关 taint、label、annotation;- 节点重新变为可调度,回到资源池。
因此,节点能否重新入池不能只看 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_VM、RESTART_BM、REPLACE_VM:通常整节点 drain,修复后节点重新入池,原负载由调度系统在任意健康节点重建;COMPONENT_RESET:如果启用 partial drain 且事件包含故障 GPU UUID,可以只驱逐使用该 GPU 的 Pod,reset 后节点继续可用;- 外部恢复系统介入:NVSentinel 通过 CR status 和后续健康事件确认结果,不直接执行底层根因定位和维修动作。
从系统边界看,NVSentinel 负责“发现故障、隔离节点、驱逐风险负载、派发恢复请求、确认节点恢复并重新入池”。业务是否恢复到原进度,属于 workload controller、训练框架和 checkpoint 机制的职责。