局域网监控全家桶搭建笔记:NetAlertX + Uptime Kuma + Beszel + Grafana + ntfy
🧭 局域网监控全家桶搭建笔记:NetAlertX + Uptime Kuma + Beszel + Grafana + ntfy
💡 设备一多,「谁挂了」就变成了玄学:NAS 上某个容器停了、软路由 DHCP 抽风、ESXi 里一台虚机失联——每次都是等发现不了网的时候才知道出事。这篇文章记录我把整个局域网的「设备、服务、指标、告警」四层监控补齐的过程。
0. 为什么要做监控
家里现在跑着的东西:25 台 LAN 设备(iStoreOS、fnOS NAS、Debian 宿主、ESXi、米家网关、各种盒子),56 条服务端口映射,7 台需要看系统指标的主机。
之前的痛点很典型:
- 设备无账本:新设备接入没人知道,IP 被占了才想起来查
- 服务黑盒:42 个 Web 服务全靠「打不开才发现」
- 指标缺失:CPU/磁盘/带宽没有历史曲线,排查全凭感觉
- 告警分散:有的发邮件、有的没告警,出事了都不知道
目标:设备有台账、服务有探测、主机有指标、告警走一个入口。
1. 总体架构
先看全貌:

分工原则是一个工具只干一件事:
| 组件 | 职责 | 部署位置 |
|---|---|---|
| NetAlertX | 设备发现与台账(双网段) | .55 · :20211 |
| Uptime Kuma | 服务可用性探测(42 项) | .51 · :3001 |
| Beszel | 主机系统指标(7 台) | .55 · :3017 |
| 8789 控制台 | 端口探测 + 设备 JSON 出口 | .51 · :8789 |
| Grafana 12 | 设备清单/类型分布展示面板 | .55 · :3023 |
| ntfy | 统一告警推送 | .51 |
2. 探测层四件套
NetAlertX:设备台账
双网段扫描(31.x + 0.x)都能发现,新设备接入、设备离线都会推 ntfy。后来把 25 台设备全部补了可读名称(.50 iStoreOS、.51 fnOS、.55 Debian、.99 ESXi……),并把确认过的设备标记为非新设备——这步是靠它的 API 批量写入的,Token 从运行时环境注入,不落盘。
Uptime Kuma:服务探测
42 个监控项覆盖所有 Web 服务和关键端口。它还有一个彩蛋链路:Kuma → Home Assistant → 台灯,服务挂了台灯闪红,躺在沙发上也能知道出事了(后面告警链详说)。
Beszel:主机指标
7 台物理/虚拟主机接入,CPU、内存、磁盘、磁盘 I/O、上下行吞吐都有,通知同样接 ntfy。选它而不是 Node Exporter 全家桶的原因很简单:单容器部署,agent 够轻。
8789:自研总览控制台
这是整个体系里最关键的一块拼图——自己写的总览控制台,27 项探测全部正常,除了页面展示,它还提供带 CORS 的设备 JSON:http://192.168.31.51:8789/net/devices。这个接口后来成了 Grafana 面板和 3D 拓扑的数据源。
3. Grafana Infinity 设备面板
Grafana 用原生 systemd 部署(12.2.1,开机自启),通过 Infinity 插件直接读 8789 的 JSON,用 JSONata 做解析:
1 | root_selector = $.devices |
面板做了四块:统计卡(总设备 25 / 待确认 0 / 智能家居 14 / 服务器 4)、可排序设备清单、设备类型分布柱状图、四个监控入口的可点击直达。



Infinity 插件的坑
这个坑值得单独记:查询对象的数组字段写成 null 会直接报错。
Infinity 3.11.1 的前端会对 url_options.headers、params、body_form 做 .map(),写成 null 就是 Cannot read properties of null (reading 'map')。正确做法是省略这些字段,或写成空数组,不要写 null。
另外两个小处理:表格查询把空类型/空厂商补成「未分类/未知」;无法识别名称的新设备显示为「未知设备 · IP」,不再出现空白或 (name not found)。
4. 告警链:ntfy → Home Assistant → 台灯
所有告警走 ntfy 一个入口:
- NetAlertX:新设备接入、设备离线
- Uptime Kuma:服务探测失败
- Beszel:主机指标越阈
再加一条物理链路:Kuma → Home Assistant → 台灯故障灯。深夜服务挂掉,手机推送可能被静音,但台灯闪红光不会。
5. 全平台汉化
监控面板是全家要看的,英文界面家庭接受度低,顺手全汉化了:
| 组件 | 方式 |
|---|---|
| Grafana | custom.ini 加 [users] default_language = zh-Hans |
| NetAlertX | UI 语言 zh_cn + 设备名/类型改中文 |
| Uptime Kuma | 浏览器 zh-CN 自动生效,无需改服务端 |
| Beszel | 浏览器 zh-CN 自动生效,无需改服务端 |
| 8789 | 原生中文页面 |




已知残留:Grafana 上游中文包有个别内置字符串(如 Last 15 minutes)未翻译,不影响业务内容。
6. 踩坑记录
- Infinity
null.map报错:数组字段写null必挂,省略或[](见上文) - Grafana 别用 Docker 和 systemd 混跑:早期 compose 部署升级麻烦,换成原生
grafana.service后,旧 compose 文件改名停用、同名容器删除,避免再次抢占 3023 端口 - Dashboard 改前必备份:JSON 存本地 + 服务器双份(
grafana-dashboard-before-2026-11-01.json),出问题秒回滚 - 批量改设备名前先备数据库:NetAlertX 的
app.db改名前做了.bak备份,Token 不落盘
7. 验收标准
- 6 个动态面板查询全部返回 200,统计卡无空数据
- 设备表返回 25 行,类型汇总 5 类
- 页面渲染无错误,浏览器控制台无 Infinity 异常
- 27/27 端口探测正常,4 个监控入口可直达
本文首发于 ShallowRecall,转载请注明出处。





