Linux 上的 UPS 工具 NUT 

Last Update: 2024-01-09

NUT 是 C/S 架构的软件。它的优点在于省成本,不用买很贵的带网络管理卡的 UPS,只需要一个 master 节点能和 UPS 通信就够了。断电时候 master 节点可以通过网络通知 slave 节点关机。

它的缺点是,如果停电后、来电前的时间间隔较短,UPS 没有自动关闭,那么,来电后,所有节点启动时,要先等 master 节点启动 (server 所在机器),然后再在 master 节点上使用 wake-on-lan 唤醒其他 slave 节点。而如果停电后、来电前的时间间隔较长,UPS 自动关闭,那么,来电后 UPS 重新开机也可以让所有连接到该 UPS 的主机自动开机。不必先等 master 节点启动,再由 master 节点唤醒其他节点。

UPS 为 APC BK650M2-CH,它使用 USB 与主机通信。以下操作在 Debian 12 / RHEL 9 测试通过,nut-server 和 nut-client 的版本号均为 v2.8.0。

NUT 的配置文件:

man [ nut.conf | ups.conf | upsd.conf | upsd.users | upsmon.conf | upssched.conf ] 可查看详细信息。

NUT 工具包在 Debian 系系统和 RedHat 系系统上的不同:

此外,网上有些教程里写用 sudo systemctl enable nut-server nut-monitor 启动 server 进程和 client 进程,然后用 sudo systemctl enable nut-driver@<ups-config-name> 生成驱动相关的 .service 文件,最后使用 systemctl 启动 nut-server.service, nut-monitor.servicenut-driver@<ups-config-name> 三个服务。这个做法就是前文提到的手动配置每个 service 和 target,但它不如官方提供的 nut.target 完备,而且做不到自动更新 /etc/nut/ups.conf 的信息到驱动配置中。

NUT 的 systemd 配置文件的用途:

配置目标:

根据配置目标,A 主机上需要修改的文件有:

安装 NUT server 和 client 到 A 机器,用 RJ45 转 USB 线连接 UPS 和 A 机器,A 机器认到 UPS 的话有这样的输出:

# lsusb | grep American
...
Bus 001 Device 002: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
...
# dmesg | grep American
...
[34032.651776] usb 1-1.4: Product: Back-UPS BK650M2-CH FW:294803G -292804G
[34032.651779] usb 1-1.4: Manufacturer: American Power Conversion
...

在 A 机器上使用 sudo nut-scanner -U 扫描 UPS 设备。并将 UPS信息填入 /etc/nut/ups.conf 中:

# ...
maxretry = 3
# ...
[myups]
    # 这里省略了部分内容
    desc = "BK650M2-CH old"
    driver = "usbhid-ups"
    port = "auto"
    vendorid = "051D"
    productid = "0002"
    product = "Back-UPS BK650M2-CH FW:294803G -292804G"
    serial = "xxxxxxxxxxxx"
    vendor = "American Power Conversion"
    # 有了上面的 port = "auto" 配置,upsd 可以自动找到对应的 UPS
    # 所以这个指定 usb 端口的选项可以注释掉
    #bus = "000"
    # 这个参数设置了 NUT 从 USB 接口快速更新 (quick update) UPS 状态的时间间隔
    # 默认值 2s,改成 6s 可以减轻通信压力,不过 2s 的频率也没什么压力
    #pollinterval = 6
    # pollfreq 是从 USB 接口全量更新 (full update) UPS 状态的间隔,默认 30s
    #pollfreq = 30
    # 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 电量低于 40% 时触发低电量状态,NUT 收到低电量状态信号后关机。
    # 选择 40% 是为了方便定期放电到 50% 保持电池健康,但又不让机器关机。
    # 40% 的电量足够让机器在断电时有足够电量撑到所有 slave 关机后自己再关机。
    override.battery.charge.low = 40

需要注意,写在 /etc/nut/ups.conf 中 UPS 驱动相关的配置由 /usr/lib/systemd/system/nut-driver-enumerator.service 处理,这个 .service 会根据 /etc/nut/ups.conf 中的信息在 /etc/systemd/system 目录下生成 nut-driver@myups.service.d, nut-driver@.service.d, nut-driver.target.wants 三个与 ups 驱动相关的目录。

这个 .service 不会被自动调用,需要用户手动调用 sudo systemctl restart nut-driver-enumerator.service 来更新驱动相关的信息。另外,由于这是个 .service 文件,所以可以将其设置为系统启动后自动执行,但并不推荐这样做。

每次更改 /etc/nut/ups.con 文件之后都需要手动执行 sudo systemctl restart nut-driver-enumerator.service 并不方便,所以,有了 /usr/lib/systemd/system/nut-driver-enumerator.path 这个路径监视服务。这个服务默认不启用,它的用途是在 /etc/nut/ups.conf 中的内容改变时,自动调用 nut-driver-enumerator.service 更新配置到驱动中。使用 sudo systemctl enable nut-driver-enumerator.path 启动路径监视服务后每次修改 /etc/nut/ups.conf 之后,配置都会被自动更新到驱动中。

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: 40
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-monitor 启动 client。

至此 A 机器配置完成。

可以先在 A 机器上执行 watch -n 1 /bin/upsc myups 把 UPS 信息页面拉起来,然后拔掉 UPS 电源测试一下。如果一切正常,拔掉 UPS 电源之后,机器会继续运行,直到 UPS 剩余电量到 40% 的时候,机器关机。

实际使用时可能在连到 A 机器的 SSH 里看到 UPS 剩余电量低于 40% 的时候才关机,这是正常的:

根据配置目标,B 主机中需要修改的文件有:

在 B 机器上安装 NUT client 并配置自动启动:

这里需要注意,B 机器只作为 nut-client,所以不需要配置开机自动加载 nut.target;B 机器不直接与 UPS 通信,所以不需要配置 ups.conf,也就不必配置 nut-driver-enumerator.path。只需要把 nut-monitor 进程拉起来就行。

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-monitor 启动 client。

至此 B 机器配置完成。