Homelab 搭建手记(10)实验室开发主机接入 W150 UPS 与 NUT 配置
创建于 2026-09-09
更新于 2026-09-09
科技
迷你主机
homelab
debian
ups
nut
6572 字 · 约 22 分钟

前言

我目前还在读博,这台开发服务器就放在实验室的桌面上,平时 24 小时开机。把开发环境集中在一台机器上之后,在宿舍、家里或者出差时,都能接着远程工作,不用每换一个地方就重新配置环境。

但实验室的供电并不总是那么省心,尤其是假期,经常遇到跳闸。电一断,远程连接也就没了,我还得想办法到实验室处理。这台联想来酷迷你主机又有一个让我头疼的问题:断电后重新开机会卡死。到现在还没确定是固件、硬件还是系统引导的问题,不能直接归因于 Debian;可以确定的是,我不能指望它每次恢复供电都能自己回到可访问状态。

所以这次给主机加了一台瓦力方程 W150 UPS。短时间停电时尽量维持运行,长时间停电时则希望给系统留出正常关机的机会。下面记录选购考虑、USB 状态读取、NUT 配置,以及接入过程中遇到的几个问题。

一、从迷你主机的取舍说起

Homelab 搭建手记(1)迷你主机选购与 Debian 系统安装 已经记录了当时的配件涨价背景和最终购入配置:联想来酷 Mini Pro,R7 8745H、24G 内存、512G 硬盘,购入价格为 2499 元。这里补一点第一篇没有展开的取舍。

最初选机器时,我也考虑过用手头的 DDR4 内存搭配旧平台准系统,尽量利用已有配件。后来选择这台成品机,很大程度上是受当时内存和整机价格影响,并不是觉得板载内存更适合开发服务器。这台机器的内存扩展空间有限,后面容器、开发服务和模型实验多起来,很难再靠加内存解决。若不是预算和配件价格的限制,我会更倾向于能更换、扩充内存的平台。

这些是当时购买时的取舍,不是现在的硬件购买推荐。继续使用已有机器后,供电和恢复能力也成了需要补上的部分。UPS 可以减少突然失电的机会,但不能修复主机断电后启动卡死的问题。

二、为什么选 W150,没有买 W180

UPS 也要放在实验室桌面上,我更看重体积、适合长期放置的电池方案,以及能否接入 Linux 监控。这次只考虑保护这台迷你主机,没有打算把整张桌面的设备都接进去,也没有为了更大的标称功率上 W180。

厂商的 W150 与 W180 对比页 列出,W150 使用磷酸铁锂(LiFePO4),W180 使用三元锂。这与 W150 在本次 USB 读取中上报的 battery.type: LiFePO4 一致。考虑到放在实验室桌面、长期接电,我更偏向磷酸铁锂的热稳定性,因此选择了 W150。这是我的选购权衡,并不代表三元锂产品不能使用,整机安全也不能只看电芯化学体系。

W150 属于面向直流设备的 UPS。实际接入前,要按机器和 UPS 的说明核对输入输出电压、接口极性、线材以及负载功率,不能只凭接口插得上、型号里带有某个数字就判断合适。功率决定能否带动负载,电池能量和实际负载才共同影响续航。

供电和监控是两条连接:

text
1
2
供电:市电 → 匹配的电源适配器 → W150 → 迷你主机 监控:W150 → USB 数据线 → Debian 上的 NUT

USB 让系统知道 UPS 当前的状态,并不代替主机供电。UPS 也只保护接在其输出上的设备;上游网络设备如果同时失电,即使主机还在运行,也未必能继续远程访问。

三、让 Debian 先读到 UPS

3.1 安装与识别

本次记录的环境是 Debian 13,NUT 版本为 2.8.1。先安装所需组件:

bash
1
2
3
4
5
6
sudo apt update sudo apt install nut-client nut-server usbutils lsusb upsc -V sudo nut-scanner -U

当时的 USB 识别结果为:

text
1
ID 04d8:d005 Microchip Technology, Inc. Smart UPS W150

扫描结果推荐 usbhid-ups 驱动,随后实际读取也确认使用该驱动。不要只根据“UPS”这个设备类别就预先套驱动;同一个品牌的不同设备,也应以本机扫描和读取结果为准。usbhid-ups 文档

后续会修改 /etc/nut/ 中的配置。已有配置时,先备份:

bash
1
sudo cp -a /etc/nut "/etc/nut.backup-$(date +%Y%m%d-%H%M%S)"

下面列出关键有效项;编辑发行版已有配置时,修改对应项并保留相关默认设置,避免追加出重复定义。整个配置过程应在市电正常、可以处理主机状态的时段进行。

3.2 单机模式与驱动配置

这次由 UPS 通过 USB 直接连接主机,NUT 的服务端和监控端都运行在本机。/etc/nut/nut.conf 设置为:

conf
1
MODE=standalone

/etc/nut/ups.conf 中加入设备:

conf
1
2
3
4
5
6
[w150] driver = usbhid-ups port = auto vendorid = 04d8 productid = d005 desc = "WalleCube W150"

w150 是本机配置中的逻辑名称,后面的查询、监控和服务实例都使用这个名称。只有一台对应设备时,这组标识已经能定位本次 UPS;多台同型号设备需要进一步区分,不能照搬为同一个实例。

/etc/nut/upsd.conf 只保留本机监听:

conf
1
2
LISTEN 127.0.0.1 3493 LISTEN ::1 3493

无需为了本机监控把 NUT 端口开放给整个网络。随后启动驱动枚举和信息服务:

bash
1
2
3
4
5
6
7
sudo systemctl restart nut-driver-enumerator.service sudo systemctl enable --now nut-server.service sudo systemctl restart nut-server.service systemctl status nut-driver@w150.service --no-pager systemctl status nut-server.service --no-pager upsc w150@localhost

这里使用 Debian 13 的 systemd 驱动实例管理。其他版本若没有对应单元,应先检查发行版提供的服务方式,不要同时用手工驱动进程和 systemd 管理同一台 USB 设备。

3.3 当时遇到的两个错误

第一个是前台调试驱动时出现 USB Resource busy。检查后发现,nut-driver@w150.service 已经在运行,再启动一个 usbhid-ups 进程就会争用设备。因此,驱动服务正常时直接查询数据即可,无需再开第二个实例。

第二个是驱动已经运行,upsc 却返回 Connection refusedupsc 连接的是 upsd,不是直接读取 USB;启动 nut-server 后才打通了这段链路:

text
1
W150 → usbhid-ups → upsd → upsc

这两个问题分别发生在 USB 驱动层和本机服务层,检查顺序也应沿着这条链路进行。

四、实际读到了哪些状态

市电正常时,日志中的部分字段如下,设备序列号已省略:

text
1
2
3
4
5
6
7
8
battery.charge: 100 battery.type: LiFePO4 battery.runtime: 17354 device.model: Smart UPS W150 driver.name: usbhid-ups driver.version: 2.8.1 driver.version.data: openUPS HID 0.5 ups.status: OL

在当时的断电识别检查中,又读到了:

text
1
2
input.current: 0.000 ups.status: OB DISCHRG

OL 表示市电在线,OB 表示由电池供电,DISCHRG 表示正在放电。这次状态变化已经观察到,所以可以确认设备能够向 NUT 报告掉电。

不过 battery.runtime 是设备上报的估计值,不是我测出的续航。两次采样中它还出现了明显变化,不适合直接用来承诺运行时间。日志里的电压字段也有变化,读数的含义、不同供电状态下的输出和主机电压要求,都要结合设备说明核对;这里只把它们作为遥测,不能当作供电兼容性认证。

五、配置监控与半小时延迟策略

5.1 先接通 upsmon

upsmon 负责监视供电条件,并在达到关机条件时启动系统关机流程。先生成单独的监控密码:

bash
1
openssl rand -base64 24

将生成的值填入 /etc/nut/upsd.users

conf
1
2
3
[upsmon] password = your_random_password upsmon primary

再修改 /etc/nut/upsmon.conf,两处密码保持一致:

conf
1
2
3
4
5
6
7
8
9
MONITOR w150@localhost 1 upsmon your_random_password primary MINSUPPLIES 1 SHUTDOWNCMD "/sbin/shutdown -h +0" POWERDOWNFLAG /etc/killpower NOTIFYCMD /usr/sbin/upssched NOTIFYFLAG ONLINE SYSLOG+EXEC NOTIFYFLAG ONBATT SYSLOG+EXEC NOTIFYFLAG LOWBATT SYSLOG+EXEC

这是单 UPS、单主机的配置。primary 让本机承担该 UPS 的主要监控角色,MINSUPPLIES 1 与唯一的供电来源相对应。配置文件包含监控密码,应限制读取权限:

bash
1
2
sudo chown root:nut /etc/nut/upsd.users /etc/nut/upsmon.conf sudo chmod 640 /etc/nut/upsd.users /etc/nut/upsmon.conf

NOTIFYCMD 的路径需要按安装包核对。当时普通用户的 command -v upssched 没有输出,通过包文件列表才确认实际位置:

bash
1
dpkg -L nut-client nut-server | grep '/upssched$'

结果为 /usr/sbin/upssched。PATH 中找不到程序,不等于程序没有安装。

5.2 为什么增加计时器

我当时希望断电后留出半小时,给短时跳闸恢复供电留一点余量;超过这个时间,就进入关机流程。这个时间按自己的使用需求设置,实际部署时应结合负载和可用续航调整,给服务退出和文件系统写回留出余量。

计时策略为:

事件 处理
转为电池供电 ONBATT 启动 1800 秒计时器
计时结束前恢复市电 ONLINE 取消尚未触发的计时器
计时到期 请求 NUT 执行 FSD 关机流程
电池供电期间报告低电量 LOWBATT 提前进入关机处理,不等待半小时

低电量本身也是 upsmon 的关键供电判断之一;下面的 LOWBATT 回调保留当时的配置,不能理解成“没有这个脚本,NUT 就不会处理低电量”。upsmon 配置说明

5.3 upssched 配置

/etc/nut/upssched.conf 的关键内容为:

conf
1
2
3
4
5
6
7
CMDSCRIPT /usr/bin/upssched-cmd PIPEFN /run/nut/upssched/upssched.pipe LOCKFN /run/nut/upssched/upssched.lock AT ONBATT w150@localhost START-TIMER onbatt_shutdown 1800 AT ONLINE w150@localhost CANCEL-TIMER onbatt_shutdown AT LOWBATT w150@localhost EXECUTE lowbatt_shutdown

NOTIFYFLAG 中的 EXEC 不能漏掉,否则事件不会调用 NOTIFYCMDSTART-TIMER 到期后把事件名传给处理脚本,CANCEL-TIMER 用于撤销仍在等待的计时器。upssched 配置说明

Debian 当时已经提供了专用运行目录:

text
1
2
3
/run/nut/upssched/ owner: nut:nut mode: 0770

因此沿用这个目录存放管道和锁文件,没有把它们放进任何用户都能写的公共目录。/run 是运行期目录,重启后也需要由发行版的 tmpfiles/服务机制重建。

5.4 处理脚本与关机权限

当时修改的是 Debian 提供的 /usr/bin/upssched-cmd,其原始内容只有示例事件。下面保留本次使用的核心逻辑:

sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/sh case "$1" in onbatt_shutdown) logger -t upssched-cmd \ "UPS has been on battery for 30 minutes, requesting FSD" /lib/nut/upsmon -c fsd ;; lowbatt_shutdown) logger -t upssched-cmd \ "UPS battery is low, requesting FSD" /lib/nut/upsmon -c fsd ;; *) logger -t upssched-cmd "Unrecognized command: $1" ;; esac

脚本使用实际安装的 /lib/nut/upsmon 路径,请先通过包文件列表核对。编辑已有脚本前也要备份;本次脚本设置为 root:root0755,使监控用户可执行但不可修改:

bash
1
2
3
4
5
sudo cp -p /usr/bin/upssched-cmd "/usr/bin/upssched-cmd.backup-$(date +%Y%m%d-%H%M%S)" # 编辑完成后检查脚本语法和权限 sh -n /usr/bin/upssched-cmd sudo chown root:root /usr/bin/upssched-cmd sudo chmod 755 /usr/bin/upssched-cmd

这是当时直接修改发行版脚本的记录。若重新整理部署,我会把自定义脚本放在 /etc/nut/upssched-cmd,同时修改 CMDSCRIPT,避免软件包升级覆盖自己的逻辑。

upsmon -c fsd 会请求真正的关机流程,不是无害的连通性测试。通知脚本通常在非特权监控用户下运行,最终的 SHUTDOWNCMD 才由有权限的部分执行。部署时要确保监控用户可执行处理脚本,并具备向运行中的 upsmon 发出控制请求所需的权限。upsmon 手册

5.5 启动监控

确认配置后启动监控:

bash
1
2
3
4
5
6
sudo systemctl restart nut-server.service sudo systemctl enable --now nut-monitor.service sudo systemctl restart nut-monitor.service systemctl status nut-monitor.service --no-pager sudo journalctl -u nut-monitor -n 50 --no-pager

当时的服务状态为 active (running),日志也列出了 w150@localhost (primary)。监控进程至此已经加载目标 UPS,供电状态和事件处理也有了统一的日志入口。

六、计时与恢复供电的处理

这套配置通过 ONBATT 启动计时、ONLINE 取消计时,将短时停电和持续停电分开处理。ONLINE 只能取消仍在等待的计时器;一旦进入 FSD 关机流程,来电事件不会撤回关机。计时依赖正在运行的调度进程,也不应当作跨重启保存的停电时钟。upssched 手册

UPS 缓冲供电与主机恢复开机也有不同的条件。主机正常关机后,如果 UPS 一直维持输出,市电恢复时主板未必经历一次新的供电恢复事件。因此,BIOS 中的恢复供电自动启动选项,需要结合 UPS 的输出行为理解。

本次采用的是由 NUT 监视供电状态、按时间或低电量条件请求系统关机的配置。对于放在实验室长期运行的开发主机,这让突然失电之前多了一段缓冲时间,也把供电状态纳入了 Debian 的服务管理和日志中。

参考

手机扫码阅读
本文作者: 有次元袋的 tiger
本文链接: https://www.superheaoz.top/2026/09/31179/
版权声明: 本站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 我的个人天地