起因
配置了一台All in One主机,系统是装的PVE,一个linux的虚拟机。里面装了openwrt软路由,还有OMV这个NAS系统。为了防止数据丢失,最好是配置一台UPS来保护数据,毕竟数据无价。于是买了一台山特的TGBOX-850。由于山特官方的winpower不支持linux系统,只能用NUT来对接。
安装NUT
apt update
apt install nut
NUT简介
nut 主要包含三个核心服务:
- nut-driver: 这个服务负责通过特定放驱动来与 UPS 进行通信
- nut-server: 该服务利用 nut-dirver 沟通 UPS, 并将 UPS 状态通过网络服务发布
- nut-monitor(nut-client): 该服务连接 nut-server, 根据 UPS 状态做出特定响应
nut-client
┌─────────────┐ ┌────────────┐ nut.conf 只配置运行模式
┌──── │ nut-monitor │ ───────────────► │ nut-server │ upsd.*
│ └─────────────┘ └────────────┘
│
│ │
│ │
▼ ▼
┌─────────────┐ ┌────────────┐
│ upssched │ │ nut-driver │ ups.conf
└─────────────┘ └────────────┘
│ │
│ │
│ │
▼ ▼
┌────────────────┐ ┌─────────────┐
│ user scripts │ │ UPS(HW) │
└────────────────┘ └─────────────┘
从上图可以看出,nut是基于C/S的设计,可以有多个client,对应这ups供电的多个设备,不过我这里只有1个设备需要用保护,就是我的AIO主机,其它多设备的情况请以此类推。
nut的默认配置文件位于 /etc/nut 目录,后面提及的配置文件没有写完整路径的话,都在该目录下
配置
首先将ups上电,并把usb线连上被保护的设备,我这里是pve的机子。首先确认驱动是否正常:
root@VM:~# lsusb
Bus 004 Device 002: ID 8087:8002 Intel Corp. 8 channel internal hub
Bus 004 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 8087:800a Intel Corp. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 002: ID 18f8:0f99 [Maxxter] Optical gaming mouse
Bus 002 Device 004: ID 0463:ffff MGE UPS Systems UPS
Bus 002 Device 003: ID 24ae:4005 Shenzhen Rapoo Technology Co., Ltd. Rapoo Gaming Keyboard
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
使用 lsusb 命令可以看到,我这里已经有了ups的驱动程序
如果你也是tgbox-850,那么在debian linux里面应该是没有驱动问题的。如果是其它ups,则需要先解决驱动问题。
ups配置 (nut-driver)
ups.conf 是 nut-driver 沟通ups硬件的配置
可以先执行 nut-scanner 得到一些信息
那我的 ups.conf 内容如下:
root@VM:~# grep -v '^#.*' /etc/nut/ups.conf
maxretry = 3
[tgbox850]
driver=usbhid-ups
port=auto
vendorid=0463
desc="SANTAK TGBOX-850 UPS"
pollinterval = 1
override.battery.charge.low = 60
最后[tbgox850] 是需要我们手动添加的内容,可以参考上面的输出进行配置
其中 “override.battery.charge.low = 60” 是改变 ups 电量降到 60%的时候报低电量,默认这个是20,我这里是修改了一下,按自己的设备负载来计算就好
tbgox850 是我取的ups名字,后面client来查询的时候需要用到
nut-server配置
nut.conf
主要定义 nut 的运行模式, 只有一个配置字段 MODE=xxx, 该配置可选值及含义如下:
- none: nut未配置
- standalone: 独立模式, 一般在只有一个 UPS 且只负责本地系统(不提供网络服务)的情况下使用
- netserver: 跟独立模式类似, 会启动 driver、upsd 和 upsmon 服务, 不同之处是可以提供网络服务, 其他机器上的 nut-monitor 可以通过网络来连接 Nut Server
- netclient: 仅客户端模式, 只启动 nut-monitor, 用于连接远程的 nut服务
我这里的环境设置成 stanalone
MODE=standalone
upsd.conf
设置监听的网络端口,我这里只给本机用,因此只监听在 127.0.0.1,如果你要给其它机子来连,则最好将ip改成 0.0.0.0
LISTEN 127.0.0.1 3493
upsd.users
用于定义通过网络连接到 nut-server 的用户名和密码,这里的用户名并不是本地linux的账户,是自己随意指定的。client来连的时候使用它们进行认证而已,如果没有,hacker连接过来也能操作你的ups,那多悲剧。我的配置如下:
[monuser]
password = secret
upsmon master
其中,monuser是用户名,而secret 就是对应的密码,这里的用户名和密码自己设定好即可。后面client端使用同样的用户名和密码就行。
nut-client配置
upsmon.conf
主要用于配置 nut-monitor 如何监控 UPS, 同时定义 UPS 出现哪些事件要进行怎样的处理动作
主要设置以下一些字段: RUN_AS_USER、MONITOR、POWERDOWNFLAG、SHUTDOWNCMD、NOTIFYCMD、NOTIFYFLAG
RUN_AS_USER: 以什么用户执行,我这里为了权限问题,用的是root
MONITOR:定义如何和nut-server沟通的动作,语法如下
MONITOR <system> <powervalue> <username> <password> ("master"|"slave")
- <system>: nut-server 链接地址, 格式为 “UPS 名称” + “@” + “nut-server 地址”, 例如 myups@192.168.1.2
- <powervalue>: UPS 数量, 大多数情况你只有一个 UPS 电源, 所以写 1 就行
- <username>/<password>: 在 upsd.users 中定义的用户名和密码
- master/slave: master 表示该系统将最后关闭, 让从属系统先关闭; slave 表示该系统立即关闭
我这里的设置如下
MONITOR tgbox850@localhost 1 monuser secret master
localhost就是127.0.0.1 tgbox850 是前面取的ups名字
POWERDOWNFLAG:改变POWERDOWNFLAG /etc/killpower
官方的解释如下:
upsmon creates this file when running in primary mode when the UPS needs to be powered off. You should check for this file in your shutdown scripts and call upsdrvctl shutdown
if it exists.
SHUTDOWNCMD: 需要关机的时候调用的程序,想直接关机,而不做其它事情的话,可以写成
SHUTDOWNCMD "/sbin/shutdown -h +0"
如果想执行自己的脚本关机,则将引号内的内容改成你脚本的完整路径即可,记得给脚本增加执行权限。我这里是
SHUTDOWNCMD "/root/shutdown/ups-shutdown.sh"
NOTIFYCMD:用于配置在发生特定事件(如市电中断、UPS 处于低电量等)时执行的程序,通常使用upssched来间接调用用户脚本来实现。如果自己写脚本,需要对接server的参数,比较麻烦。
NOTIFYCMD /sbin/upssched
NOTIFYFLAG:需要与 NOTIFYCMD
配合使用; NOTIFYFLAG
指令负责指定一系列的 UPS 事件应该触发何种操作。语法如下:
NOTIFYFLAG <notify type> <flag>[+<flag>][+<flag>] ...
<notify type> 表示事件类型, 可选类型如下:
- ONLINE: UPS 在线, 即市电恢复时会触发
- ONBATT: UPS 使用电池供电, 即市电中断时会触发
- LOWBATT: UPS 低电量时会触发
- FSD: UPS 正在被关闭(Forced Shutdown)
- COMMOK: 与 nut-server 成功建立连接时触发
- COMMBAD: 与 nut-server 建立连接失败(连接丢失)时触发
- SHUTDOWN: UPS 发出关机指令触发
- REPLBATT: UPS 需要更换电池时触发
- NOCOMM: 无法与 UPS 建立连接(UPS未就绪)时触发
<flag> 标志通常有四种, 多种组合时用加号(+)连接:
- SYSLOG: 只打印 syslog
- WALL: 在终端上弹出消息(/bin/wall)
- EXEC: 调用 NOTIFYCMD 指定的命令, 并传递相关事件
- IGNORE: 啥也不干, 忽略该事件
通常把自己感兴趣的事件写进去,我的如下:
NOTIFYFLAG ONLINE SYSLOG+EXEC
NOTIFYFLAG ONBATT SYSLOG+EXEC
NOTIFYFLAG LOWBATT SYSLOG+EXEC
NOTIFYFLAG FSD SYSLOG+EXEC
NOTIFYFLAG COMMOK SYSLOG+EXEC
NOTIFYFLAG COMMBAD SYSLOG+EXEC
NOTIFYFLAG SHUTDOWN SYSLOG+EXEC
NOTIFYFLAG REPLBATT SYSLOG+EXEC
NOTIFYFLAG NOPARENT SYSLOG+EXEC
前面提到使用 upssched 来进行事件的监控操作,实际上我们想自己来写脚本监控,通过 upssched中转的配置是 upssched.conf,其内容如下:
CMDSCRIPT /etc/nut/upssched-cmd.sh
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
#AT ONBATT * START-TIMER onbattwarn 180
#AT ONLINE * CANCEL-TIMER onbattwarn
AT ONLINE * EXECUTE ups-back-on-line
AT LOWBATT * EXECUTE lowbatt
AT SHUTDOWN * EXECUTE shutdown
AT REPLBATT * EXECUTE replace
以上配置表示 会调用 我们自己写的 /etc/nut/upssched-cmd.sh,并且会按不同的事件传递不同的参数给这个脚本,比如低电量的时候传递的参数是 lowbatt
以上注释的两行 #AT 表示在断电用电池供电时,启动一个180s的timer,如果3分钟内电又来了,则会取消这个timer,但是我觉得用处不是很大。
upssched-cmd.sh 的示例内容如下:
#!/bin/bash
logfil=/root/shutdown/log.txt
echo $(date +%Y-%m-%d" "%H:%M:%S) >> $logfil
case $1 in
onbattwarn)
logger -t upssched-cmd 'UPS 已经切换到电池供电, 准备安全关闭系统...'
echo 'UPS 已经切换到电池供电, 准备安全关闭系统...' >> $logfil
;;
ups-back-on-line)
logger -t upssched-cmd '市电已恢复...'
echo '市电已恢复...' >> $logfil
;;
lowbatt)
logger -t upssched-cmd 'UPS 电量不足, 立即关闭系统...'
echo 'UPS 电量不足, 立即关闭系统...' >> $logfil
;;
shutdown)
logger -t upssched-cmd 'UPS 发出关机指令...'
echo 'UPS 发出关机指令...' >> $logfil
;;
replace)
logger -t upssched-cmd 'UPS 需要换电池了...'
echo 'UPS 需要换电池了...' >> $logfil
;;
*)
logger -t upssched-cmd "Unrecognized command: $1"
echo "Unrecognized command: $1" >> $logfil
;;
esac
设置自动启动
上面已经将配置设置好了,但是nut还不会自动启动,设置自动启动的命令很简单
systemctl enable nut-driver nut-server nut-monitor
手动启动
systemctl start nut-driver nut-server nut-monitor
释疑
1. 最开始测试的时候,一开始断电就进入了关机流程,最后发现是ups电还没充够,一断电就低于设置的低电量,就开始跑关机流程了。可以通过 upsc 这个程序来查询ups的信息,当前就包括电量信息了
2. upssched-cmd.sh 监控到的shutdown 和 SHUTDOWNCMD的先后问题
经过测试,SHUTDOWNCMD 指定的程序会是最后调用的程序
3. 附上我的SHUTDOWNCMD 脚本内容
#!/bin/bash
source /etc/profile
echo $(date +%Y-%m-%d" "%H:%M:%S) >> /root/shutdown/log.txt
echo "UPS detect shutdown " >> /root/shutdown/log.txt
echo $(date +%Y-%m-%d" "%H:%M:%S) >> /root/shutdown/log.txt
if [ -e /etc/nut/killpower ]
then
upsdrvctl shutdown
fi
echo "UPS start shutdown directly" >> /root/shutdown/log.txt
running="status: running"
#close 101
qmst=`qm status 101`
if [ "$qmst" = "$running" ]
then
rm -f /run/lock/qemu-server/lock-101.conf
qm unlock 101
qm stop 101
echo "force shutdown 101 now" >> /root/shutdown/log.txt
else
echo "101 not running" >> /root/shutdown/log.txt
fi
#close 100
qmst=`qm status 100`
if [ "$qmst" = "$running" ]
then
rm -f /run/lock/qemu-server/lock-100.conf
qm unlock 100
qm stop 100
echo "force shutdown 100 now" >> /root/shutdown/log.txt
else
echo "100 not running" >> /root/shutdown/log.txt
fi
sleep 19
sync
sync
halt -p