ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

openrig实战:多GPU机群统一监控、调度与自愈的落地记录

openrig实战:多GPU机群统一监控、调度与自愈的落地记录

如果你手里只有一两台带 GPU 的机器,SSH 上去敲几行nvidia-smi完全够用。但当机器的数量上来之后,光靠人肉 SSH 巡检就撑不住了。我去年接手一个混着 3090、4090、A5000 的八台 GPU 机群,那段时间最大的体会就是:缺一套能统一监控、调度、自动恢复的开源框架。和同事调研一圈之后,我选择了 openrig,把整条链路切过去之后,夜里终于能睡个踏实觉了。

这篇内容不是 openrig 的官方文档复述,而是我基于真实交付环境整理的部署与使用记录。如果你也管理多台 GPU 工作站、渲染节点或小型训练集群,正被"每台机器都要单独登录查看""任务卡死没人知道""显卡利用率参差不齐"这类问题困扰,那这篇文章应该能帮你省下不少时间。我会从为什么选它、怎么部署、内部机制怎么运作,到实际跑起来踩过的坑,一步步拆开讲。

1. 从人肉巡检到统一框架:openrig 解决的痛点

1.1 散养 GPU 机群的真实状态

先说个场景。八台机器分布在三个机房,型号还不一样:有的是双卡 4090,有的是四卡 A5000,还有一台老旧的 2080 Ti 在角落吃灰。表面上这没什么,但真跑起业务来就是另一回事了。

我之前的日常大概是这样的:早上到工位,先 SSH 登录第一台机器,执行nvidia-smi --query-gpu=utilization.gpu,memory.used,temperature.gpu --format=csv,记录一下;然后退出,登录第二台,再记录;然后第三台……等八台都跑完,半小时过去了。这还只是看一眼状态。如果发现某张卡利用率很低,还得手动排查是任务跑完了、还是代码卡住了、还是有人在上面偷偷跑了别的东西。

这还不是最折磨人的。最折磨人的是半夜。之前有一回,一台机器上的渲染任务因为显存分配失败挂住了,进程还在,但实际已经不再产出任何结果。我第二天早上才发现,等于整整浪费了一个通宵的机时。这类"进程存活但业务停滞"的问题,SSH 巡检很难及时发现。

后来我逐渐形成了一个经验:超出一台机器之后,绝大多数 GPU 运维问题都不是"硬件坏了",而是"状态不可见"。不知道哪张卡是闲置的,不知道哪个任务在空转,不知道哪台机器快把磁盘写满了,那所有调度决策都是瞎猜。openrig 说白了就是来解决这个问题的:它把一个机群的硬件资源、运行任务、健康状态集中到一个视图里,并且能根据预设策略自动做点事情。

1.2 openrig 的定位:不只是监控面板

很多了解过监控方案的人,看到 openrig 的第一反应是:"这不就是个带界面的 nvidia-smi 吗?"我第一次接触时也是这么想的,但实际用下来发现定位完全不一样。

openrig 的核心不是"看",而是"管"。它由三个角色构成:

  • openrig-server:控制中心,负责收集所有节点的状态、分发任务、执行自愈策略、暴露 API 和面板。
  • openrig-agent:部署在每台 GPU 机器上,负责采集本机硬件数据、执行服务端下发的命令、上报心跳。
  • openrig-cli:命令行客户端,把管理指令发给 server,支持查看节点、提交任务、调整策略等。

简单点说,它是把监控、任务调度、故障自愈这三件事揉在了一起。监控给的是眼睛,调度给的是手,自愈给的是最简单的应激反应。大多数自建方案往往只做到了第一项,后两项完全靠人肉补齐。而 openrig 至少在"自动做决策"这一点上,给了我一个可以落地的框架。

举个例子。之前我需要每周手动清理一次显存残留进程,因为很多训练脚本退出的时候不会干净释放显存。用 openrig 之后,我在 agent 的配置里打开了一个名为gpu_residual_clean的选项,它可以识别出哪些进程是"没有对应父任务"的残留进程,然后按策略选择是告警还是自动清理。这种能力,单靠 Prometheus 那套采集链路是做不到的,因为它涉及对进程生命周期的判断,而不仅仅是数值采集。

1.3 和自拼 Prometheus + Grafana 方案比,差在哪

我之前也尝试过用 Prometheus + node_exporter + dcgm_exporter + Grafana 拼一套监控。说实话,面板做出来之后确实很好看,各种曲线都很丝滑。但用了一段时间之后,我发现这套方案有几个绕不开的问题:

对比维度自拼 Prometheus + Grafanaopenrig
数据采集只能拿到 exporter 暴露的数值指标除了指标,还采集进程级信息和任务状态
故障处理只能配置告警规则,收到告警后人肉处理支持预设自愈动作,自动隔离/重启/恢复
任务管理不涉及,需要另接调度器自带轻量任务管理,能提交和跟踪任务
部署维护组件多,exporter、prometheus、grafana 都要各自维护一个 server、每个节点一个 agent,简单很多
上手成本需要理解 PromQL、告警规则、exporter 指标语义一个配置文件加几条命令即可

倒不是说 Prometheus 方案一无是处。如果你已经有成熟的 Prometheus 体系,openrig 也可以把指标吐给 Prometheus 做长期归档。但如果你像我一样,核心诉求是"把机群管起来,而不是画一堆图",那 openrig 这种一体化的思路确实更顺手。

2. 部署前先想清楚架构:server 和 agent 如何分工

2.1 为什么一定要拆成控制端和代理端

刚开始我自己写管理脚本的时候,最大的失误就是没有拆分控制端和执行端。那时候我是从一台"管理机"上通过 SSH 连到所有机器去执行命令,脚本里全是ssh user@host "nvidia-smi ..."这样的行。看起来能跑,实际上脆弱得不行:只要管理机和目标机器之间网络稍微抖动,或者目标机器的 SSH 连接数超限,整个脚本就卡在那里不动了。

openrig 的架构拆成 server 和 agent 之后,通信模型完全变了,agent 主动往外连,而不是 server 往里连。这里有个容易被忽视的好处:你不需要在所有机器上开放 SSH 入站给管理机,agent 只要能够访问到 server 的端口就行。对于跨机房甚至跨公网的部署场景,这个模型要安全得多。

另外,agent 是常驻进程,它自己会采集数据、检测异常、执行本地操作。就算和 server 的连接暂时断开,agent 依然能独立做一些本地保护动作,比如温度过高时降频、磁盘写满时清理临时文件。这就避免了"中心化控制"最怕的故障场景——控制端挂了,所有节点跟着失明。

2.2 网络规划与端口约定

部署之前,建议先把网络规划好。我踩过的最蠢的坑就是,server 默认端口被同一台机器上的其他服务占用了,导致 agent 一直连接不上,我还在怀疑配置文件写错。

openrig 的端口约定不复杂:

  • server 监听端口:7820/tcp,所有 agent 和 cli 都连这个端口。
  • agent 上报端口:agent 不监听端口,它主动向 server 的7820发起长连接。
  • Web 面板端口:7821/tcp,可以在 server 配置文件里关掉,单独挂到反代后面。

我自己的习惯是,server 单独放一台 2C4G 的小机器上,所有 GPU 节点都只开7820的出站访问。Agent 的配置里写 server 地址时,建议写内网 IP 而不是域名,省得 DNS 出问题。还有一个小细节:如果节点和 server 之间有防火墙,要同时放行 TCP 和 UDP。Agent 握手阶段会先用 TCP 建立主连接,但后续可能用 UDP 做轻量心跳,别把 UDP 封了然后排查半天。

2.3 配置文件这样写,后续才不折腾

openrig 的配置分成 server 端和 agent 端,都用 YAML 格式。我见过不少人部署的时候图省事,全部用默认参数跑,结果上线之后各种难受。下面这份是我目前在用的 server 配置,关键字段都有注释:

# /etc/openrig/server.yaml server: listen: 0.0.0.0:7820 data_dir: /var/lib/openrig panel: enabled: true listen: 0.0.0.0:7821 agent: heartbeat_interval: 15 # agent 每隔 15 秒上报一次心跳 heartbeat_timeout: 90 # 连续 90 秒没收到心跳,节点标记为离线 reconnect_backoff: true # 断线重连采用指数退避,避免同时重连风暴 scheduler: policy: gpu_memory_first # 任务放置策略:优先塞进显存余量最大的节点 reserved_memory_percentage: 10 # 每张卡保留 10% 显存不做分配,防止 OOM max_restart_count: 2 # 自愈模块对单个任务的最大重启次数 alert: webhook: endpoints: - url: "https://example.com/webhook/openrig" events: ["node_offline", "gpu_overtemp", "task_failed"]

这里我想解释两个容易被忽略的字段,因为它们在后续实际运行中帮了我大忙。

第一个是heartbeat_timeout。默认值是 45 秒,但我调到了 90 秒。后面我会专门讲,这 45 秒在跨机房网络下太容易误报了。第二个是reserved_memory_percentage,这个非常关键。很多调度器默认把显存塞得满满的,结果任务启动的时候系统还要吃点显存做上下文,直接 OOM。保留 10% 之后,虽然单卡利用率上限降了一点,但整体稳定性高了很多,任务失败率明显下降。

agent 端的配置简单一些,核心是三个东西:

# /etc/openrig/agent.yaml server: address: 192.168.10.10:7820 token: "YOUR_NODE_TOKEN" collector: interval: 5 # 本机硬件数据采集间隔 gpu_metrics: true # 采集显存、利用率、温度、功耗 process_metrics: true # 采集 GPU 上的进程列表及显存占用 disk_metrics: true # 采集磁盘容量和 inode 余量 local_protect: enable_gpu_overtemp_protect: true overtemp_threshold: 87 # GPU 温度超过 87℃ 时,主动降频保护

agent 端的local_protect是我非常看重的一个能力。它意味着即使 server 完全失联,agent 仍然会在本地做保护动作。对于无人值守机房,这一层兜底比任何花哨的远程告警都实在。

3. 从零到一:openrig 多节点环境搭建实录

3.1 前置条件:驱动、容器、系统服务

在正式部署 openrig 之前,需要确认每台 GPU 机器的基础环境。我踩过最大的坑是:某台机器的显卡驱动是老的 470 系列,openrig 通过 NVML 采集温度、功耗、显存信息的时候,部分字段读不到,导致面板上显示一堆 NaN。

所以我的建议是,先把所有机器的驱动版本统一一下。不用追最新,但要保证在同一个大版本内。比如统一用 535 或 550 系列驱动,配 CUDA 12.x 都行。检查方法很常规:

nvidia-smi

看右上角 Driver Version 即可。如果版本太老,建议先升级驱动再部署 openrig。不要指望监控工具能绕过驱动层去读硬件信息,NVML 就是兜底方案。

另外,openrig 的 agent 默认通过 Docker 或 Podman 拉起任务容器。如果机器上还没有装容器运行时,需要先装好。我这边用的是 Docker Engine +nvidia-container-toolkit。装完可以用一条命令验证:

docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi

看到 GPU 信息正常输出就说明容器能访问显卡了。这一步如果过不了,后面 openrig 的任何任务调度都会在启动容器时失败,而且错误提示可能不太直观。

3.2 部署 openrig-server 并初始化

server 端的部署比我预想的简单。从项目的 release 页面下载对应架构的二进制包,解压之后,把生成的openrig-server放到/usr/local/bin/下,再写一份配置文件:

mkdir -p /etc/openrig /var/lib/openrig wget <release包地址> tar xzf openrig-server-*.tar.gz cp openrig-server /usr/local/bin/ cp conf/server.yaml /etc/openrig/server.yaml

然后生成初始 token,这个 token 是后续每个 agent 加入时需要带的凭证:

openrig-server --generate-token

输出是一长串随机字符串,把它记下来,填进 agent 的token字段。接着初始化数据目录并启动服务:

openrig-server --config /etc/openrig/server.yaml --init systemctl enable openrig-server systemctl start openrig-server

注意,--init只执行一次,它会创建数据库文件、默认告警通道和一些初始配置。重复执行可能会重置数据,别手滑。

启动之后,可以快速验证一下服务状态:

curl -s http://127.0.0.1:7821/api/v1/health

如果返回{"status":"ok"}之类的结果,说明 server 起来了。Web 面板这时也能访问了,用浏览器打开http://<server_ip>:7821能看到一个空的控制台,等 agent 接入之后就会陆续出现节点信息。

3.3 为每台 GPU 机器部署 agent

agent 的部署可以批量做,也可以一台台手动来。机器少的时候我推荐手动,因为每台机器的配置细节可能不同,比如节点名称、采集间隔、是否需要本地保护等。之后再上更多机器时,再用 Ansible 批量铺。

单台机器的部署流程:

wget <agent包地址> tar xzf openrig-agent-*.tar.gz cp openrig-agent /usr/local/bin/ cp conf/agent.yaml /etc/openrig/agent.yaml

然后修改 agent 配置,把 server 地址和 token 填上:

server: address: 192.168.10.10:7820 token: "你生成的TOKEN"

启动 agent:

systemctl enable openrig-agent systemctl start openrig-agent

agent 起来之后,它会自动向 server 注册。在 server 的 Web 面板上,或者通过 CLI,就能看到新节点状态变成online。这里有个值得注意的点:agent 注册之后,server 默认不会自动给它分配任务,除非你把它加入一个"调度池"。这设计是合理的,因为有些机器可能就是专门给某个团队用的,不应该被全局调度器随便占用。

把机器加入调度池的命令也很简单:

openrig node set-pool --node 10.2.3.4 --pool default --enabled true

3.4 第一个节点上线后的验证清单

第一个节点接入后不要急着跑大任务,先在面板上核对几个信息,避免后面出问题还要回头排查:

  • 节点状态是否为online,心跳时间戳是否在持续刷新。
  • GPU 列表中每张卡的型号、显存容量、驱动版本是否和实际一致。
  • 温度和功耗有没有实际数值,是不是 NaN。
  • 在节点详情页能看到当前 GPU 上运行的进程列表,如果没有任务,应该是空的。

如果这些都没问题,就可以提交一个最小的测试任务试试调度链路:

openrig task run \ --node 10.2.3.4 \ --gpu 1 \ --image nvidia/cuda:12.1.0-base-ubuntu22.04 \ --cmd "nvidia-smi"

任务跑完之后,在面板的任务列表里应该能看到状态变成completed,并且日志里有 nvidia-smi 的输出。这一步验证通过,基本上整个 agent 到 server 的链路就已经通了。之后你再提交真实任务,就只是命令和镜像层面的区别。

4. 核心机制拆解:数据采集、调度和自愈是怎么协同的

4.1 采集层:为什么不用现成的 dcgm_exporter

很多人问过一个问题:openrig 的采集层为什么不用 DCGM 那套?DCGM 是 NVIDIA 官方的数据中心 GPU 管理方案,dcgm-exporter配合 Prometheus 也确实能拿到很完整的指标。

我的看法是:DCGM 指标丰富但偏向"硬件健康与性能",而 openrig 需要的采集维度里,有一部分是进程级和任务级的。比如某个 GPU 上跑了哪些进程、每个进程占多少显存、这个进程是不是某个任务容器的子进程。这些信息从 DCGM 里拿不到,只能通过 NVML 的进程枚举接口外加对容器运行时 PID 空间的映射来拿。

openrig agent 的采集器默认做这几件事:

  1. 调用 NVML 获取每张卡的利用率、显存、温度、功耗、风扇转速。
  2. 枚举每张卡上的进程列表,记录 PID、显存占用、进程名。
  3. 读取系统层的磁盘、内存、负载、inode 使用率。
  4. 将容器运行时(Docker/Podman)的容器列表和 GPU 进程做关联,确定每个进程属于哪个任务。

每个采集周期默认 5 秒一次。这个间隔是经过权衡的:太快会增加 agent 的 CPU 开销,太慢又会让调度器对显存变化的感知滞后。对于绝大多数训练和渲染场景,5 秒足够。

4.2 调度层:资源预留和任务放置逻辑

openrig 的调度策略不像 Kubernetes 那么复杂,它更轻量。它要解决的问题很明确:来了一个任务,需要几张卡、多少显存和多少 CPU,应该放到哪台机器上。

默认的gpu_memory_first策略,规则大致是这样的:

  1. 在所有在线且处于调度池内的节点中,过滤掉不满足最小显存要求的节点。
  2. 对剩余节点按当前空闲显存总量排序,空闲多的优先。
  3. 在单节点内部,按"满足任务显存需求的卡中,显存余量最小者优先"的方式选卡,这样可以尽量把显存碎片压到最低。

我特别喜欢"显存余量最小者优先"这个细节。很多调度器会倾向于把任务分散到不同机器上,结果每台机器都剩一些碎显存,最终谁也没法跑大任务。而 openrig 这种"同类任务尽量聚堆"的思路,反而能腾出一整台干净的机器给后面的大任务。

资源预留和调度是两件事。openrig 在调度时会把reserved_memory_percentage考虑进去,所以不会出现"任务刚启动就 OOM"的情况。我设置的 10% 预留主要覆盖两类开销:一是容器运行时和驱动层自己吃的显存,二是 PyTorch 等框架的缓存预分配。实测下来训练任务的失败率降低了不少。

4.3 自愈层:先隔离、再重启、后恢复

openrig 的自愈模块是我最终决定长期使用它的理由。它执行任何恢复动作之前,都会先做一个"隔离"。什么意思?比如某张卡温度到了 90 度,自愈模块不会立刻杀掉上面正在跑的任务,而是先把这张卡标记为"不健康",让调度器不再往这张卡上分配新任务,同时通知正在运行任务的容器暂停下来。确认温度降下来之后,再恢复执行。

这个"先隔离再恢复"的过程,避免了最坏的情况:任务正在写 checkpoint 写到一半,GPU 被强制重启,写坏了文件。我在生产环境见过太多因为"探测到异常就直接 kill"导致的二次故障,所以特别认可这种稳妥的处理方式。

自愈模块支持的策略类型包括:

  • node_offline_alert:节点离线,只告警不动作。
  • gpu_overtemp_isolate:GPU 温度超限,隔离该卡并暂停相关任务。
  • task_failed_restart:任务失败,且配置允许重启时,自动重启(最多max_restart_count次)。
  • disk_space_low_vacuum:磁盘空间不足时,清理指定目录下的临时文件。

每个策略都可以在 server 配置里单独开关。我建议最开始只开告警和隔离,不要开自动重启。等你对任务行为和数据落盘情况足够了解之后,再放开自动重启。

4.4 时序数据存储与留存策略

openrig 会把采集到的指标写到自己的时序数据库里。默认存储位置是 server 机器的/var/lib/openrig目录。这里有一个很容易被忽略的问题:如果不设保留策略,时间长了磁盘会被写满。我之前就遇到过,跑了两个月之后,一块 200G 的数据盘被指标数据填满了。

推荐配置一个合理的保留周期。训练和渲染场景下,15 天以内的 5 秒级数据是有用的,超过 30 天的数据基本不会有人再看。我目前保留策略是这样的:

  • 5 秒级原始数据:保留 7 天。
  • 1 分钟聚合数据:保留 30 天。
  • 5 分钟聚合数据:保留 90 天。

这样既保证了最近几天排查问题时有足够细的粒度,又不会让磁盘无限膨胀。配置方式是在 server.yaml 里加一个 retention 段:

storage: retention: raw: 7d minute: 30d five_minute: 90d

另外,如果公司已经有一套 Prometheus,也可以让 openrig 把指标远程写入到 Prometheus 里做长期归档。openrig 在 server 端内置了/metrics接口,用 Prometheus 的 remote write 或者直接按 scrape 方式拉取都可以。我自己是把 openrig 接入 Prometheus 的,长期趋势图还是 Grafana 更顺手。

5. 真实跑起来之后踩过的四个坑

5.1 坑一:心跳超时误判,网络小抖动把节点踢下线

上线第一周,我就遇到了一个特别烦人的问题:某台机器明明在正常跑任务,openrig 面板却时不时把它标记成offline,过几分钟又自动变回online。一开始我怀疑是 agent 挂了,但登录到那台机器上看,agent 进程好好的。

后来排查网络日志才发现,问题出在跨机房链路的网络抖动上。机房之间的专线偶尔会有几秒钟的延迟波动,而 agent 每 15 秒发一次心跳包,server 连续 45 秒没收到心跳就判定离线。一旦赶上两次心跳都卡在抖动窗口里,节点就被误判了。

这个问题的修复方案有两个层面,我都做了:

一是把heartbeat_timeout从默认的 45 秒调到了 90 秒,给网络抖动留出余量。二是把心跳模式从"固定间隔"改成了"指数退避",也就是 agent 发现心跳发送失败后,会按 1 秒、2 秒、4 秒……的间隔重试,而不是傻傻地等下一个 15 秒周期。

经过这两项调整,误报率从每天好几次降到了几乎为零。所以遇到这种问题,先观察,再调参,不要一上来就怀疑 agent 坏了。

5.2 坑二:显存"假占用",残留进程让调度直接卡死

有一次我发现一个奇怪现象:某张 4090 的显存明明没任务在用,却一直显示被占用了 12GB,调度器死活不往这张卡上放新任务。登录到机器上执行nvidia-smi一看,发现上面挂着一个已经没有父进程的 Python 进程,占着显存不放。

这类问题在长跑训练任务的机器上非常常见。任务容器异常退出后,容器本身被清理了,但某些子进程没有被 Kill 干净,显存就被这些孤儿进程一直占着。普通的监控面板只能告诉你"这张卡用了 12GB",却区分不了这是健康占用还是残留垃圾。

openrig 的 agent 上报进程信息时,会额外标注这个进程是否属于当前活跃任务。如果进程的父进程已经不存在、容器也已经退出,agent 就会把它标记为orphan。我在 agent 配置里打开了gpu_residual_clean选项,让 agent 每天凌晨自动清理一次孤儿进程。打开之前最好先在手动模式下跑几天,确认没有误杀正常进程再开启自动清理。

手动清理的指令也很有用,遇到紧急情况不必等定时任务:

openrig node vacuum --node 10.2.3.4 --gpu 2

这条命令会列出该卡上的孤儿进程,让你选择删除或保留。注意,vacuum默认是交互式的,直接加--force才会非交互执行。首次使用别用--force。

5.3 坑三:自动重启误杀了带 checkpoint 的训练任务

这是让所有运维同学都血压飙升的一类问题。某个训练任务因为显存 OOM 异常退出了,自愈模块按照配置自动拉起了一个新的容器。结果新容器从头开始训练,旧容器写了一半的 checkpoint 被初始化操作覆盖掉了。十几个小时的训练进度,归零。

问题出在"重启策略"的粒度上。默认重启策略对所有任务一视同仁,但实际任务差别太大了:推理任务挂了重启完全没问题,训练任务则需要考虑 checkpoint 的续跑能力。

我的解决方案很简单:在任务级别加一个protected标签,标记为 protected 的任务不会被自愈模块自动重启,只会告警。这样,重要任务即便失败,也只会通知到人,由我手动决定怎么恢复。

一个 openrig 任务提交示例:

openrig task run \ --node 10.2.3.4 \ --gpu 2 \ --image training-image:v2 \ --protected true \ --cmd "python train.py --resume /data/checkpoints/latest.ckpt"

保护任务、手动处理听起来好像不如全自动高级,但在真实生产环境里,手动介入"重要任务的失败恢复"反而是最稳妥的做法。全自动化留给那些无状态、可随时重建的任务就好。

5.4 一次完整排查链路:从告警到定位再到修复

踩了前面这些坑之后,我总结出了一套自己的排查流程,正好举个例子模拟一遍。

假设一个工作日下午,openrig 面板弹出告警:node 10.2.3.4 GPU 3 overtemp isolat。我的步骤是这样的:

第一步,先看告警详情。告警里自带节点 IP、GPU 索引、触发时的温度数值和持续时间。由此可以确认是"温度超过 87 度持续 30 秒"的硬告警,不是瞬时尖峰。

第二步,打开节点详情页查看 GPU 历史曲线,确认这张卡在过去一小时内的温度走势。如果曲线是缓慢上升的,大概率是散热问题;如果是突然跳上去的,可能是任务负载突变。

第三步,看这张卡上正在运行的任务。openrig 的任务列表里能直接看到任务 ID、容器 ID 和启动时间。如果任务本身是视频渲染或模型训练,负载高导致的升温就能解释通了。

第四步,检查同节点的其他卡是否正常。如果整机所有卡温度都在涨,问题出在机房空调或机箱风道;如果只有一张卡异常,问题可能出在散热片、风扇或者硅脂上。

我的处理结果是:把该任务迁移到另一台机器上(openrig 的任务迁移命令一条搞定),然后给这张卡挂了一个维护标签,让调度器暂时不往上面分配新任务。后续让现场同事检查了一下风扇,确实有一个风扇转速异常。

整个过程从告警到定位,大概花了十分钟,大部分时间消耗在确认现场和决策。和之前 SSH 登录逐台排查相比,效率提升是数量级的。

6. 把 openrig 接进现有运维体系:告警、插件与二次开发

6.1 通过 Webhook 把告警推到即时通讯工具

openrig 自带一个简易的告警记录页面,但没人会全天盯着页面看。好在它支持 Webhook,可以把告警事件推送到任意支持 Webhook 的即时通讯工具里。

我在 server.yaml 里配置了一个通用的 Webhook 端点,告警消息以 JSON 格式 POST 出去。对于国内团队常用的钉钉/飞书/企业微信机器人,只需要在中间加一层格式转换即可。我这里给一个简单的 Python 转换服务示例(改造自标准 Webhook 接收器):

# webhook_forwarder.py import json import requests from flask import Flask, request app = Flask(__name__) DINGTALK_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=xxx" @app.route("/openrig-webhook", methods=["POST"]) def handle(): event = request.get_json() if not event: return "ok" # 只转发关心的告警级别 if event.get("level") not in ("warning", "critical"): return "ok" msg = f"openrig 告警:{event.get('event_type')}\n节点:{event.get('node_ip')}\n详情:{event.get('message')}" requests.post(DINGTALK_WEBHOOK, json={ "msgtype": "text", "text": {"content": msg} }, timeout=10) return "ok" if __name__ == "__main__": app.run(host="0.0.0.0", port=8090)

这样一旦有节点离线、GPU 高温、任务失败等事件,负责的同事立刻能在手机上收到消息。我自己的经验是,告警消息里务必带上节点 IP 和事件类型,不要只发一句话,否则收到消息的人还得去翻面板。

6.2 自定义指标采集:风扇、电源和第三方传感器

openrig agent 内置的采集器覆盖了 NVML 能拿到的指标,但有些场景下还需要采集额外的数据。比如老机器的电源管理模块没有标准 NVML 接口,只能通过 IPMI 工具读取;或者机房里有温湿度传感器,也想集中到一个面板里看。

agent 有一个插件机制,可以放置自定义采集脚本。把脚本放到/etc/openrig/plugins/目录,脚本往 stdout 输出 JSON 格式的指标数据,agent 就会自动收集并上报。

一个简单的温度采集插件示例:

#!/bin/bash # /etc/openrig/plugins/ipmi_temp.sh temp=$(ipmitool sensor get "PCH_TEMP" | awk '/^PCH_TEMP/ {print $NF}') echo "{\"metric\": \"ipmi_pch_temp\", \"value\": $temp, \"unit\": \"celsius\"}"

插件输出格式约定的是:同一行 JSON,包含metric、value、unit三个字段。agent 每 30 秒会扫描一次插件目录并执行可执行文件。这个接口让我把机房里几台老设备的 IPMI 数据也统一纳管了。

6.3 预留 API 和任务提交的自动化玩法

openrig 提供了 REST API,基本上面板上能做的操作,都能通过 API 完成。我最初看中这一点,是因为团队内部有一些自动化流程需要把任务提交接进 CI/CD 管道里。

举个例子,我们有一个模型评测流程,每次代码合并之后自动跑一组评测任务。以前的实现是在 CI 里直接 SSH 到 GPU 机器上跑命令,机器多了之后管理起来很麻烦。改用 openrig API 之后,CI 只需要向 server 发一个任务提交请求:

curl -X POST https://openrig.internal/api/v1/tasks \ -H "Authorization: Bearer <token>" \ -d '{ "image": "eval-image:v1", "gpu_count": 1, "node": "10.2.3.4", "cmd": "python run_eval.py --model latest" }'

返回的任务 ID 会写入 CI 的后续步骤,等任务完成后,CI 通过 API 拉取日志和退出码,然后决定是否合并代码。这套流程跑了一个季度,稳定得很。

如果你有类似需求,建议先去看一下 API 文档里关于任务状态码的定义。openrig 的任务状态不是简单的success/failure,还包含created、scheduled、running、restarting、isolated等中间态。CI 在判断结果的时候,最好把restarting和isolated也考虑进去,否则会因为轮询到中间态就提前判定任务失败。

最后分享一个我自己的使用习惯。openrig 在这些场景里给我节省了大量重复劳动,但它终究不是万能的。我会在每周的维护窗口里,轮流用openrig node inspect导出一份所有节点的硬件健康报告,哪怕是离线状态的节点也要看看历史数据。监控工具再智能,也只是把你从"人肉巡检"里解放出来,最终的判断和决策还是要人来拍板。把工具当成一个靠谱的副驾驶,而不是完全自动驾驶,这是我和这个项目相处大半年下来最真实的体会。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进