前两年我手里的机器从一台变成三台的时候,最大的烦恼不是性能,而是管理。主力工作站要跑渲染和深度学习实验,一台NUC长年挂着下载和轻量容器,老笔记本退役之后不甘心吃灰,被我拿来当备份机和临时测试机。听起来只有三台,但每次想干点正事都得先翻笔记查IP、开SSH窗口、手动敲命令看温度,机器一旦断电还得跑到机器旁边按电源键。折腾了一个月后我决定不再忍:不买商业软件,不引第三方云平台,就用开源组件加少量代码,自己写一套能远程开机、能看温度、能串流操作、能定时执行任务的设备管理平台。这个项目我做着做着就成了一个小系统,我管它叫openrig。
如果你跟我一样,手里有两台以上需要长期开机的Linux机器、又不想为几台设备去维护Zabbix或OpenStack那种重量级方案,那这篇文章应该对你有用。我会把openrig的设计思路、部署过程、日常使用中真正用得上的功能,以及踩过的坑一并讲清楚,所有配置和代码片段都是可以直接抄走改用的。
1. 为什么会有openrig:多机管理的真实痛点
1.1 机器一多,“能开机”和“能好好用”是两回事
单机时代,一切都很简单。机器就在手边,有问题重启就行,温度高了打开机箱清灰,硬盘满了顺手删几个旧文件。但当你把几台机器分散到不同房间、甚至放在客厅角落和公司工位上,问题就全变了:你要的不是“能开机”,而是“不用站起来也能让机器按预期工作”。
我遇到的第一个尴尬场景是冬天给主力机换了一张大功率显卡之后,晚上跑实验,十一点左右机器突然没响应。我以为是显卡负载过高导致系统卡死,后来发现是房间温度叠加机箱积灰,显卡到了85度触发自我保护直接熄火了。那天我站在机器面前,看着风扇狂转,才意识到一台机器如果没人在旁边,很多小事都会被放大成事故。
openrig最开始不是冲着“做一个系统”去的,而是我一个个解决这些小事:怎么远程知道它在过热?怎么远程开它?怎么在桌面环境里直接点一下就进SSH?等我把这些单点方案都攒齐了,自然汇聚成了一个有统一入口的管理系统。
1.2 现有监控方案为什么不合用
市面上其实不缺设备管理工具,我在动手之前也试过几样,各有各的问题。
Prometheus加Grafana这套组合拳,功能确实强,数据采集、告警、图表全都有,但为了管三台机器要维护一套Prometheus、node_exporter和Grafana,光改告警规则就得学半天,对个人和小团队来说太重了。Zabbix则自带一股企业级气息,部署和配置学习曲线陡,agent的安装包跟我要管的小机器相比显得过分隆重。智能插座能解决断电重启,但它只看电源有没有通,看不到温度负载、磁盘寿命和任务状态。还有一类商业化远程控制软件,装上去倒是方便,不过对于我这种对数据出口比较敏感、又想尽量自己掌控的人,始终不太舒服。
所以我给自己定下了一条原则:openrig只做轻量编排和统一入口,具体的能力能复用现成协议就绝不重复造轮子。远程开机用WOL,远程会话用SSH和VNC/RDP,传感器读取用lm-sensors和smartctl,任务调度用内置的轻量cron,我负责把这些东西用一个统一界面穿起来。
1.3 openrig到底要干什么事
总结下来,openrig要解决的核心问题就四个:
- 开机可达性:设备关机状态下能通过WOL魔术包远程唤醒,断电来电后能自动启动。
- 状态可观测:CPU温度、负载、内存、磁盘、网络流量、系统服务状态,一个页面就能看全。
- 操作不挑地方:能一键进SSH终端,也能打开远程桌面会话,不用记IP和端口。
- 维护能自动化:定时开关机、定时跑清理脚本、异常状态触发通知。
这四个需求是逐级递进的关系。你得先能开机,才能谈观测;能观测了,才会放心让它自动跑;自动跑起来之后,你需要有足够的工具及时介入。openrig整个架构都是围绕这条链路展开的。
2. openrig的整体架构与服务拆分
2.1 hub-agent-web三层结构
openrig被拆成了三个独立但又互相配合的部分:agent跑在被管理的每一台机器上,负责采集数据、执行任务、维持本机存在感;hub跑在一台常开的管理机上,负责汇总所有agent上报的数据、存储状态、调度任务;web界面是hub对外提供的前端页面,也是我平时用得最多的入口。
这个结构其实借鉴了传统agent/manager模型,但我做了一点调整:agent默认是“拉模式”配合“推模式”混合工作。正常情况下agent定期把状态推到hub,但如果hub暂时不可用,agent会在本地缓存最近几次数据,等hub恢复后再补交。这样就算主控重启,被管机器的运行数据也不会彻底丢空缺页。
我为什么不用全推模式?因为在真实网络环境里,hub和agent之间的链路不是永远可靠的。全推模式一旦链路抖动,很容易在hub侧形成数据空洞。拉模式在agent数量少的时候够用,但一旦任务调度密集,频繁轮询会放大延迟。折中一下,就变成了现在这种混合模式:心跳和关键事件走推送,批量拉取历史数据走主动查询。
2.2 技术选型:为什么是FastAPI加Python全家桶
openrig的后端我选了Python和FastAPI,而不是Go或Node.js。主要理由是Python在“系统和硬件打交道”这件事上生态实在太丰富了:psutil可以直接拿CPU、内存、网络和磁盘数据,PyYAML处理配置文件,lm-sensors虽然要调命令行但封装起来不费劲,smartctl这类磁盘工具本来就有Python wrapper。FastAPI则解决接口层的问题,自带OpenAPI文档,开发的时候调试接口特别顺手,不用额外写文档工具。
可能有人会说Go更适合做这种常驻服务,内存占用小、部署方便。这个我不反对,但我一个人的项目最怕的是开发效率不够。用Python,一个下午就能把agent的核心采集逻辑写完,而用Go光处理各种系统的API差异就够折腾一阵。对我这个使用规模来说,几百MB内存的开销完全不是瓶颈,省下来的时间是实打实的。
前端我用了Vue3加ECharts,其实如果只做内网自用,P上静态HTML加Fetch轮询也能撑住。考虑到后续要扩展多人协作和告警看板,ECharts的图表能力省了我自己画状态图表的功夫。
2.3 数据库与关键数据模型
存储层我第一步用的是SQLite,因为单文件、零运维,对个人用户最友好。agent数量超过二三十台或者要保留一年以上的历史趋势的时候,再迁到InfluxDB也不迟,openrig的采集接口在设计时就预留了时序数据后端的抽象层。
核心表设计上,我维护了这么几张表:
| 表名 | 主要字段 | 用途 |
|---|---|---|
| devices | agent_id, hostname, lan_ip, mac, group_id, online | 记录被管设备的基础信息 |
| telemetry | device_id, cpu_temp, cpu_load, mem_used, disk_used, net_rx, net_tx, time | 存每一次心跳采集的状态快照 |
| tasks | device_id, type, cron_expr, command, enabled | 存定时任务的配置 |
| alerts | device_id, rule_type, level, message, status | 存告警事件 |
| users | username, pubkey, role | 存用户和公钥信息 |
design的时候有一个小细节建议你注意:设备信息表里尽量用agent_id作为逻辑主键,不要用hostname和IP。因为主机名会改、IP会变,而agent_id是安装agent时生成的唯一标识,重装系统时也要保留持久化。我在早期版本用hostname做了关联,后来发生过一次重命名主机导致历史数据全部断裂的情况,改造成agent_id后这个问题消失了。
3. 部署openrig的完整步骤:以三台物理机为例
3.1 环境准备与目录规划
我先说结论:openrig可以完全跑在普通Ubuntu 22.04环境里,不需要Kubernetes,不需要微服务框架,一台管理机加若干台被管机就行。最小可行的初始配置是三台机器:一台做hub兼web,另外两台只装agent。
目录规划上我建议统一放到/opt/openrig下面,这样备份和迁移都很方便:
sudo mkdir -p /opt/openrig/{hub,agents,web,data} cd /opt/openrig git clone https://git.example.com/openrig/hub.git # 按你自己的仓库地址来 git clone https://git.example.com/openrig/agent.git git clone https://git.example.com/openrig/web.github和agent的依赖最好用虚拟环境隔离,避免跟系统Python打架。这里我给agent那台机器演示一下:
python3 -m venv /opt/openrig/agents/venv source /opt/openrig/agents/venv/bin/activate pip install fastapi uvicorn psutil pyyaml netifaces python-wakeonlan pydanticUvicorn是FastAPI的ASGI服务器,这里直接做agent的HTTP服务承载。psutil用来读系统状态,python-wakeonlan是发WOL魔术包的库,注意这个只需要在有远程开机需求的那一侧装就行,agent实际上不依赖它。
3.2 部署hub:docker-compose最省心
hub我是用Docker Compose跑起来的,因为管理机不像被管机那样需要直接读硬件传感器,它的职责就是汇总、存储和提供界面,容器化之后升级和回滚都方便。
下面是一份可以直接用的docker-compose.yml:
version: "3.8" services: openrig-hub: image: openrig/hub:latest container_name: openrig-hub restart: unless-stopped ports: - "8080:8080" volumes: - /opt/openrig/data:/data - /opt/openrig/hub/config.yml:/app/config.yml:ro environment: - OPENRIG_JWT_SECRET=your_long_random_secret - OPENRIG_BIND_ADDR=0.0.0.0:8080启动前先写一份最小配置config.yml:
database: path: /data/openrig.db hub: name: homelab token: your_join_token_here auto_accept: false metrics: retention_days: 30这里重点说一下auto_accept参数。我把它设成false,意思是agent来注册时不会自动通过,需要在web界面或者API里手动确认。这个设计是为了防止局域网里有人扫到端口就能随意注册设备进来。对个人用户来说多一步确认不费事,但安全收益立竿见影。
然后启动:
cd /opt/openrig/hub docker compose up -d curl http://127.0.0.1:8080/health看到{"status":"ok"}就说明hub起来了。JWT_SECRET一定要自己换成一个足够长的随机字符串,不要用仓库里的默认值。
3.3 部署agent:让每台机器接入集群
agent的安装就简单很多了。如果是Ubuntu/Debian系,我直接用一个临时安装脚本:
#!/usr/bin/env bash AGENT_VERSION="0.4.2" sudo mkdir -p /opt/openrig-agent cd /opt/openrig-agent sudo python3 -m venv venv sudo venv/bin/pip install --upgrade pip sudo venv/bin/pip install openrig-agent==$AGENT_VERSION sudo cp /opt/openrig-agent/venv/lib/python3.10/site-packages/openrig_agent/contrib/openrig-agent.service /etc/systemd/system/ sudo systemctl daemon-reloadagent的配置文件放在/etc/openrig-agent/config.yaml:
agent_id: "default-keepme" hub_url: "http://10.0.0.5:8080" join_token: "your_join_token_here" metrics_interval: 15 sensors: enabled: true cpu_temp_source: "coretemp" disk_smart: trueagent_id字段我建议不要让它自动随机生成,而是手动指定一个稳定的标识,比如livingroom-worker这种,因为重装agent时如果你能保持同一个ID,hub上的历史数据就不会断。
启动agent并注册:
sudo systemctl enable --now openrig-agent sudo journalctl -u openrig-agent -f # 看日志确认是否注册成功正常的话,日志里会出现“registration request sent”或者类似信息。这时去hub的web界面找到待确认设备列表,点一下确认授权,设备就正式上线了。
3.4 web界面初始化与设备分组
web界面如果用的是hub自带的静态页,启动hub之后直接访问http://10.0.0.5:8080/就行。第一次登录会让你创建一个管理员账号,然后把刚才授权的设备挪到一个分组里。
我比较建议按“功耗段”来分组,而不是按房间或用途分组。比如“常开组”“夜间关机组”“备用冷备组”。因为你在做定时任务的时候,按功耗段批量化操作最顺手。按房间分看起来直观,但实际操作下来你很快会发现,你关心的是哪些机器可以关机省电,而不是它们在哪个房间。
4. openrig落地后最实用的四个功能
4.1 WOL远程开机与来电自启配置
远程开机这件事,我几乎是第一时间就要用的。WOL的原理并不复杂:目标机器关机后,网卡如果没有被完全断电,仍然在监听特定格式的魔术包;你从管理机发一个包含目标网卡MAC地址的广播包,网卡收到后就会触发主板开机。
要让WOL生效,需要过三关。第一关是BIOS/UEFI里开启“Power On By PCI-E”或者“Wake on LAN”选项,不同主板叫法不一样,但基本都在电源管理设置区。第二关是操作系统层面要让网卡不关闭WOL功能,Ubuntu下可以用ethtool检查:
sudo ethtool enp3s0 | grep Wake-on Wake-on: d如果显示d(disabled),执行:
sudo ethtool -s enp3s0 wol g但这个设置重启后会丢失,所以要把命令写进systemd服务里。我直接建了一个/etc/systemd/system/wol-enable.service:
[Unit] Description=Enable Wake on LAN [Service] Type=oneshot ExecStart=/usr/sbin/ethtool -s enp3s0 wol g [Install] WantedBy=basic.target然后启用它。第三关是openrig这边,hub通过python-wakeonlan向局域网广播发送魔术包。如果你有多个网段的设备,记得在配置里给每个设备指定广播地址,默认用255.255.255.255在复杂网络下不一定能到达。
断电后来电自启是另一条路,靠的是BIOS里的“Restore on AC Power Loss”或者“AC Power Recovery”选项,把它设为Power On。WOL解决的是“单独远程开机”,来电自启解决的是“停电恢复时所有设备能自动回来”,两者不冲突,我建议都开。实际测试中,个把月没停电的话你可能感觉不到这个配置,但真实经历过一次半夜小区跳闸,第二天早上所有NAS和服务器自己回来了,那感受真的比什么都靠谱。
4.2 一键进SSH和远程桌面会话
openrig的web界面里,我做了两个会话入口。最简单的是内置SSH终端,技术上用xterm.js去连hub的WebSocket,再由hub转发到目标机器的SSH端口。这个功能在Linux机器上足够用了,打开页面直接就是终端,跟本机开着Terminal没有区别。
如果你要管的是带桌面的Linux机器,或者偶尔需要操作图形界面,那就得靠远程桌面。我的方案是让agent提供一份“会话配置”,自动探测本机有没有跑VNC或者RDP服务,如果有,web界面会生成一个一键连接链接,通过hub中转或者直接跳到局域网地址。
有一条经验我要多说一句:如果是无人值守的机器,最好插一个HDMI欺骗器或者启用虚拟显示器内核模块。没有物理显示器接入时,很多显卡驱动会不输出画面,VNC端看到的就是黑屏或低分辨率。一个十几块钱的HDMI欺骗器就能解决,不要觉得麻烦。用NVIDIA显卡的机器,也可以试试在/etc/X11/xorg.conf.d/下加一段虚拟显示配置,不过稳定性不如物理欺骗器。
4.3 传感器数据与温度策略
观测功能是我用得最多的,因为很多硬件故障在温度曲线上能提前看出苗头。agent采集数据时,CPU温度走的是lm-sensors,NVMe硬盘温度走的是smartctl,整机功耗如果有PMU接口就直接读,没有就退一步用CPU包络功率估算。
采集间隔我设的是15秒一次,这个频率对个人场景足够,图表也不会显得毛糙。温度数据上来之后,我在hub里做了两条规则:一是“持续5分钟超过80度”产生告警;二是“一度达到95度”直接触发降载脚本。降载脚本是agent执行端的功能,命令写得比较保守:
#!/usr/bin/env bash # 当温度过高,让CPU稍歇一下 sleep 5 systemctl stop heavy-task.service 2>/dev/null echo "$(date): high temp action triggered" >> /var/log/openrig-agent.log这个脚本是配置在agent的config.yaml里的,方便每台机器差异化设置。你可以在web界面里看到当前温度和告警状态,也能翻历史曲线。有一次我就是靠这个曲线发现某台机器散热器上的风扇转速突然掉了一半,还好提前发现,不然长期高温运行肯定影响寿命。
4.4 任务编排:定时开关机
机器多了之后,最直观的省电策略就是定时开关机。我的NAS和实验室机器都是工作日晚上才用,所以openrig里内置了基于cron表达式的任务调度。比如给一台夜班机设置:
tasks: - name: "workday boot" cron: "30 8 * * 1-5" action: "wol_wake" - name: "shutdown at midnight" cron: "0 0 * * *" action: "ssh_command" params: command: "sudo poweroff"注意:开机的动作是WOL,关机的动作走SSH远程执行poweroff,不是直接断网卡电源。因为优雅关机可以保证文件系统同步,减少硬件损坏风险。如果遇到机器没有网卡监听或者WOL不灵的情况,再考虑智能插座断电,但那是兜底方案,不是常规手段。
定时任务最好在hub侧统一管理,不要散落在各台机器的crontab里。原因很简单:集中管理之后你可以在web界面看到所有“计划内动作”的日志,而且可以随时暂停、调试。散落式的crontab,过两个月你自己都想不起来当初写进去的命令是干嘛的了。
5. 踩过的坑:openrig落地期间的真实故障记录
5.1 网络唤醒跨VLAN失效,局域网里也有死角
我第一次部署WOL时,以为同一个路由器下就万事大吉了。实际用下来发现,主力机放在客厅,hub放在书房,两台设备确实是同一个局域网IP段,但WOL就是唤醒不了。排查的时候我用tcpdump抓包:
sudo tcpdump -i enp3s0 udp port 9然后从hub那边手动发一次WOL包,发现目标机器网卡上根本没有任何UDP包出现。问题出在交换机把不同物理接口划分在不同VLAN,广播包是过不去的。而且我的无线设备里,除了个别高端型号,几乎所有Wi-Fi网卡都不支持WOL。
后来处理方案很简单:把需要远程开机的设备统一接到同一个有线交换机,并且确认这个交换机端口都在同一个VLAN里。最稳定的拓扑是:一台管理机加一台支持WOL的有线交换机,所有常关机但有唤醒需求的机器都插在这台交换机上。无线设备就不要指望WOL了,或者给它们配一个支持来电自启的智能插座做补偿。
5.2 agent重复注册导致hub上出现两个同名设备
这个坑是我重装系统时踩的。之前agent安装包会自动生成一个随机agent_id,重装之后ID换了,但我在hub上忘了删掉旧的设备记录,于是界面上就出现“livingroom-worker”和“livingroom-worker-2”两条记录,MAC地址一模一样,但agent_id不同。
看起来只是显示列表多一行,实际上问题不小:我自己已经分不清哪个是当前活动的设备,而且历史数据跟着旧ID走,新ID没有积累。后来我改成了两个做法:一是配置文件里明确写一个固定的agent_id,重装系统时保留这个配置;二是hub端加了注册检测逻辑,如果新agent的MAC地址已经存在,就提示管理员“是否接管旧设备”,接管时自动把旧设备的历史数据合并到新ID下。
5.3 smartctl权限与NVMe设备ID变化
普通用户跑smartctl经常会碰到权限不够的问题,尤其是直通卡(HBA/RAID卡)和NVMe盘。我第一次在agent里集成了磁盘健康检查后,日志里全是Permission denied。解决办法是给smartctl配置sudo白名单,而不是给agent整体sudo权限。在/etc/sudoers.d/openrig-agent里加一行:
openrig-agent ALL=(root) NOPASSWD: /usr/sbin/smartctl这样agent只会在调用smartctl时提权,其他命令还是普通权限,安全风险小很多。
另外,设备盘符变化这件事也值得留意。Linux下/dev/sda这种命名在重启后可能变化,尤其机器里插了好几张盘时。openrig的磁盘监控脚本一开始按盘符做关联,重启后数据全乱了。后来我改用磁盘的序列号或者NVMe的namespace ID做关联,彻底解决了这个问题。如果你也在自己写类似的监控脚本,强烈建议从一开始就用稳定标识,不要用盘符。
5.4 无人值守串流出现黑屏和低分辨率
这个坑在4.2里提过,但展开说一下排查过程。第一次设置远程桌面时,我远程启动VNC服务,能看到连接成功,但画面是全黑的,切换分辨率也没用。后来我意识到,这台机器根本没插显示器,显卡侧的显示控制器被禁用了。
试过用DRM的虚拟输出,效果不稳定。最后还是老老实实买了一个HDMI欺骗器,插在显卡HDMI口上。系统以为有真实显示器,图形会话正常启动,VNC过去就有内容了。这个方法便宜、无驱动依赖,如果你也在搞家庭服务器和无人值守远程桌面,我建议直接走这条路,别在虚拟显示配置上浪费太多时间。
6. openrig还能怎么玩:从远程电源键到自动维护
6.1 系统自动维护脚本示例
openrig的任务编排不仅限于开关机。agent端可以挂任何命令,所以我把磁盘清理和日志轮转也做成定时任务。比如这台下载机,经常被迅雷缓存和Docker日志塞满,我给它写了一个清理脚本:
#!/usr/bin/env bash # 清理 Dangling images 和旧日志 docker image prune -f >/dev/null 2>&1 journalctl --vacuum-size=200M >/dev/null 2>&1 df -h / | tail -1然后把脚本路径填到task配置里,每周日凌晨跑一次。通过hub的日志页面我能看到每一次执行结果,比全部塞进系统crontab可观测性强太多了。
6.2 告警通知的通用webhook对接
状态监控最终要落到“及时知道”,不然就只适合事后翻历史。openrig支持通用webhook告警,规则可以按设备或分组配置。比如“CPU温度连续5分钟超过80度”和“设备离线超过10分钟”我各配了一个webhook推送。
webhook的实现很简单,配置里给一个URL,事件触发时hub往这个URL发一个POST请求,body里带上设备名、指标值、等级和消息。下面是我常用的消息格式:
{ "event": "telemetry.alert", "device": "livingroom-worker", "level": "warning", "message": "CPU temperature 84C for 5m", "ts": 1716019200 }你可以把它接到任何支持webhook的服务上,比如企业群里建一个机器人、或者自己搭一个通知聚合端。对个人用户来说,哪怕只是往邮箱发一封摘要,也保证了出事时能及时知道。
6.3 我的上手建议与后续方向
如果你打算照着这个思路搭自己的设备管理平台,我的建议是不要一上来就追求全自动。先把“看得见”做好:所有机器的温度、内存、磁盘在一个页面里能看;然后做“够得着”:一键SSH、远程桌面;最后再做“自动跑”:定时任务、告警、自动清理。这个顺序让我少走了很多弯路。
openrig目前的形态已经很贴近我的日常需求了。后续我想再加两个方向:一个是通过通用消息服务做短命令下发,就是收到一条“dashboard off”消息后能远程关掉某个分组;另一个是继续完善多用户权限,方便偶尔让朋友或同事访问指定设备而不是看到整个集群。折腾这个项目给我最大的体会是,管理主机这种事,最有价值的部分不是堆功能,而是让你对自己的系统状态有掌控感,你不一定天天盯着它,但它一旦出问题,你能很快知道发生了什么、该从哪里查起。就冲这种掌控感,当初那个凌晨蹲在机箱旁边手动重启的夜晚,也算值了。