为Home Server配置UPS(一):配置NUT服务监控UPS - Setup NUT Primary Node

Self Hosted 自托管
Apr 22, 2023 ~

最近刚刚把 Proxmox 主机上面的 NUT server 搞定,趁着热乎赶紧水一篇,过几天可能就又忘了。NUT 服务是 C/S 架构的,主要考虑的应用场景是,UPS给多台电脑供电,而其中一台主机为主,它负责与UPS的通信,同时也成为NUT中的服务器。其他的主机则运行客户端,经由NUT server获取UPS的情况数据以及决定是否需要关机等操作。

首先是server主机,需要负责与UPS的通信链接,并需要配置一些服务端的信息。

  • Step 1: Install NUT
    • apt install nut
    • 这一步应该也会创建一个 nut 的用户(会有一个单独的group: nut)
  • Step 2: Use nut-scanner to find the connected UPS device
$ nut-scaner

# Output
SNMP library not found. SNMP search disabled.
Neon library not found. XML search disabled.
IPMI library not found. IPMI search disabled.
Scanning USB bus.
No start IP, skipping NUT bus (old connect method)
[nutdev1]
        driver = "usbhid-ups"
        port = "auto"
        vendorid = "051D"
        productid = "0002"
        product = "Back-UPS NS 1500M2 FW:##### .D USB FW:##"
        serial = "XXXXXXXXXXXX"
        vendor = "American Power Conversion"
        bus = "001"
  • Step 3:  Config the mode of this server for NUT service in /etc/nut/nut.conf
# content of /etc/nut/nut.conf

MODE=netserver
  • Step 4: UPS device configuration file: /etc/nut/ups.conf.
    • 其中一些参数的意义:
      • offdelay: offdelay 参数用于指定 UPS 与市电断开后,upsmon给出了FSD后,然后已经进行到SHUTDOWN这个信号之后,UPS的输出口多长时间之后断电。这个时间相当于给UPS上面负载的主机关机的时间。这个时间到达之后,无论怎么样,UPS都会断开负载的电源。
        • 注意这个设置,实际上一旦开始(从到达server端进行到SHUTDOWN信号之后)就没有回头路了。UPS输出的断电已经是注定发生的了,无论期间市电是否恢复。
          • Server端主机在进行SHUTDOWN的时候(通常就是 /sbin/shutdown -h now),会调用 upsdrvctl shutdown (具体在哪一步得看具体的systemd配置)。UPS本身收到这个信号之后就会开始倒计时。这个倒计时的时间就是 offdelay.
          • 这个值默认一般是30秒,如果只有一台主机的话,一般也是够的。主要需要考虑的是从机(NUT client端到主机)。Server主机一般运行到 upsdrvctl shutdown 的时候基本也就要完成关机了,但是从机什么时候关机能完成,就不好说了。我们需要在这个倒计时结束之前,从机都完成关机操作。
          • UPS负载端的断电操作通常来讲还是需要的,这个可以防止主机主板误以为现在依旧有电,进而开机。
      • ondelay: ondelay 参数用于指定 UPS 重新上电后,NUT 客户端将会等待多少时间后开始重新开机。这个计时器的开始时间跟 offdelay一样,也是 upsdrvctl shutdown 被调用的时间点。
        • 准确来讲,这个是,假设upsdrvctl shutdown 调用后,市电立刻恢复了,那么在这种情况下,UPS最快多长时间之后恢复输出口的输出。
        • 假设这个时间过去之后,市电才恢复,那么 UPS 会立刻恢复负载端的输出。
        • 这个设置对于BIOS上面设置了“来电自动启动”的主机来说是需要稍微注意一下的。offdelay 之后的断电,主板是可以会察觉到的,而后的上电也被察觉到之后,主板才会开机。但是这个“断电”和“上电”之间应该是有一个间隔的,有些主板或者系统对这个间隔的长度是有要求的(比如大于10秒之类的)。所以这里就有了ondelay 的这个要求。
        • ondelay 和 offdelay 被使用到的场景是,NUT server 主机已经开始了关机流程,这个流程已经不为UPS上面是不是有市电所改变了。
        • ondelay 当然就必须要比 offdelay 大了。
# content of /etc/nut/ups.conf
pollinterval = 15 # Num of seconds between UPS info polling, default is 5 seconds
maxretry = 3 # Generally this is just the number of retries, effective together with 'retrydelay'. Normal: 3~5.
offdelay = 120 # 2 minutes to make sure all other UPS-powered hosts are shut down.
ondelay = 135 # Minimum 15 seconds of interval between 'no power' and 'power back online' from the host perspective.
lowbatt = 33 # Consider 33% as the threshold for low battery.

# So the name of my UPS is 'apc' now
[apc]
driver = usbhid-ups # From nut-scanner
port = auto
desc = "APC UPS 1500M2" # Just a string for yourself, should be human readable
vendorid = 051D
productid = 0002
serial = XXXXXXXXXXXX
  • Step  5: Start UPS driver
    • Run: upsdrvctl start
      • NOTE:这一步可能也可以用 systemctl status nut-driver.service 来验证是否一切正常。
    • Output should be like below
      • Note: If you’ve already kicked it off, you will see messages like Duplicate driver instance detected! Terminating other driver!, then Communications with UPS <your_ups_name>@localhost lost. It is totally fine
Network UPS Tools - UPS driver controller 2.7.4
Network UPS Tools - Generic HID driver 0.41 (2.7.4)
USB communication driver 0.33
Using subdriver: APC HID 0.96  
  • Step 6-1: Setup the NUT Daemon
    • Server端实际上要配置的东西很少,大多逻辑/策略都是在客户端的(Server端主机也是需要运行一个客户端的)。
    • Daemon config: /etc/nuc/upsd.conf
LISTEN 0.0.0.0 3493
LISTEN :: 3493
  • Step 6-2: Setup NUT server users (Generally, a simple ACL)
    • 你需要一个 admin 用户,被允许设定 UPS,另外还需要一些单纯的 read-only users,来获取UPS的信息,以判断是不是要关机。
    • File: /etc/nut/upsd.users
# So the admin user is 'upsadmin'
[upsadmin]
password = admin_password # Your admin user 'upsadmin' password
actions = SET
actions = FSD # So this user can do 'set' and 'fsd' (force shutdown)
instcmds = ALL
upsmon master # 用这个账户的主机是“主”机 (comm with UPS).

# A normal user for normal access
[upsuser]
password = normal_user_password
upsmon slave # 用这个账户的主机是从机
  • Step 6-3: 启动 nut-server 服务
    • 此时NUT server已经配置好,启动即可:service nut-server start
  • Step 7-1:配置 /etc/nut/upsmon.conf
    • 这一步开始就是配置 client端的的策略逻辑了。
      • 解释一下几个容易搞错的参数:
        • RUN_AS_USER
          • 执行脚本的时候用的用户。应该用之前安装 nut 的时候创建的 nut 用户
          • 不用担心这个用户有没有权限执行 SHUTDOWNCMD 上面的命令。NUT在运行的时候,会保留一个root user的 session,就是用来执行shutdown命令的。而其他的策略判断方面,就会用这里指定的用户。
        • MONITOR
          • 这里要填这个 client 要监视的 UPS 的标识,以及这个monitor 作为 client,要用的 user
          • 对于我这里的server端主机,就应该是 MONITOR apc@localhost 1 upsadmin admin_password master.
          • 而从机就应该是 MONITOR apc@<server_ip> 1 upsuser normal_user_password slave
        • MINSUPPLIES
          • 这个是最小的,认为还有足够UPS支持供电的,UPS个数。一般就是1了(除非你有多个UPS给服务器进行供电)
        • SHUTDOWNCMD
          • 这个是在upsmon在决定要进行关机操作的时候(即触发 SHUTDOWN 信号之后),要执行的脚本。一般就用 /sbin/shutdown -h +0 或者 /sbin/shutdown -h now. 在有些系统中,可能会修改为将硬盘之类的挂起,而不是关机(类似休眠吧),有些情况更复杂,需要迁移VM/CT之类的附加操作,然后才能关机。
        • NOTIFYCMD
          • 这个是当有事件触发的时候,要触发的script。一般就用 /usr/sbin/upssched。只有在用这个 script 的时候,后面设定的 upssched-cmd 和 upssched.conf 才会被用到。
        • POLLFREQ
          • 这个就是平时 POLL 的间隔,单位为秒。一般5秒足够了。
        • POLLFREQALERT
          • 在触发UPS已经在用电池供电之后,poll的间隔。一般来讲可能是POLLFREQ的一半。因为在用电池之后,可能就要触发一系列的关机操作了,这个时候客户端最好更频繁的poll server端的信息,来让自己更快知道是不是应该触发什么操作。
        • HOSTSYNC
          • 这个实际上是主-从机之间通信的参数。当主机告知从机要关机的时候,主机会等待从机回应。如果从机没有回应。那么主机在等待HOSTSYNC秒之后,就不管从机了,从而防止 unresponsive的从机把整个关机流程卡掉。
        • DEADTIME
          • host会根据上面的 POLLFREQ 和 POLLFREQALERT 来poll UPS的信息。如果poll失败了,这个UPS会在客户端被标记为stale,如果UPS在stale的状态保持了超过DEADTIME秒,则这个UPS会被认为不可用了。
          • 如果在“不可用”状态之前,UPS已经是on-battery的状态了,那么host就会触发 on-battery → low-battery (aka OB → LB)的转变(LOWBATT信号),进而运行 LOWBATT所对应的策略,进而可能就会关机。
        • POWERDOWNFLAG
          • 不要动这个,就用默认的 /etc/killpower 就好。
        • NOTIFYMSG
          • 基本上就是针对不同信号的一个message string builder
        • NOTIFYFLAG
          • 针对不同的信号,需要触发那一类操作,SYSLOG就是写syslog,WALL就是warn all users,EXEC就是运行NOTIFYCMD
        • RBWARNTIME
          • 当 RB 事件被触发(replace battery),则会运行一个 timer,每到这个 timer的时间,一个message以及syslog就会打印,告诉用户该换电池了。这个timer的时长就在这里设定,一般就是12小时(43200秒)
        • NOCOMMWARNTIME
          • 如果联系不上 UPS,则每过这么长的时间,就发送一次通知,告知用户UPS断联了。
        • FINALDELAY
          • upsmon 在收到 SHUTDOWN 信号之后(如果是server端,还需要通知各个从机),再等待多长时间之后,开始进行关机操作(首先是 create POWERDOWNFLAG,然后是call SHUTDOWNCMD)。5秒是合理的。
            • 通常在SHUTDOWNCMD的执行中,关机过程中会发出 upsdrvctl shutdown 。这时之前在 ups.conf里面设置的 [on|off]delay就用上了。
    • 示例如下
RUN_AS_USER nut
MONITOR apc@localhost 1 upsadmin admin_password master

MINSUPPLIES 1

SHUTDOWNCMD "/sbin/shutdown -h"
NOTIFYCMD /usr/sbin/upssched

POLLFREQ 5
POLLFREQALERT 2
HOSTSYNC 15
DEADTIME 30
POWERDOWNFLAG /etc/killpower

NOTIFYMSG ONLINE "UPS %s on line power"
NOTIFYMSG ONBATT "UPS %s on battery"
NOTIFYMSG LOWBATT "UPS %s battary is low"
NOTIFYMSG FSD "UPS %s: forced shutdown in progress"
NOTIFYMSG COMMOK "Communications with UPS %s established"
NOTIFYMSG COMMBAD "Communications with UPS %s lost"
NOTIFYMSG SHUTDOWN "Auto logout and shutdown proceeding"
NOTIFYMSG REPLBATT "UPS %s battery needs to be replaced"
NOTIFYMSG NOCOMM "UPS %s is unavailable"
NOTIFYMSG NOPARENT "upsmon parent process died - shutdown impossible"

NOTIFYFLAG ONLINE   SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT   SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT  SYSLOG+WALL+EXEC
NOTIFYFLAG FSD      SYSLOG+WALL+EXEC
NOTIFYFLAG COMMOK   SYSLOG+WALL+EXEC
NOTIFYFLAG COMMBAD  SYSLOG+WALL+EXEC
NOTIFYFLAG SHUTDOWN SYSLOG+WALL+EXEC
NOTIFYFLAG REPLBATT SYSLOG+WALL
NOTIFYFLAG NOCOMM   SYSLOG+WALL+EXEC
NOTIFYFLAG NOPARENT SYSLOG+WALL

RBWARNTIME 43200
NOCOMMWARNTIME 600

FINALDELAY 5
  • Step 7-2: 配置 /etc/nut/upssched.conf
    • 这个文件需要 NOTIFYCMD是 /usr/sbin/upssched的时候才会起作用。
    • 参数解释
      • CMDSCRIPT
        • 这个指定的是,下面介绍的 AT 指令里面,触发动作的时候,要执行的脚本
      • PIPEFN
        • Timer运行的时候收发信号的pipe。这个需要在一个已存在的文件夹内,而且这个文件夹需要对于 RUN_AS_USER 的用户可读写。
      • LOCKFN
        • 用于防止race condition的锁文件。跟PIPEFN的需求一样。
      • AT
        • 在不同的信号(ONBATT, LOWBATT, ONLINE,SHUTDOWN等)下,触发的操作。
    • 示例
CMDSCRIPT /etc/nut/upssched-cmd

# This folder '/etc/nut/' must be accessible to the 'RUN_AS_USER' user.
# Use `chmod/chown` to set the permission properly.
PIPEFN /etc/nut/upssched/upssched.pipe
LOCKFN /etc/nut/upssched/upssched.lock

# When UPS switched to battery, trigger CMDSCRIPT with 'onbatt' after 10 seconds.
# If the UPS switched back to online (not on battery anymore), cancel the 10-sec timer,
# and trigger CMDSCRIPT with 'online'
AT ONBATT * START-TIMER onbatt 10
AT ONLINE * CANCEL-TIMER onbatt online

# When UPS being on battery for more than 2 minutes, trigger CMDSCRIPT with 'early-shutdown'
# Also cancel the timer if the power recovered within 2 minutes, and trigger CMDSCRIPT with
# 'online'
AT ONBATT * START-TIMER early-shutdown 120
AT ONLINE * CANCEL-TIMER early-shutdown online

# Critical cases
AT LOWBATT * EXECUTE lowbatt
AT SHUTDOWN * EXECUTE shutdown
AT FSD * EXECUTE fsd

# MISC
# After 15 seconds of the UPS on COMMBAD state, trigger CMDSCRIPT with 'commbad'
AT COMMBAD * START-TIMER commbad 15
# Cancel the timer above if the UPS back to COMMOK and trigger CMDSCRIPT with 'commok'
AT COMMOK * CANCEL-TIMER commbad commok
# Once UPS on NOCOMM state, trigger CMDSCRIPT with 'nocomm'
AT NOCOMM * EXECUTE nocomm
  • Step 7-3:配置上面已经引用的 /etc/nut/upssched-cmd
    • 首先要注意,这个script 对于之前设定的 RUN_AS_USER 是需要有运行权限的。
    • 示例内容如下
      • 可以看到,除了 early-shutdown 以外,其他的都只是 logging 而已。只有 early-shutdown 这个情况,我们是需要主动 trigger 一个 FSD 信号的。
        • 注意 lowbatt的情况是被认为是 critical的,所以 upsmon 会自动trigger FSD。
#!/bin/sh
 case $1 in
       nocomm)
          MSG="UPS no comm"
          ;;
       commbad)
          MSG="UPS comm bad"
          ;;
       commok)
          MSG="UPS comm ok"
          ;;
       onbatt)
          MSG="UPS running on battery"
          ;;
       online)
          MSG="UPS back online"
          ;;
       early-shutdown)
          MSG="UPS on battery too long, early shutdown"
          /usr/sbin/upsmon -c fsd
          ;;
       lowbatt)
           MSG="UPS Low Battery Warning"
          ;;
       shutdown)
          MSG="UPS - shutdown in progress"
          ;;
       fsd)
          MSG="UPS forced shutdown has been executed (i.e. upsmon -c fsd)"
          ;;
       *)
          MSG="Unrecognized command: $1"
          ;;
 esac
 logger -t upssched-cmd $MSG
  • Step 7-4: Start nut-client
    • systemctl start nut-client.service

至此,主控端(server端)的NUT配置就算完成了。一个简单的连通性测试是 upsc apc@localhost (看看client直接poll本地的这个叫 apc 的UPS,能不能拿到数据)。至于控制策略,就需要采用拔电源的方法来看是不是起效果了。

那到现在为止,CGI (也就是 web UI)的配置以及从机的配置都还没有涉及,留待下一篇来讲吧。

关于UI的配置:

为Home Server配置UPS(二): 配置NUT服务的Web UI - Setup NUT - Web UI
UPS 本文紧接之前的Primary Node。没有搞定主机和UPS通信的朋友请想看链接的这个文章。 配置NUT服务监控UPS: Setup NUT - Primary Node最近刚刚把 Proxmox 主机上面的 NUT server 搞定,趁着热乎赶紧水一篇,过几天可能就又忘了。NUT 服务是 C/S架构的,主要考虑的应用场景是,UPS给多台电脑供电,而其中一台主机为主,它负责与UPS的通信,同时也成为NUT中的服务器。其他的主机则运行客户端,经由NUTserver获取UPS的情况数据以及决定是否需要关机等操作。 首先是server主机,需要负责与UPS的通信链接,并需要配置一些服务…

标签