项目/监控运维
监控运维 · 运行中

双通道告警系统(邮箱 + ntfy)

磁盘/内存/温度/服务异常自动发邮件 + ntfy 推送,两个渠道互为备份。

告警ntfySMTPGatus

这是什么

这是我 Homelab 里的一套「双通道告警系统」:磁盘、内存、CPU 温度、systemd 服务、容器健康、挂载盘、节点在线、网站/隧道可用性一出问题,手机 ntfy 立刻响,同时发一封邮件到自建邮箱 xiejun@xiejun-server.top。两个通道互为备份,谁挂了都还有另一个能喊人。

为什么做

之前只靠盯监控台页面,问题往往是我主动打开才看到。有一次照片盘 SMART 报警、一次容器不健康,都是事后才发现的。所以我想要的是「不主动看也能知道」——机器自己找上门。选 ntfy 是因为它手机端免费、无广告、订阅一个 topic 就完事;选邮件是因为它足够独立,不依赖同一台机器的推送链路。

怎么分工

告警分两层,互相独立:

  • Gatus(跑在 .223)负责外部视角:16 项探测,网站/服务 URL、公网可用性、两条樱花隧道、照片盘 SMART、Windows 主机 SSH、三台机器在线。
  • 监控台(.164 上的 notify.py)负责内部视角:节点离线、磁盘 ≥88%/94%、内存 ≥92%/96%、CPU 温度 ≥85/92°C、服务启动失败、容器不健康、挂载盘失效。

两层都做了防抖和冷却:连续失败 2 次才推,恢复后再推一条「已恢复」,同一问题 6 小时内只重复提醒一次。

现在的状态

运行中。Gatus→ntfy、监控台→ntfy 两条链路都做过真实触发的验证,状态机自检通过;邮件侧走自建 docker-mailserver + Brevo 免费中继,mail-tester 实测 DMARC/DKIM/SPF 全 pass。

已知边界

ntfy 用的是公共 ntfy.sh,topic 就是密码;.164 整体挂掉时,靠 .223 上的 Gatus 兜底报警。

功能

  • Gatus 侧 16 项探测覆盖网站、公网可用性、隧道、SMART、SSH 与机器在线
  • 监控台侧覆盖节点离线、磁盘/内存/温度阈值、服务与容器健康、挂载盘失效
  • 连续失败 2 次才推送,恢复后补一条「已恢复」,避免抖动刷屏
  • 同一问题 6 小时冷却,只重复提醒一次
  • ntfy 手机推送 + 自建邮箱 SMTP 发信,双通道互为备份
  • 通道可切换:notify.json 里换成企业微信或钉钉机器人,30 秒内生效

技术栈

PythonntfyGatusDockerdocker-mailserverBrevo SMTP 中继systemd

使用 / 部署教程

一、手机端订阅(1 分钟)

  1. 装 ntfy App(安卓应用商店 / F-Droid,iOS App Store)。
  2. 打开 App → 右上角 + → Subscribe to topic → 填入 topic 串(22 字节随机串,等于密码,别外传)。
  3. 完成后不用再管。刚订阅时可能看到几条 12 小时内的历史测试消息。

二、部署监控台侧 notify.py

  1. 把 notify.py(纯标准库实现,检测 + 推送)放到 /opt/dashboard/,同目录建 notify.json,权限设 600:
   chmod 600 /opt/dashboard/notify.json
  1. notify.json 里写 topic、阈值和 channels;默认 {"type": "ntfy"},想换企业微信就把 channels 改成 webhook 形式,30 秒内自动生效,不用重启。
  2. 在 server.py 里加两行接入:import notify 和 notify.start(stats)。改前先备份 server.py。
  3. 自检状态机(含「恢复」路径断言):
   python3 /opt/dashboard/notify.py --selftest
  1. 验证:临时把某个阈值调到当前真实值以下触发一轮,确认收到告警与「已恢复」两条,再改回正式阈值,检查 notify-state.json 为 {"problems": {}}。

三、配置 Gatus 侧

  1. 编辑 /opt/gatus/config/config.yaml,先备份。
  2. 新增 alerting.ntfy 段,填 topic 与推送地址。
  3. 给需要告警的每个 endpoint 加 alerts: [- type: ntfy](当前 16 项)。
  4. 重启 Gatus,然后临时加一个必定失败的端点,确认手机收到告警,再删掉。

四、接邮件通道

  1. 在 VPS 上用 docker-mailserver 建发信账号(docker exec mailserver setup email add <addr> <password>)。
  2. 出站 25 被封的环境必须走 ESP 中继:在 mailserver.env 配 RELAY_HOST / RELAY_PORT=587 / RELAY_USER / RELAY_PASSWORD,改完 docker compose restart mailserver。
  3. 在 ESP 侧完成域名认证(根域必须与 From 地址的域一致),否则签名对不上。
  4. 用 mail-tester 发一封测试信,确认 spf=pass、dkim=pass、dmarc=pass。

五、日常维护

阈值按机器实际负载调整;冷却时间用 cooldown_sec 控制;每加一台机器或一个服务,记得在 Gatus 和监控台两边都补上对应项。

注意

  • ntfy topic 相当于密码,公开博客、截图和仓库里都不要出现完整串;notify.json 保持 600 权限。资料原文里的邮箱密码、Brevo 账号密码等明文凭据不应随文档一起发布。
  • 用的是公共 ntfy.sh:消息在对方服务器保留 12 小时,且推送可用性依赖第三方;想彻底自建要在 DNS 加一条 ntfy.xiejun-server.top 再配。
  • 邮件通道依赖 Brevo 免费中继(300 封/天),且 .top 后缀在 SpamAssassin 有固定扣分,正式依赖前先给 Gmail/QQ 各发一封确认进收件箱。