Linux 上的 UPS 工具 NUT
NUT 是 C/S 架构的软件。它的优点在于省成本,不用买很贵的带网络管理卡的 UPS,只需要一个 master 节点能和 UPS 通信就够了。断电时候 master 节点可以通过网络通知 slave 节点关机。但缺点是,来电后,所有节点启动时,要先等 master 节点启动 (server 所在机器),然后再在 master 节点上使用 wake-on-lan 唤醒其他 slave 节点。
NUT 的主要 systemd 服务:
- nut-server.service 负责对外提供服务
- nut-client.service 负责监视 UPS 状态、执行指定操作
NUT 的配置文件:
- nut.conf (影响 nut-server) 被用于配置 NUT 的工作模式
- ups.conf (影响 nut-server) 被用于配置 NUT 和 UPS 的连接。也可用于重载 NUT 某些默认的配置,就是
upsc [myups]
输出的那些值。比如,battery.charge.low
表示触发自动关机时的 UPS 电量,battery.runtime.low
表示出发自动关机时的设备可运行时间 - upsd.conf (影响 nut-server) 当一个 UPS 被用来保护多台机器时,这个文件被用于配置 slave 访问 master 的方式
- upsd.users (影响 nut-server) 被用于配置访问 nut-server 的角色
- upsmon.conf (影响 nut-client) 被用于配置 NUT 要监视哪个 UPS,在什么时候执行关机操作
- upssched.conf 中可以配置用户自定义的操作,比如脚本之类,并配置触发条件
man [ nut.conf | ups.conf | upsd.conf | upsd.users | upsmon.conf | upssched.conf ]
可查看详细信息。
UPS 为 APC BK650M2-CH,它使用 USB 与主机通信。以下操作在 Debian 12 测试通过,nut-server 和 nut-client 的版本号均为 v2.8.0。
配置目标:
- 两台主机 A 和 B 都由 UPS 供电,其中:
- A 既是 server 也是 client,通过 USB 直接连接 UPS 并接入网络。(NUT server 和 client 是两个进程,server 进程只管通信,关机操作由 client 进程完成,所以 A 也需要一个 client 进程来完成关机操作)
- B 作为 client,接入网络并由 A 控制关机。
- 在 nut-server 所在主机 A 上屏蔽 UPS 主动发来的低电量状态信号。因为有些 UPS 检测到市电输入消失就会立即发送低电量信号,而 NUT 检测到低电量信号就会触发关机操作。所以为了避免这个问题,直接把 UPS 发来的低电量信号屏蔽掉,由 NUT 自己判断什么时候触发低电量状态。
- 在 nut-server 所在主机 A 上不使用 剩余最大可运行时间
battery.runtime.low
进行关机判定。这个值不很准。 - UPS 剩余电量低于 60% 自动关闭 A B 主机。
根据配置目标,A 主机上需要修改的文件有:
- nut.conf
- ups.conf
- upsd.conf
- upsd.users
- upsmon.conf
用 RJ45 转 USB 线连接 UPS 和 A 机器上之后,先在 A 机器上用 lsusb
看看能不能认到 UPS,如果认到了,输出类似这样:
Bus 001 Device 002: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
在 A 机器上安装 NUT client 和 server 并配置自动启动:
sudo apt install nut-server nut-client
sudo systemctl enable nut-server
sudo systemctl enable nut-client
在 A 机器上使用 sudo nut-scanner -U
扫描 UPS 设备。并将 UPS信息填入 /etc/nut/ups.conf 中:
# ...
maxretry = 3
# ...
[myups]
# [] 中省略了部分内容
driver = usbhid-ups
port = auto
desc = "BK650M2-CH"
# ignorelb 表示让 NUT 忽略 UPS 传来的低电量状态标志,
# 有些 UPS 检测到市电输入消失就会立即发送低电量状态信号到 NUT,
# 而 NUT 检测到低电量状态信号就立即触发关机操作。
# 设置 ignorelb 之后,还得设置 battery.charge.low 和 battery.runtime.low
# 这两个变量来定义低电量状态什么时候被触发。
# -1 表示不使用这个变量。
# 变量的值满足下面条件之一就触发关机操作:
# battery.charge < battery.charge.low
# battery.runtime < battery.runtime.low
ignorelb
# 表示不使用 剩余最大可运行时间 来进行低电量状态判定
override.battery.runtime.low = -1
# 表示当 UPS 电量低于 60% 时触发低电量状态,NUT 收到低电量状态信号后关机。
override.battery.charge.low = 60
A 机器 /etc/nut/nut.conf 中的配置:
# ...
MODE=netserver
# 只有一台需要使用 UPS 的设备的话,用 standalone 就行
# 但这里写 netserver 的原因是后续可能有其他的设备使用这个 UPS
# 直接写成 netserver 免得后面再改模式
A 机器 /etc/nut/upsd.conf 中的配置:
# ...
# 这个文件里的多数配置保持默认就够用。
# 如果机器有多个 IP 就读一下 29 到 45 行的 LISTEN 部分,然后自己配置允许哪些 IP 访问 nut-server
# 或者直接写成下面这样,允许从本机的所有 IP 发来的数据包访问 nut-server
LISTEN 0.0.0.0 3493
A 机器 /etc/nut/upsd.users 中的配置:
# ...
# 这个给 nut-server 所在主机上的 client
# [] 中的是 username
# upsmon 值选 slave 表示这台主机在收到关机信号以后立刻关机
# 选 master 表示这台主机最后关机 (等 slave 全部关机以后再关机)
[admin]
password = adminpwd
upsmon master
# 这是 client 要使用的角色,由于需求简单,一个角色就够用了
[slave1]
password = slave1pwd
upsmon slave
在 A 机器上执行 sudo systemctl restart nut-server
让 nut-server 重读 /etc/nut/upsd.conf 和 /etc/nut/upsd.users 并以此配置启动。
在 A 机器上执行 /bin/upsc myups
以检测 nut-server 是否工作正常,输出类似这样的:
Init SSL without certificate database battery.charge: 100 battery.charge.low: 60 battery.mfr.date: 2001/01/01 battery.runtime: 1416 battery.runtime.low: -1 battery.type: PbAc battery.voltage: 13.6 battery.voltage.nominal: 12.0 device.mfr: American Power Conversion device.model: Back-UPS BK650M2-CH device.serial: xxxxxxxxxxxx device.type: ups driver.flag.ignorelb: enabled driver.name: usbhid-ups driver.parameter.pollfreq: 30 driver.parameter.pollinterval: 2 driver.parameter.port: auto driver.parameter.synchronous: no driver.version: 2.7.4 driver.version.data: APC HID 0.96 driver.version.internal: 0.41 input.sensitivity: low input.transfer.high: 278 input.transfer.low: 160 input.voltage: 230.0 input.voltage.nominal: 220 ups.beeper.status: disabled ups.delay.shutdown: 20 ups.firmware: 294803G -292804G ups.load: 19 ups.mfr: American Power Conversion ups.mfr.date: 2022/03/02 ups.model: Back-UPS BK650M2-CH ups.productid: 0002 ups.realpower.nominal: 390 ups.serial: xxxxxxxxxxxx ups.status: OL ups.test.result: Done and passed ups.timer.reboot: 0 ups.timer.shutdown: -1 ups.vendorid: 051d
如果需要实时监测输出结果可以用 watch -n 1 /bin/upsc myups
每隔一秒输出一次命令执行结果。
A 机器 /etc/nut/upsmon.conf 中的配置:
# ...
# 这里写 NUT 要监控些什么
# 主要读一读 32 到 84 行
# 写下面的配置就够了
MONITOR myups@localhost 1 admin adminpwd master
# MONITOR 表示命令
# myups 是 ups.conf 里定义的配置名称
# @ 是连接符,localhost 是主机地址
# 1 表示这台机器只有一个电源
# ( 条件宽裕的机房里每个服务器有 2 个 UPS,有 2 个 UPS 的机器得写更多配置信息,这里略过 )
# admin adminpwd master 是 upsd.conf 里定义的账户、密码与对应角色
最后在 A 机器执行 sudo systemctl restart nut-client
启动 client。
至此 A 机器配置完成。
可以先在 A 机器上执行 watch -n 1 /bin/upsc myups
把 UPS 信息页面拉起来,然后拔掉 UPS 电源测试一下。如果一切正常,拔掉 UPS 电源之后,机器会继续运行,直到 UPS 剩余电量到 60% 的时候,机器关机。
实际使用时可能在连到 A 机器的 SSH 里看到 UPS 剩余电量低于 60% 的时候才关机,这是正常的:
- 因为 /etc/nut/nut.conf 中的 mode 用的是
nutserver
,所以当触发关机指令时,nut-server 所在的 master 节点会先通知各个 slave 节点关机,然后自己再关机。这个过程中:- 如果这个时候刚好 slave 掉线了,那么 nut-server 在等待
DEADTIME
定义的秒数之后才放弃连接这个 slave。 - 如果 slave 全部都收到关机信号了。那么 master 还会继续等待
FINALDELAY
定义的秒数之后才对本机发出关机指令。 - ( 更多配置在 /etc/nut/upsmon.conf 中,可自行查看 )
- 如果这个时候刚好 slave 掉线了,那么 nut-server 在等待
- 如果 nut-server 所在的 master 节点跑的服务比较复杂,那么关机时停止各项服务也需要时间。比如,虚拟化服务器关机前要迁移或关闭其上的虚拟机,这个过程很花时间。
根据配置目标,B 主机中需要修改的文件有:
- nut.conf
- upsmon.conf
在 B 机器上安装 NUT client 并配置自动启动:
sudo apt install nut-client
sudo systemctl enable nut-client
B 机器 /etc/nut/nut.conf 中的配置:
# ...
MODE=netclient
B 机器 /etc/nut/upsmon.conf 中的配置:
# ...
# 这里写 NUT 要监控些什么
# 主要读一读 32 到 84 行
# 写下面的配置就够了
MONITOR myups@<host-A> 1 slave1 slave1pwd slave
# <host-A> 可以是 IP, FQDN 或者主机名
# 这里的 slave1 slave1pwd slave 是在 A 机器上
# /etc/nut/upsd.users 中定义的用户配置
最后在 B 机器执行 sudo systemctl restart nut-client
启动 client。
至此 B 机器配置完成。