1. NUT 是什么?
NUT 全称是 Network UPS Tools,即 网络UPS工具,它的作用是提供服务来让网络中的计算机了解当前 UPS 的状态,并在 UPS 电池耗尽之前执行关机以确保数据的安全。
值得注意的是,不是所有的UPS都支持这个功能。在购入 UPS 前需要查看它支不支持与计算机通讯,且有完备的驱动。
2. NUT 中重要的概念(软件的分层)
- drivers - 和硬件交互:
upsdrvctl + ups.conf - server - 提供硬件状态的接口:
upsd + upsd.conf/upsd.users - clients - 查询server的能力:
upsc - cgi-bin - 基于web 的客户端(需要自己编译,并加上
--with-cgi的flag) - scripts - 特定情况下触发的脚本:
upsmon + upsmon.conf/upssched.conf
其他的软件:
- upslog: 查看监听日志
- upscmd: 运行UPS内置命令(需要UPS支持,使用
upscmd -l来查看; 需要upsd.users 授权用户才能执行)
3. NUT的运行模式
我这里采用了 “Advanced” 模式,如下

4. NUT 配置步骤
4.0. 安装 NUT 工具
sudo apt install nut
4.1. 配置 NUTdriver
扫描
nut-scanner -q [nutdev1] driver = "usbhid-ups" port = "auto" ...复制上述内容至
/etc/nut/ups.conf,或者直接sudo nut-scanner -q >> /etc/nut/ups.conf建议把UPS名字换了,换一个好记一点的,我就换成了
APC,正是我买的这个UPS的品牌[APC] driver = "usbhid-ups" port = "auto" ...开启 nut-driver
sudo systemctl start nut-driver # 或者 sudo upsdrvctl start APC # 这里是配置文件中的UPS名称
4.2. 配置 upsd 并启动
修改配置
可以选择配置
etc/nut/upsd.conf但我全留了默认值,因此它只监听localhost:3493配置 upsd 用户
/etc/nut/upsd.users(在后面用得到)[upsmon] password = "xxxx" upsmon master启动uspd
sudo upsd
4.3. 查看UPS状态
查询所有状态
upsc APC@locahostups.status
upsc APC@locahost ups.status # OLOL: line power(使用外部供电)
OB: on battery (使用电池供电)
LB: low battery (电量低)
4.4. 配置NUT自启动
修改 /etc/nut/nut.conf
MODE=netserver
特别老的版本好像是没有此配置的,需要手动配置.serivce文件
4.5. 设置自动关机 和 低电量事件
5.1 NUT Flow(当停电以后发生了什么?)
UPS 断电并使用电池供电;
UPS 电池电量即将耗尽(此时
ups.status: OB LB);master
upsmon监听到这一事件,并且设置FSD(forced shutdown)的标识来告诉所有的 slave 准备关机;slave 收到
FSB信号,它会:- 触发
NOTIFY_SHUTDOWN事件 - 等待
FINALDELAY秒(一般来说5秒) - 执行
SHUTDOWNCMD脚本 - 断开与
upsd的链接
- 触发
master
upsmon最多等待HOSTSYNC秒(通常是15秒),让辅助系统断开与 upsd 的链接。如果在这段时间后仍有任何连接,upsmon就会停止等待并继续后续操作。master 收到
FSB信号,它会:- 触发
NOTIFY_SHUTDOWN事件 - 等待
FINALDELAY秒(一般来说5秒) - 创建名为
POWERDOWNFLAG的文件(通常叫/etc/killpower) - 执行
SHUTDOWNCMD脚本
- 触发
init准备回收系统资源,关闭进程、卸载挂载等;init执行关机的脚本。此时会去检查POWERDOWNFLAG文件,并告诉 UPS 关闭供电;整个系统的电力供给完全停止;
随着时间推移,电力恢复,UPS 重新开启;
所有系统重启;
5.2 配置关机策略
先创建 upsd 用户(刚才创建过了)
配置
/etc/nut/upsmon.conf- 添加断电标志文件
POWERDOWNFLAG /etc/killpower - 添加对upsd的监控
MONITOR APC@127.0.0.1 1 upsmon xxx master- 这里的 1 指的是 “power value”,应该一直被设置为 1,除非你正在管理一个非常昂贵、拥有冗余供电的系统
- 定义关机脚本
SHUTDOWNCMD "/usr/sbin/shutdown -h +0" - 开启
upsmon服务:upsmon或者systemctl start nut-monitor
- 添加断电标志文件
5.3 高级应用
原理 设置
NOTIFYFLAG $type $action来让upsmon监听特定的事件,然后执行特定的动作。 其中 已知的 type 为ONLINE,ONBATT,LOWBATT,FSD,COMMOK,COMMBAD,SHUTDOWN,REPLBATT,NOCOMM,NOPARENT, 已知的 Action 为WALLSYSLOGEXECIGNORE关于 action
WALL: 给所有已登录的用户发送内容为NOTIFYMSG的消息SYSLOG:记录日志EXEC:执行NOTIFYCMD中配置的脚本。同时,他会设置UPSNAME和NOTIFYTYPE两个环境变量IGNORE:啥也不干
为了获取更加高级的功能,我们可以将
NOTIFYCMD设置为/usr/sbin/upssched,这样我们需要配置upssched.conf因此,
upsmon.conf中的配置可以为NOTIFYCMD /usr/sbin/upssched NOTIFYFLAG ONLINE SYSLOG+EXEC NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC NOTIFYFLAG LOWBATT SYSLOG+WALL+EXECupssched: 针对某个NOTIFYFLAG执行脚本、开启定时任务,我们至少要配置:CMDSCRIPT: 定时器所要触发的脚本,最好是case ... esac的结构的脚本,所有的定时器都只会执行这一个脚本;PIPEFN: 这个文件将在进程之间传递通信,以启动和停止定时器;LOCKFN: 这个文件被当做锁,防止定时器在某些状态下由于并发而产生的竞争。AT定时器:AT ONBATT * START-TIMER onbattwarn 30监听所有 ups(*) 的ONBATT事件,如果有事件发生,设定一个每 30 秒执行一次的,名为onbattwarn的定时器AT ONLINE * CANCEL-TIMER onbattwarn监听所有 ups(*) 的ONLINE事件,如果有事件发生,取消一个名为onbattwarn的计时器AT ONLINE * EXECUTE ups-back-on-power监听所有 ups(*) 的ONLINE事件,立即执行CMDSCRIPT,并将ups-back-on-power以参数的形式传递
注意:timer 在执行的时候会讲自己的名字作为参数传给脚本
- 一个
CMDSCRIPT的示例:#! /bin/sh case $1 in onbattwarn) # Send a notification mail echo "The UPS has been on battery for awhile" \ | mail -s"UPS monitor" bofh@pager.example.com # Create a flag-file on the filesystem, for your own processing /usr/bin/touch /some/path/ups-on-battery ;; ups-back-on-power) # Delete the flag-file on the filesystem /bin/rm -f /some/path/ups-on-battery ;; *) logger -t upssched-cmd "Unrecognized command: $1" ;; esac
5.4 我的配置
先来看下我的需求:
- 当 UPS 启动电池供电时,记录日志并且发送 pushdeer 的通知
- 当 UPS 恢复供电时,发送 pushdeer 的通知
- 当 UPS 的电池电量少于 50% 时,发送 pushdeer 的通知 (TODO)
- 当
NOTIFY_SHUTDOWN事件被触发,准备关机时,发送 pushdeer 的通知。(master和slave都要配置)(TODO)
为了满足上述需求,我们可以对下述配置文件行进修改
upsmon.conf
NOTIFYCMD /usr/sbin/upssched
NOTIFYFLAG ONLINE SYSLOG+EXEC
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
upssched.conf
CMDSCRIPT /usr/local/bin/upssched-script.sh
PIPEFN /run/nut/upssched/upssched.pipe
LOCKFN /run/nut/upssched/upssched.lock
AT ONBATT * EXECUTE battery_on # 发送断电的消息
AT ONLINE * EXECUTE power_online # 发送来电的消息
AT ONBATT * START-TIMER watch_battery 60 # 每 60 秒就监控电池状态
AT ONLINE * CANCEL-TIMER watch_battery # 取消 监控电池状态
/usr/local/bin/upssched-script.sh
#! /bin/bash
UPSNAME=${UPSNAME}
NOTIFYTYPE=${NOTIFYTYPE}
PUSH_DEER_HOST=${PUSH_DEER_HOST:-"https://api2.pushdeer.com"}
PUSH_DEER_KEY=${PUSH_DEER_KEY:-"PDU524TTKZWck93IFhVd8jMAaEqvZ8VBqV3HVCa"}
function send_push_deer(){
message=$1
curl -X GET -G --data-urlencode "text=${message}" $PUSH_DEER_HOST"/message/push?pushkey=${PUSH_DEER_KEY}"
}
case $1 in
battery_on)
send_push_deer "UPS 电池已启用,请密切关注电池状态"
;;
power_online)
send_push_deer "UPS 恢复供电,目前电量: `upsc apc@192.168.31.50 battery.charge`"
;;
*)
logger -t upssched-cmd "Unrecognized command: $1"
;;
esac
经测试配置文件生效,能正常触发pushdeer
需要注意
PIPEFN LOCKFN 这俩文件必须要设置运行者的权限,一般是 nut 用户,可以在
upsmon.conf中配置需要 CMDSCRIPT 有可执行的权限,并且需要指定 shebang
#!