在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
发布于
2026年03月21日
许可协议