李守中
该站已迁往根域名 https://lishouzhong.com
需要注意,迁移后的文章的 url 可能会发生变化。
域名 https://note.lishouzhong.com 下的内容将不再更新,但已有内容会永久保留。

FreeBSD 上的 UPS 工具 NUT

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

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

NUT 在 FreeBSD 上有 4 个 daemon:

NUT 的配置文件被放在 /usr/local/etc/nut 下,主要要用到的有:

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

配置目标:

UPS 设备为 APC BK650M2-CH,它使用 USB 与主机通信。以下操作在 FreeBSD 中完成,NUT 软件的版本为 v2.8.0。

另外,FreeBSD 所在的硬件为 HPE Microserver Gen10,该配置可以正常关闭系统,但来电之后 UPS 无法通知到 Gen10,所以 Gen10 无法在来电之后自动启动。

如果 UPS 支持 switched output outlets 功能的话,可以让 UPS 在来电时有一个断开插座的电源供应再恢复的功能,这个功能可以让 Gen10 在来电时自动启动系统。(需要在 BIOS 里将电源状态调整为 'always power on')

群晖的家用硬件能获取从 USB 发来的电源供应恢复的信号并开机是因为,它在主板上外接了额外的电路来通过 USB 监控 UPS 状态,这个额外的电路可以从连接的 UPS 的 USB 线取电,所以才能保证在关机状态下也能获取 UPS 信息并通知系统开机。

那么最终实现来电自动开机的方案是,在没有接入 UPS 的 RouterOS 路由器上写一个脚本每 10 分钟执行一次。脚本内 ping Gen10,没有回应就使用 WoL 唤醒 Gen10,这个方案也能正常工作。

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

用 RJ45 转 USB 线连接 UPS 和 A 机器上之后,先在 A 机器上用 tail /var/log/messages 可以看到已与 UPS 建立连接,输出类似这样:

Jun 19 21:12:23 gen10 kernel: ugen0.2: <American Power Conversion Back-UPS BK650M2-CH FW:294803G -292804G> at usbus0
Jun 19 21:14:25 gen10 kernel: ugen0.2: <American Power Conversion Back-UPS BK650M2-CH FW:294803G -292804G> at usbus0 (disconnected)
Jun 19 21:14:26 gen10 kernel: ugen0.2: <American Power Conversion Back-UPS BK650M2-CH FW:294803G -292804G> at usbus0

或者执行 usbconfig 可以看到这样的输出:

ugen0.1: <AMD XHCI root HUB> at usbus0, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)
ugen1.1: <AMD EHCI root HUB> at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen0.2: <American Power Conversion Back-UPS BK650M2-CH FW:294803G -292804G> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)
ugen1.2: <vendor 0x0438 product 0x7900> at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (100mA)

在 A 机器上 sudo pkg install nut 安装 NUT 软件。在 /etc/rc.conf 中配置自动启动:

# UPS configs of NUT master
nut_enable="YES"
nut_upslog_enable="YES"
nut_upsmon_enable="YES"

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

# ...
maxretry = 3
# ...
[nutdev1]
    desc = "BK650M2-CH"
    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

A 机器 /usr/local/etc/nut/nut.conf 中的配置:

# ...
MODE=netserver

# 只有一台需要使用 UPS 的设备的话,用 standalone 就行
# 但这里写 netserver 的原因是后续可能有其他的设备使用这个 UPS
# 直接写成 netserver 免得后面再改模式

A 机器 /usr/local/etc/nut/upsd.conf 中的配置:

# ...
# 这个文件里的多数配置保持默认就够用。
# 如果机器有多个 IP 就读一下 54 到 72 行的 LISTEN 部分,然后自己配置允许哪些 IP 访问 nut-server
# 或者直接写成下面这样,允许从本机的所有 IP 发来的数据包访问 nut-server
LISTEN 0.0.0.0 3493

A 机器 /usr/local/etc/nut/upsd.users 中的配置:

# ...
# 这个给 nut-server 所在主机上的 client
# [] 中的是 username
# upsmon 值选 slave 表示这台主机在收到关机信号以后立刻关机
#   选 master 表示这台主机最后关机 (等 slave 全部关机以后再关机)
[admin]
    password = adminpwd
    upsmon master

# 这是 client 要使用的角色,由于需求简单,一个角色就够用了
[slave1]
    password  = slave1pwd
    upsmon slave

在 A 机器上启动 upsd, upslog:

另外,如果第一条命令报找不到 HID 设备:

libusb1: Could not open any HID devices: no USB buses found

sudo nut-scanner -U 确实可以扫描到 UPS 设备。那么此时需要重启系统即可解决这个问题,目前问题的原因还不清楚,推测可能是有内核模块没加载。

在 A 机器上执行 sudo upsc nutdev1 以检测 upsd 是否工作正常,输出类似这样的:

battery.charge: 97
battery.charge.low: 60
battery.mfr.date: 2001/01/01
battery.runtime: 3116
battery.runtime.low: -1
battery.type: PbAc
battery.voltage: 13.5
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.bus: 000
driver.parameter.pollfreq: 30
driver.parameter.pollinterval: 2
driver.parameter.port: auto
driver.parameter.product: Back-UPS BK650M2-CH FW:294803G -292804G
driver.parameter.productid: 0002
driver.parameter.serial: xxxxxxxxxxxx
driver.parameter.synchronous: auto
driver.parameter.vendor: American Power Conversion
driver.parameter.vendorid: 051D
driver.version: 2.8.0
driver.version.data: APC HID 0.98
driver.version.internal: 0.47
driver.version.usb: libusb-1.0.0 (API: 0x1000102)
input.sensitivity: low
input.transfer.high: 278
input.transfer.low: 160
input.voltage: 239.0
input.voltage.nominal: 220
ups.beeper.status: disabled
ups.delay.shutdown: 20
ups.firmware: 294803G -292804G
ups.load: 12
ups.mfr: American Power Conversion
ups.mfr.date: 2023/05/22
ups.model: Back-UPS BK650M2-CH
ups.productid: 0002
ups.realpower.nominal: 390
ups.serial: xxxxxxxxxxxx
ups.status: OL CHRG
ups.test.result: No test initiated
ups.timer.reboot: 0
ups.timer.shutdown: -1
ups.vendorid: 051d

A 机器 /usr/local/etc/nut/upsmon.conf 中的配置:

# ...
# 这里写 NUT 要监控些什么
# 主要读一读 32 到 111 行
# 写下面的配置就够了

MONITOR nutdev1@localhost 1 admin adminpwd master

# MONITOR 表示命令
# nutdev1 是 ups.conf 里定义的配置名称
# @ 是连接符,localhost 是主机地址
# 1 表示这台机器只有一个电源
# (条件宽裕的机房里每个服务器配 2 个 UPS,有 2 个 UPS 的机器配置信息更多,这里略过)
# admin adminpwd master 是 upsd.conf 里定义的账户、密码与对应角色

最后在 A 机器启动 client 进程 upsmon sudo /usr/local/etc/rc.d/nut_upsmon restart

至此 A 机器配置完成。

可以先在 A 机器上拔掉 UPS 电源测试一下。如果一切正常,拔掉 UPS 电源之后,机器会继续运行,直到 UPS 剩余电量到 60% 的时候,机器关机。

实际使用时可能在连到 A 机器的 SSH 里看到 UPS 剩余电量低于 60% 的时候才关机 (电量剩余 59% 的时候才开始关机,完成关机以后剩余电量可能小于 59%),这是正常的:

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

在 B 机器上 sudo pkg install nut 安装 NUT 软件。在 /etc/rc.conf 中配置自动启动:

# UPS configs of NUT slave
#nut_upslog_enable="YES"
nut_upsmon_enable="YES"

B 机器 /usr/local/etc/nut/nut.conf 中的配置:

# ...
MODE=netclient

B 机器 /usr/local/etc/nut/upsmon.conf 中的配置:

# ...
# 这里写 NUT 要监控些什么
# 主要读一读 32 到 111 行
# 写下面的配置就够了

MONITOR nutdev1@<host-A> 1 slave1 slave1pwd slave

# <host-A> 可以是 IP, FQDN 或者主机名
# 这里的 slave1 slave1pwd slave 是 A 机器上的
# /usr/local/etc/nut/upsd.users 中定义的用户配置

最后在 B 机器执行 sudo /usr/local/etc/rc.d/nut_upsmon restart 启动 client。

至此 B 机器配置完成。



Last Update: 2024-01-09 Tue 09:49

Generated by: Emacs 28.2 (Org mode 9.5.5)   Contact: lsz.sino@outlook.com

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议