UPS/NUT (網絡 UPS 工具)#
NUT#
NUT (網絡 UPS 工具),集成了一系列方便操作、通知的 UPS 工具。
Nut 主要包含三個核心服務:
- nut-driver: 這個服務負責通過特定驅動來與 UPS 進行通信
- nut-server: 該服務利用 nut-driver 溝通 UPS,並將 UPS 狀態通過網絡服務發布
- nut-monitor (nut-client): 該服務連接 nut-server,根據 UPS 狀態做出特定響應
注意: nut-client 實際上是一個 systemd 軟連接文件,本質上還是 nut-monitor。
┌─────────────┐ ┌────────────┐
┌──── │ nut-monitor │ ───────────────► │ nut-server │
│ └─────────────┘ └────────────┘
│
│ │
│ │
▼ ▼
┌─────────────┐ ┌────────────┐
│ upssched │ │ nut-driver │
└─────────────┘ └────────────┘
│ │
│ │
│ │
▼ ▼
┌────────────────┐ ┌─────────────┐
│ user scripts │ │ UPS │
└────────────────┘ └─────────────┘
安裝 NUT#
pve 控制台直接安裝
apt install -y nut
連接 UPS,通過命令:
# nut-scanner -U
SNMP library not found. SNMP search disabled.
Neon library not found. XML search disabled.
IPMI library not found. IPMI search disabled.
Scanning USB bus.
[nutdev1]
driver = "blazer_usb"
port = "auto"
vendorid = "0665"
productid = "5161"
product = "USB to Serial"
vendor = "INNO TECH"
bus = "001"
or
/bin/upsc nutdev1@localhost
Init SSL without certificate database
battery.charge: 100
battery.voltage: 13.50
battery.voltage.high: 13.00
battery.voltage.low: 10.40
battery.voltage.nominal: 12.0
...
查詢 UPS 的狀態。
NUT Server 配置#
在 Nut 安裝完成後會自動在 /etc/nut 目錄生成配置文件,接下來的配置工作主要是調整該目錄下的各種配置文件。
nut.conf#
該配置主要定義 nut 的運行模式,只有一個配置字段 MODE=xxxx,該配置可選值及含義如下:
- none: Nut 未配置或使用外部系統啟動 Nut 服務,可以理解為 "啥也不幹"。
- standalone: 獨立模式,一般在只有一個 UPS 且只負責本地系統 (不提供網絡服務) 的情況下使用。
- netserver: 跟獨立模式類似,會啟動 driver、upsd 和 upsmon 服務,不同之處是可以提供網絡服務,其他機器上的 nut-monitor 可以通過網絡來連接 Nut Server。
- netclient: 僅客戶端模式,只啟動 nut-monitor,用於連接遠程的 Nut 服務。
這裡我使用的 SANTAK TG-BOX 850 通過 USB 連接我的 pve,pve 安裝了 NUT 工具,所以 pve 當作 server,其他的查詢 server 的狀態就行。
# cat /etc/nut/nut.conf
MODE=netserver
ups.conf#
該配置用於定義 nut-driver 如何連接到物理 UPS,該配置文件的格式如下:
# 需要注意的是,如果想要群暉或者 QNAP 能通過網絡連接 UPS,
# 那麼 UPS 的名稱是有特殊要求的(群暉必須起名叫 ups,QNAP 沒有測試過可能叫 qnapups);
[ups]
driver = usbhid-ups
port = auto
desc = "TG-BOX 850"
upsd.conf#
該配置文件用於控制 nut-server 的網絡服務,例如監聽端口、最大連接數、證書配置等;由於我是在內網使用,所以只需要配置網絡監聽,其他參數保持默認即可:
LISTEN 0.0.0.0 3493
upsd.users#
upsd.users 配置文件用於定義通過網絡連接到 nut-server 的用戶名和密碼,該配置樣例如下:
[monuser]
password = secret
upsmon master
關於 群暉 和 QNAP 用戶:如果期望這兩個 NAS 可以直接連接到 Nut Server,可以確認的是群暉需要保證存在用戶名為 monuser 密碼為 secret 的用戶;QNAP 我沒有驗證過,網上查詢到的結果是需要保證存在用戶名為 admin 密碼為 123456 的用戶。
NUT 監控配置#
相較於基本配置來說,Nut 核心處理在於如何做好監控配置;Nut 對於監控策略支持大致有兩種:
- 1、直接由
upsmon.conf
配置,任何事件直接由用戶指定的腳本負責處理,沒有定時器等高級特性 - 2、在
upsmon.conf
中配置執行腳本為upssched
,任何事件先由upssched
處理,借助於upssched
可以實現一些高級功能,比如選擇性觸發關機等
upsmon.conf#
該配置主要用於配置 nut-monitor 如何監控 UPS,同時定義 UPS 出現哪些事件要進行怎樣的處理動作,下面詳細解釋一下核心配置。
MONITOR 指令#
MONITOR 指令用於定義要監控 UPS 的連接地址, 其格式如下:
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 ups@localhost 1 monuser secret master
SHUTDOWNCMD#
SHUTDOWNCMD 指令用於定義在 UPS 電量不足或者需要主動關機時的關機命令,建議填寫完整路徑。
SHUTDOWNCMD "/usr/sbin/poweroff"
NOTIFYCMD#
NOTIFYCMD
指令是一個非常重要的指令,該指令用於配置在發生特定事件 (如市電中斷、UPS 處於低電量等) 時, nut-monitor
所執行的命令。 簡單的說就是 NOTIFYCMD
定義了具體執行命令,可以直接在此處配置一個自己編寫的腳本,當 UPS 有事件發生時此腳本都會被調用。
NOTIFYCMD
指令大致有兩種配置方式,一種是配置成自己的腳本,腳本需要有可執行權限,腳本內可以通過 NOTIFY
環境變量獲取事件類型,然後自己進行處理。 這種方式有點 "簡單粗暴" 的意思,可定制化程度完全依賴於你的腳本怎麼寫。具體可以使用哪些變量請自行測試,因為版本不同可能環境變量名稱也不同。
還有一種方式是將 NOTIFYCMD
配置為執行內置的 upssched
命令; upssched
是 Nut 提供的一個帶有特定策略的調度程序;簡而言之就是基於常用的功能進行了抽象, upssched
有自己單獨配置,可以實現 "如果市電在 180s 內恢復則不進行關機" 的這種高級調度策略。
這裡我選擇使用第二種方式,
NOTIFYCMD /usr/sbin/upssched
NOTIFYFLAG#
NOTIFYFLAG
同樣是關鍵配置,需要與 NOTIFYCMD
配合使用; NOTIFYFLAG
指令負責指定一系列的 UPS 事件應該觸發何種操作,該指令格式如下:
NOTIFYFLAG <notify type> <flag>[+<flag>][+<flag>] ...
其中 表示事件類型,可選類型如下:
- 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: 啥也不幹,忽略該事件
如果 NOTIFYCMD 使用了自定義腳本,則此處請根據實際需要來配置需要腳本處理的事件;如果 NOTIFYCMD 配置為使用 upssched,可以將所有事件配置為 EXEC,然後具體過濾在 upssched 處理。
例如如果只想讓 NOTIFYCMD
配置的腳本只處理市電中斷和恢復事件,同時打印 syslog,可以這樣配置:
NOTIFYFLAG ONLINE SYSLOG+EXEC
NOTIFYFLAG ONBATT SYSLOG+EXEC
由於我 NOTIFYCMD
配置的是 upssched
,所以我這裡將所有事件全部傳遞給 upssched
:
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
其他配置#
除了以上的核心配置,其他配置如果沒特殊情況一般保持默認即可;具體的配置可以參考官方文檔: UPSMON.CONF(5)。
upssched.conf#
使用該配置的前提是 upsmon.conf 配置中的 NOTIFYCMD 指向了 upssched,並且 NOTIFYFLAG 為相關事件配置了 EXEC; 該配置主要的作用是使用一些 upssched 內置的高級語法來控制特定事件的處理方式。 upssched.conf 配置主要包含兩部分:頭部的選項配置和尾部的規則配置。
4.2.1、選項配置#
頭部選項配置只包含三個配置:
- CMDSCRIPT: 該配置應該位於首行 (推薦這樣,實際上是 AT 指令之前就行),該指令用於定義事件的處理腳本;腳本一般由用戶自行編寫,
upssched
會根據規則將指定參數傳遞到此腳本並執行。 - PIPEFN: 用於進程間通信的管道文件,需要位於 AT 指令之前
- LOCKFN: 互斥鎖文件,用於防止 upsmon 同時調度多個文件,需要位於 AT 指令之前
關於 CMDSCRIPT 配置的腳本,下面是一個樣例:
#!/usr/bin/env bash
set -ex
exec >> /var/log/upssched-cmd.log 2>&1
# 主要負責關閉系統的函數
function xpoweroff(){
logger -t upssched-cmd '準備關閉 XPEnology...'
logger -t upssched-cmd '準備關閉 TProxy Gateway...'
logger -t upssched-cmd '所有必要系統已成功關閉...'
}
# 判斷 upssched 觸發的事件
case $1 in
onbattwarn)
logger -t upssched-cmd 'UPS 已經切換到電池供電,準備安全關閉系統...'
xpoweroff
;;
ups-back-on-line)
logger -t upssched-cmd '市電已恢復...'
;;
lowbatt)
logger -t upssched-cmd 'UPS 電量不足,立即關閉系統...'
xpoweroff
;;
*)
logger -t upssched-cmd "Unrecognized command: $1"
;;
esac
有一點需要注意,CMDSCRIPT 配置的腳本默認是以 nut 用戶運行的,所以要處理好 nut 用戶權限問題。
對於 PIPEFN 和 LOCKFN 官方推薦單獨創建叫 upssched 目錄,然後文件放在這個目錄裡; 但是有些系統例如 Ubuntu Server 22.04,/run/nut/ 是 tmpfs,重啟會丟失目錄導致出現權限問題;所以推薦直接不創建獨立目錄直接配置:
CMDSCRIPT /opt/scripts/upssched-cmd.sh
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
4.2.2、規則配置#
使用 upssched 的好處就是內置了一個規則引擎,我們可以通過一些簡單的語法來配置複雜規則; upssched 的規則語法如下:
AT notifytype upsname command
規則以 AT 開頭,notifytype 用於指定需要關注的事件類型; upsname 指定 UPS 名稱,只有一個的情況下或者不想區分時可以無腦寫 *; command 部分用於指定需要執行的動作,command 大致有三種類型:
- START-TIMER: 啟動一個定時器
- CANCEL-TIMER: 取消一個定時器
- EXECUTE: 立即執行
這部分看起來複雜實際很簡單,例如以下規則實現了: 當市電斷開 (UPS 使用電池供電時) 啟動一個名字叫 onbattwarn
的定時器,這個定時器在 180s 後會執行 CMDSCRIPT
定義的腳本並將 onbattwarn
作為第一個參數傳遞給腳本;同時如果市電在計時器的 180s 之內恢復 (臨時閃斷),則取消執行腳本。
AT ONBATT * START-TIMER onbattwarn 180
AT ONLINE * CANCEL-TIMER onbattwarn
當然也有些事件是需要立即執行的,例如: 市電中斷立即發送通知
# 立即執行 `CMDSCRIPT` 指定的腳本,並將 `onbattnoti` 作為參數傳遞給腳本
AT ONBATT * EXECUTE onbattnoti
NUT Server 配置文件#
完整配置為:
nut.conf
MODE=netserver
ups.conf
# 名稱是為了一開始兼容群暉,所以就叫 UPS
[ups]
driver = "usbhid-ups"
port = "auto"
upsd.conf
LISTEN 0.0.0.0 3493
upsd.users
[monuser]
password = secret
upsmon master
[qnapups]
password = 123456
upsmon master
upsmon.conf
MONITOR ups@localhost 1 monuser secret master
MINSUPPLIES 1
SHUTDOWNCMD "/usr/sbin/poweroff"
NOTIFYCMD /usr/sbin/upssched
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 30
DEADTIME 15
POWERDOWNFLAG /etc/killpower
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
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5
upssched.conf
CMDSCRIPT /opt/scripts/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
upssched-cmd.sh
#!/usr/bin/env bash
set -ex
exec >> /var/log/upssched-cmd.log 2>&1
echo "執行 ID: $(id)"
SSH_CMD='ssh -o StrictHostKeyChecking=no'
case $1 in
onbattwarn)
logger -t upssched-cmd 'UPS 已經切換到電池供電,準備安全關閉系統...'
;;
ups-back-on-line)
logger -t upssched-cmd '市電已恢復...'
;;
lowbatt)
logger -t upssched-cmd 'UPS 電量不足,立即關閉系統...'
;;
*)
logger -t upssched-cmd "Unrecognized command: $1"
;;
esac
重啟 NUT Server#
systemctl restart nut-server.service
systemctl restart nut-driver.service
systemctl restart nut-monitor.service
NUT Client 配置#
配置 nut 客戶端#
編輯 /etc/nut/nut.conf 文件,將 MODE 設置為 netclient 模式
MODE=netclient
配置 upsmon#
編輯 /etc/nut/upsmon.conf 文件,添加以下配置項:
MONITOR ups的名稱@ups的ip地址 1 用戶名 密碼 slave
MINSUPPLIES 1
SHUTDOWNCMD "/usr/sbin/poweroff"
NOTIFYCMD /usr/sbin/upssched
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 30
DEADTIME 15
POWERDOWNFLAG /etc/killpower
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
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5
RUN_AS_USER root
這裡注意 RUN_AS_USER,在 upsmon.conf 文件中,通常會定義 upsmon 運行的用戶。在某些系統中,upsmon 默認以普通用戶運行,這可能導致無法執行關機命令,所以需要 root。
配置 upssched#
wall 命令在系統中用於向所有已登錄用戶廣播消息。upsmon 默認配置會使用 wall 命令來發送通知消息給所有用戶。
這裡需要安裝 wall 工具,Gentoo 安裝過程為:
echo "sys-apps/util-linux tty-helpers" >> /etc/portage/package.use
sudo emerge -v sys-apps/util-linux
# 驗證是否安裝
which wall
upssched 工具允許你在特定條件下執行腳本。編輯 /etc/nut/upssched.conf 文件,並添加以下內容:
CMDSCRIPT /etc/nut/upssched-cmd.sh
PIPEFN /run/nut/upssched.pipe
LOCKFN /run/nut/upssched.lock
AT ONBATT * START-TIMER onbattwarn 600
AT ONLINE * CANCEL-TIMER onbattwarn
AT ONLINE * EXECUTE ups-back-on-line
AT LOWBATT * EXECUTE lowbatt
這將會在 ups 供電後 600 秒後發送 onbattwarn 事件到腳本,然後在腳本定義執行的命令。
/etc/nut/upssched-cmd.sh 內容為
#!/usr/bin/env bash
set -ex
exec >> /var/log/upssched-cmd.log 2>&1
echo "執行 ID: $(id)"
function xpoweroff(){
/sbin/shutdown -h +0 "UPS has been on battery for 1 minute, shutting down."
}
case $1 in
onbattwarn)
logger -t upssched-cmd 'UPS 已經切換到電池供電,準備安全關閉系統...'
xpoweroff
;;
ups-back-on-line)
logger -t upssched-cmd '市電已恢復...'
;;
lowbatt)
logger -t upssched-cmd 'UPS 電量不足,立即關閉系統...'
xpoweroff
;;
*)
logger -t upssched-cmd "Unrecognized command: $1"
;;
esac
賦予可執行權限
sudo chmod +x /etc/nut/upssched-cmd.sh
重啟 NUT Client#
systemctl enable nut-monitor.service
systemctl restart nut-monitor.service
systemctl status nut-monitor.service
驗證配置#
你可以通過以下命令測試 UPS 狀態:
upsc <ups_name>@IP