在PVE中部署瓦力UPS服务
## 运行逻辑
市电正常
│
│
apcupsd 每60秒轮询UPS
│
│
检测到市电断电
│
▼
ONBATTERYDELAY 60秒
(防止瞬间跳电误触发)
│
│
▼
apcupsd 触发事件
调用 apccontrol
│
│
▼
apccontrol onbattery
│
│
记录日志 + 广播消息
│
▼
关闭所有 PVE 虚拟机
qm shutdown
│
▼
关闭所有 LXC 容器
pct shutdown
│
▼
等待虚拟机关闭
最长 180 秒
│
▼
VM / 容器全部关闭
│
▼
关闭 PVE 宿主机
shutdown -h now
│
▼
系统关机
│
▼
UPS等待6分钟睡眠断电(UPS端设置)
│
▼
来电
│
▼
主板自动开机
安装 apcupsd 服务
执行
apt update
安装
apt install apcupsd -y
修改 目录 /etc/default/ 下的 apcupsd 文件
nano /etc/default/apcupsd
# Defaults for apcupsd initscript (unused with systemd as init).
# Set to "yes" to enable startup of apcupsd.
ISCONFIGURED=yes
修改 ISCONFIGURED=yes 为 YES
运行命令
systemctl enable apcupsd
systemctl restart apcupsd
修改 /etc/apcupsd/apccontrol 文件
## apcupsd.conf v1.1
# UPS名称(自定义,用于标识当前UPS设备)
UPSNAME PVE-UPS
# UPS连接方式
# usb 表示通过USB线连接UPS
UPSCABLE usb
# UPS类型
# usb 表示USB通信UPS(常见APC UPS)
UPSTYPE usb
# 设备路径
# USB UPS一般留空,串口UPS才需要填写 /dev/ttyS0
DEVICE
### 轮询时间(秒)
# 每隔多少秒检测一次UPS状态
POLLTIME 60
# 锁文件目录
# 防止多个进程同时访问UPS
LOCKFILE /var/lock
# 事件脚本目录
# UPS发生事件(断电、恢复供电等)时调用
SCRIPTDIR /etc/apcupsd
# 电源故障标记目录
PWRFAILDIR /etc/apcupsd
# 系统禁止登录标记目录
NOLOGINDIR /etc
# 断电60秒触发(防止瞬间跳电)
ONBATTERYDELAY 60
# UPS电池低于20%触发关机
BATTERYLEVEL 20
# UPS预计剩余时间低于5分钟触发关机
MINUTES 5
# 强制关机时间(秒)
# 0 表示禁用,仅使用电量和剩余时间判断
TIMEOUT 0
# 断电后每隔300秒发送一次警告
ANNOY 300
# 断电60秒后开始发送警告
ANNOYDELAY 60
# 是否禁止用户登录
# disable = 不禁止登录
NOLOGON disable
# 系统关机后延迟多少秒再关闭UPS
KILLDELAY 0
# 开启UPS网络服务器
# 允许其他设备读取UPS状态
NETSERVER on
# 监听IP地址
# 0.0.0.0 表示监听所有网卡
NISIP 0.0.0.0
# UPS网络服务端口
NISPORT 3551
# UPS事件日志文件
EVENTSFILE /var/log/apcupsd.events
# 最大事件日志大小(MB)
EVENTSFILEMAX 10
# UPS类别
# standalone 表示本机直接连接UPS
UPSCLASS standalone
# UPS模式
# disable 表示单机模式
UPSMODE disable
# UPS状态写入间隔
# 0 表示不周期写入
STATTIME 0
# UPS状态文件
STATFILE /var/log/apcupsd.status
# 是否记录统计信息
LOGSTATS off
# UPS数据更新时间
DATATIME 0
重启UPS服务
systemctl restart apcupsd
systemctl enable apcupsd
查看UPS运行状态
apcaccess

注释
APC : 001,024,0584
# apcupsd 状态协议版本信息
DATE : 2026-03-21 16:14:29 +0800
# 当前读取UPS状态的时间
HOSTNAME : 5825U
# 运行 apcupsd 的主机名
VERSION : 3.14.14 (31 May 2016) debian
# apcupsd 软件版本
UPSNAME : PVE-UPS
# UPS名称(在 apcupsd.conf 中自定义)
CABLE : USB Cable
# UPS连接方式:USB数据线
DRIVER : USB UPS Driver
# 使用的驱动类型:USB UPS驱动
UPSMODE : Stand Alone
# UPS工作模式:独立模式(没有网络共享UPS)
STARTTIME: 2026-03-21 16:14:27 +0800
# apcupsd 服务启动时间
MODEL : Smart UPS W150
# UPS型号
STATUS : ONLINE
# UPS状态:ONLINE 表示市电正常供电
LINEV : 122.5 Volts
# 输入电压(市电电压)
BCHARGE : 100.0 Percent
# 电池当前电量
TIMELEFT : 206.4 Minutes
# 按当前负载估算剩余供电时间(分钟)
MBATTCHG : 20 Percent
# 当电池电量低于20%时触发关机保护
MINTIMEL : 5 Minutes
# UPS剩余时间低于5分钟时触发关机
MAXTIME : 0 Seconds
# 最大运行时间限制(0表示不限制)
OUTPUTV : 121.5 Volts
# UPS输出电压
NUMXFERS : 0
# 从市电切换到电池的次数
TONBATT : 0 Seconds
# 当前在电池模式运行的时间
CUMONBATT: 0 Seconds
# 累计电池运行时间
XOFFBATT : N/A
# 最近一次从电池恢复到市电的时间
STATFLAG : 0x05000008
# UPS状态标志位(系统内部状态代码)
SERIALNO : 8856A600DA50
# UPS序列号
END APC : 2026-03-21 16:14:36 +0800
# 本次状态数据结束时间
修改/etc/apcupsd/apccontrol 脚本
#
# apcupsd 事件处理脚本
# 当UPS发生各种状态变化时,apcupsd会调用此脚本
#
# 程序安装路径
prefix=/usr
exec_prefix=${prefix}
# apcupsd PID 文件
APCPID=/var/run/apcupsd.pid
# apcupsd 主程序
APCUPSD=/sbin/apcupsd
# 系统关机命令
SHUTDOWN=/sbin/shutdown
# 脚本使用的shell
SCRIPTSHELL=/bin/sh
# apcupsd脚本目录
SCRIPTDIR=/etc/apcupsd
# 向所有终端广播消息
WALL=wall
# 系统管理员
export SYSADMIN=root
# 邮件程序(一般未使用)
export APCUPSD_MAIL="mail"
# 如果存在额外配置文件则加载
if [ -f $SCRIPTDIR/config ]; then . $SCRIPTDIR/config ; fi
# =========================================================
# 如果存在自定义事件脚本,则优先执行
# 例如 /etc/apcupsd/onbattery
# =========================================================
if [ -f ${SCRIPTDIR}/${1} -a -x ${SCRIPTDIR}/${1} ]
then
${SCRIPTDIR}/${1} ${2} ${3} ${4}
# 返回99表示脚本已完全处理事件
if [ $? = 99 ] ; then
exit 0
fi
fi
# =========================================================
# 根据UPS事件执行对应操作
# $1 为事件名称
# =========================================================
case "$1" in
# =====================================================
# killpower
# UPS在系统关机后执行断电命令
# =====================================================
killpower)
echo "Apccontrol doing: ${APCUPSD} --killpower on UPS ${2}" | (${WALL} 2>/dev/null || cat)
# 等待10秒确保系统完全关闭
sleep 10
# 通知UPS关闭输出电源
${APCUPSD} --killpower
echo "Apccontrol has done: ${APCUPSD} --killpower on UPS ${2}" | (${WALL} 2>/dev/null || cat)
;;
# =====================================================
# UPS通信中断
# =====================================================
commfailure)
echo "Warning communications lost with UPS ${2}" | ${WALL}
;;
# =====================================================
# UPS通信恢复
# =====================================================
commok)
echo "Communications restored with UPS ${2}" | ${WALL}
;;
# =====================================================
# 检测到市电断电
# =====================================================
powerout)
;;
# =====================================================
# UPS开始使用电池供电(停电)
# =====================================================
onbattery)
# 记录日志
logger "UPS Power Failure - shutting down all VMs"
# 向所有终端广播信息
echo "UPS Power Failure - shutting down all VMs and containers" | ${WALL}
# -------------------------------------------------
# 关闭所有KVM虚拟机
# -------------------------------------------------
for vmid in $(qm list | awk 'NR>1 {print $1}')
do
qm shutdown $vmid
done
# -------------------------------------------------
# 关闭所有LXC容器
# -------------------------------------------------
for ctid in $(pct list | awk 'NR>1 {print $1}')
do
pct shutdown $ctid
done
# -------------------------------------------------
# 等待虚拟机关闭
# 最长等待180秒
# -------------------------------------------------
timeout=180
while [ $timeout -gt 0 ]; do
running_vm=$(qm list | awk 'NR>1 && $3=="running"')
running_ct=$(pct list | awk 'NR>1 && $2=="running"')
# 如果VM和容器全部关闭则退出循环
if [ -z "$running_vm" ] && [ -z "$running_ct" ]; then
break
fi
sleep 5
timeout=$((timeout-5))
done
# -------------------------------------------------
# 最后关闭PVE宿主机
# -------------------------------------------------
logger "All VMs stopped, shutting down PVE host"
${SHUTDOWN} -h now "UPS Power Failure"
;;
# =====================================================
# UPS恢复市电
# =====================================================
offbattery)
echo "Power has returned on UPS ${2}" | ${WALL}
;;
# =====================================================
# 市电恢复
# =====================================================
mainsback)
echo "Mains power restored on UPS ${2}" | ${WALL}
;;
# =====================================================
# UPS电池耗尽
# =====================================================
failing)
echo "Battery power exhausted on UPS ${2}. Doing shutdown." | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# 电池运行时间超过限制
# =====================================================
timeout)
echo "Battery runtime limit exceeded on UPS ${2}. Doing shutdown." | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# 电池电量低
# =====================================================
loadlimit)
echo "Battery charge below limit on UPS ${2}. Doing shutdown." | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# 剩余运行时间低
# =====================================================
runlimit)
echo "Battery runtime below limit on UPS ${2}. Doing shutdown." | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# UPS请求重启
# =====================================================
doreboot)
echo "UPS ${2} initiating Reboot Sequence" | ${WALL}
${SHUTDOWN} -r now
;;
# =====================================================
# UPS请求关机
# =====================================================
doshutdown)
echo "UPS ${2} initiated Shutdown Sequence" | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# UPS提醒用户
# =====================================================
annoyme)
echo "Power problems with UPS ${2}. Please logoff." | ${WALL}
;;
# =====================================================
# 紧急关机
# =====================================================
emergency)
echo "Emergency Shutdown. Possible battery failure on UPS ${2}." | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# UPS电池需要更换
# =====================================================
changeme)
echo "Batteries have failed on UPS ${2}. Replace them immediately." | ${WALL}
;;
# =====================================================
# 远程UPS关机
# =====================================================
remotedown)
echo "Remote Shutdown. Beginning shutdown sequence." | ${WALL}
${SHUTDOWN} -h now
;;
# =====================================================
# 未知事件
# =====================================================
*)
echo "Usage: ${0##*/} command"
echo "This script is intended to be launched by apcupsd."
exit 1
;;
esac`
在PVE中部署瓦力UPS服务
https://haloo.tolan.link:6688//archives/1774079577028