Homelab 断电保护全攻略:两台 APC UPS + NUT + PVE 优雅关机脚本
Homelab 断电保护全攻略:两台 APC UPS + NUT + PVE 优雅关机脚本
dong4j前言
“咔哒”一声,屋里一片黑。
家里一楼二楼总电闸在夏天经常跳,一开始只有一台 NAS 的时候无所谓,硬关机就硬关机,顶多 DSM 开机之后跑一次文件系统检查。但自从 Homelab 一点点堆起来、PVE 集群 + Ceph 上线之后,每次停电我都要紧张一阵子 —— 不是怕硬件坏,是怕突然断电的 Ceph OSD 可能不一致,或者某台 LXC 里的数据库日志写坏了。
修好电闸只是小事,事后收拾集群这些”副作用”才是真正烦。
所以我给家里上了两台 APC UPS,再花了个周末搭了一套 NUT + 自定义关机脚本,让家里的每台机器都能在停电时优雅下班。这篇把整个方案从硬件选择到脚本逐行解释写清楚,谁来抄都能用。
硬件:两台二手 APC
UPS #1:APC BK650M2-CH(跟 DS923+)
这台是最早的那台,很早以前给 DS923+ 配的。
- 容量:390W / 650VA
- 价格:闲鱼 200 出头
- 电池:自带,7Ah 铅酸,免维护几年基本没衰减
插上 DS923+ + 一个千兆交换机之后,650VA 的余量刚刚好够跑 NAS 撑 10~15 分钟,足够 DSM 做一次完整的关机。跟着 NAS 一起已经扛住了好多波意外断电,一次没掉链子。
UPS #2:APC Back-UPS RS 1500G(跟 DS218+)
最近新增了 4 台 PVE 小主机,再加上原来的 DS218+ 和两台 Mac mini,功率加起来 500W 出头,BK650 那台完全带不动,得再上一台大的。
还是闲鱼捡垃圾:
- 机器本体:APC Back-UPS RS 1500G,闲鱼 300 左右;
- 电池:不带,需要另购(两块 12V 9Ah 铅酸串联),100 块左右;
- 一套下来:接近 500 块搞定。
1500G 的标称是 865W / 1500VA,带我这一堆机器满载估计能撑 810 分钟,空闲状态 20 分钟以上 —— 再结合后面的自动关机脚本,基本上”从断电到全部关机完毕”只需要 35 分钟,时间绰绰有余。
整体方案:群晖当 NUT 广播源
最初我想的是给每台机器都接一根 USB 到 UPS,但 APC 这俩 UPS 一台只有一个 USB 口,显然不够。折腾了一会儿发现,群晖 DSM 自带了 Network UPS Tools (NUT) server,是一个现成的 UPS 广播源:
1 | ┌─────────────────────┐ ┌─────────────────────┐ |
好处是:
- USB 口问题解决:只要 UPS 插到一台 NAS 上,它就能把 UPS 状态通过网络广播给整个局域网;
- 群晖 UI 就能查电量 / 续航:不用 SSH 也能看;
- 客户端只要是 Linux,装个
nut-client就完事,配置极简。
DSM 里开启 NUT server 的路径:控制面板 → 硬件和电源 → UPS,打勾”启用 UPS 支持”,选”启用网络 UPS 服务器”,就能看到默认用户名 monuser / 默认密码 secret(强烈建议改一下)。
NUT 客户端配置(PVE / 普通 Linux)
以下配置适用于所有非群晖的机器,我的 4 台 PVE 小主机都是这个配置。
安装
1 | sudo apt install nut |
配置为 netclient 模式
NUT 有三种模式:none / standalone / netclient。我们这里只作为客户端听服务端广播,选 netclient:
1 | sudo vim /etc/nut/nut.conf |
把 MODE=none 改成:
1 | MODE=netclient |
配置监听哪台 UPS 服务器
1 | sudo vim /etc/nut/upsmon.conf |
在末尾加上:
1 | # MONITOR <system> <powervalue> <username> <password> ("master"|"slave") |
💡 我把脚本放在 cephfs 上,所有 PVE 节点共用一份,修改一处全集群生效。
启动 & 验证
1 | systemctl restart nut-client |
正常输出大概长这样:
1 | battery.charge: 100 |
重点看这三个:
battery.charge—— 剩余电量百分比;battery.runtime—— 预估能撑多少秒;ups.status——OL表示市电正常 (On Line),OB表示已切到电池 (On Battery),LB表示电量低 (Low Battery)。
PVE 优雅关机脚本(核心)
重头戏来了。默认的 NUT 关机行为就是直接 shutdown -h now,对于普通机器没问题,但对 PVE 节点来说等于硬关整台虚拟化宿主机,上面跑的 VM 和 LXC 全都相当于硬断电,对 Ceph 和某些数据库非常不友好。
我写了一个增强版的 SHUTDOWNCMD,流程是:
- 采集信息:UPS 电量、运行中的 VM / LXC 列表、系统负载、网络状态;
- 发邮件通知:让我知道”是真的停电了”还是”误触发”,顺便记录当时的集群状态;
- 优雅关 VM 和 LXC:用
qm shutdown/pct shutdown,给每个实例 20 秒超时; - 强杀残留:超时之后还没关掉的,用
qm stop/pct stop强杀; - 关主机:最后
shutdown -h now。
完整脚本:
1 |
|
脚本里几个我踩过坑的细节
1. 一定要在脚本里先探测网络
我的 PVE 节点监控的是群晖上的 UPS。如果只是”NUT client 这台到群晖的网络断了”,也会触发 SHUTDOWNCMD —— 你会发现明明没停电整个集群却自动关机了,我第一次踩到这个坑的时候一脸懵。
所以脚本里 ping 一次主路由(192.168.31.1 或者你自己定义的 BACKUP_IP),如果能 ping 通、UPS 状态又是 OB/LB,才是真正的停电。
2. qm list / pct list 的列位置会变
原来我草稿里用的是 awk 'NR>1 {print $1 ":" $2}',实际上 qm list 第二列是 NAME,第三列才是 STATUS。更安全的写法是过滤 running 状态:
1 | qm list | awk 'NR>1 && $3=="running" {print $1 ":" $2}' |
不然会把已经是 stopped 状态的实例也当成”运行中”,去 qm shutdown 个已经停了的 VM 一堆报错。
3. SHUTDOWNCMD 里不能放太慢的操作
整个 SHUTDOWNCMD 必须在 UPS 电量耗尽之前跑完,所以脚本里所有 “网络请求”(邮件、ping)都要带超时。mail 这个命令在 MTA 连不上 SMTP 的时候会挂很久,建议配一个本地的 SMTP relay(比如 msmtp),或者干脆把通知发到一个已经缓存到本地的 webhook 上(Bark / Gotify)。
Web UI 监控(可选但很香)
NUT 本身自带一个 CGI 网页:
1 | systemctl restart apache2 |
访问:
1 | http://<nut-server-ip>:82/cgi-bin/nut/upsstats.cgi |
能看到实时电量、负载、波形图,长得很”2005 年”,但胜在轻量:
| 方案 | 是否好看 | 资源占用 | 备注 |
|---|---|---|---|
upsstats.cgi | ⭐️ | 低 | NUT 自带,开箱即用 |
| Netdata + apcupsd | ⭐️⭐️⭐️ | 中 | 需要装 apcupsd,Dashboard 很漂亮 |
| Grafana + NUT exporter | ⭐️⭐️⭐️⭐️ | 高 | 配合其他指标一起看,我最终用的方案 |
我现在用的是最后一种 —— 直接走 PVE 集群里已经部署好的 Prometheus + Grafana,加一个 nut_exporter,所有 UPS 的电量、负载、温度一张图里全部可见。停电了手机 Grafana App 弹通知,比任何别的方案都直接。
跑了一年的真实体验
方案上线之后大概扛过 4 次真实停电 + 3 次跳闸,总结几点:
- 真的能帮你抢救到心态平稳。一次凌晨两点停电,我在床上看着 Grafana 上的电量曲线一路下滑,到 40% 的时候集群陆续开始自动关机,到我起床排查电路的时候所有机器已经干净地躺在那儿等我,一个 OSD 异常都没有。
- 二手电池比机器本体更容易坏。APC 这俩机器本体质量相当好,但 RS 1500G 那块铅酸电池一年下来续航打了 6 折。做好心理准备两三年换一次电池,或者直接上 LiFePO4(寿命长得多,但要改装)。
- 关机阈值设在电量 40% 就够了,别等到 20%。因为
SHUTDOWNCMD跑完到机器完全断电还要 1~2 分钟的缓冲,留得多一点安全。群晖里的阈值可以在”UPS 支持”里调。 - 别忘了 mac mini。我 2 台 Mac mini 是直接接 UPS #2 的插座的,但 macOS 没装 NUT client。早期几次停电我以为 UPS 撑得住,结果低电量保护启动后 Mac mini 还是会突然断电 —— 后来给它们也装了
nut-client(通过 Homebrew),走和 PVE 一样的那套upsmon.conf,才真正做到”全屋机器一起体面下班”。
最后聊两句
UPS 这个东西很像家里的灭火器:
- 平时花几百块买回来放那儿,100% 会觉得”这钱花得真亏”;
- 等真用上那一次,又 100% 会觉得”这钱花得真值”。
尤其是家里装了 Homelab、NAS、跑了一堆有状态服务的朋友,强烈建议先上 UPS,再考虑更多设备。不然有一天你会发现,你花了三万块搭的 Ceph 集群,被一次价值 3 毛钱的跳闸干趴下半天。
这套方案跑了一年多,目前挺稳的。如果你也在配 UPS,直接抄这一篇就行,脚本拿去改改邮箱和 IP 就能用。
参考:
- Network UPS Tools 官方文档
- Monitoring a UPS with NUT on the Raspberry Pi - Pi My Life Up
- GitHub - AdeMiller/pi-nut
- 使用樹莓派監測控管 UPS 設備 - KodeLab
- 使用 nut-client 连接到群晖 NAS 的 UPS 服务器 - DC 的无名小站
- 配置 NUT 服务的 Web UI - Dako 的博客
- 使用 Netdata 和 apcupsd 监控非 SNMP UPS - 天雨的博客
- NUT 软件——不难破解 - StorageReview
- nut_exporter - Prometheus exporter

















