Tháng trước server production của mình bị tắt đột ngột lúc 2 giờ sáng. Không phải kernel panic, không phải OOM killer hoành hành — điện cúp đột ngột, UPS chạy được 8 phút rồi hết battery trong khi server không hề hay biết. Kết quả: PostgreSQL corrupt, mất gần 4 tiếng restore từ backup, khách hàng Nhật gọi điện sáng sớm hỏi tại sao hệ thống chết.
UPS có đó, battery vẫn còn tốt, nhưng không có gì kết nối hai thứ đó lại với nhau. Đó là lúc mình bắt đầu tìm hiểu nghiêm túc NUT.
Ba cách giám sát UPS trên Linux: So sánh trước khi chọn
Câu đầu tiên mình search là “linux ups monitoring” — mất khoảng 20 phút để nhận ra có 3 hướng khác nhau, mỗi cái có trade-off rõ ràng:
Cách 1: Phần mềm proprietary của nhà sản xuất UPS
APC có PowerChute, Eaton có Intelligent Power Manager, CyberPower có PowerPanel. Cắm USB, cài phần mềm, xong. Nghe có vẻ đơn giản.
Ưu điểm: Giao diện đẹp, support chính hãng, tích hợp sẵn với model UPS của họ.
Nhược điểm: Vendor lock-in nặng. APC PowerChute chỉ chạy với UPS APC. Eaton chỉ với Eaton. Đổi UPS là phải học lại từ đầu. Tính năng enterprise thường cần license trả phí, và tài liệu cho Linux kém bản Windows rõ rệt.
Cách 2: Script tự viết kết hợp cron
Về lý thuyết nghe hợp lý: đọc USB HID data từ UPS, viết bash script check battery mỗi phút, dưới ngưỡng thì trigger shutdown.
Ưu điểm: Kiểm soát hoàn toàn, hiểu từng dòng code chạy trên server mình.
Nhược điểm: Reinventing the wheel tốn kém hơn tưởng. UPS communication protocol phức tạp, mỗi model lại có quirks riêng. Mình đã thử một lần với UPS CyberPower cũ — mất 2 ngày debug tại sao script đọc sai battery percentage. Không đáng.
Cách 3: NUT (Network UPS Tools)
Open source, hỗ trợ 170+ protocol và hàng nghìn model UPS từ APC, Eaton, CyberPower, Belkin… Kiến trúc client-server: một máy kết nối UPS (NUT server), nhiều máy còn lại theo dõi qua mạng (NUT client). Package có sẵn trong mọi distro Linux chính.
Ưu điểm: Vendor neutral, battle-tested, hỗ trợ multi-server với 1 UPS duy nhất.
Nhược điểm: Config file hơi verbose, tài liệu đôi khi cũ. Nhưng sau khi cài xong thì gần như không cần động vào nữa.
NUT là lựa chọn đúng — và đây là lý do
Nếu bạn chỉ có 1 model UPS cố định và chắc chắn không thay đổi, phần mềm proprietary cũng ổn. Nhưng muốn giải pháp lâu dài, multi-server, hoặc đơn giản là muốn thứ gì đó mà Google ra được câu trả lời — NUT phù hợp hơn hẳn.
Cài đặt và cấu hình NUT từ đầu
Bước 1: Kiểm tra UPS được nhận diện qua USB
Cắm cáp USB từ UPS vào server, rồi chạy:
lsusb | grep -i "American Power\|Eaton\|CyberPower\|Tripplite\|UPS"
Output mẫu với UPS APC:
Bus 001 Device 003: ID 051d:0002 American Power Conversion Uninterruptible Power Supply
Thấy xuất hiện là tầng USB đã OK. Chưa có driver nhưng đã có tín hiệu tốt để tiếp tục.
Bước 2: Cài NUT
Trên Ubuntu/Debian:
sudo apt update
sudo apt install nut nut-client nut-server
Trên CentOS/RHEL/Rocky Linux:
sudo dnf install nut
Bước 3: Tìm driver phù hợp
Chọn sai driver là xong — NUT sẽ không đọc được gì từ UPS. May mắn là NUT có tool scan tự động:
sudo nut-scanner -U
Output gợi ý cấu hình sẵn:
[nutdev1]
driver = "usbhid-ups"
port = "auto"
vendorid = "051D"
productid = "0002"
product = "Back-UPS RS 1500MS2"
vendor = "American Power Conversion"
bus = "001"
Copy phần này, sẽ dùng ở bước tiếp theo.
Bước 4: Cấu hình ups.conf
sudo nano /etc/nut/ups.conf
Thêm vào (dùng kết quả từ nut-scanner ở trên):
[myups]
driver = usbhid-ups
port = auto
desc = "APC Back-UPS RS 1500"
Tên trong ngoặc vuông (myups) là tên tự đặt, sẽ được reference ở các file config khác.
Bước 5: Cấu hình nut.conf — chọn mode
sudo nano /etc/nut/nut.conf
Nếu chỉ có 1 server kết nối trực tiếp UPS:
MODE=standalone
Nếu server này sẽ là UPS server cho nhiều máy khác trong mạng:
MODE=netserver
Bước 6: Cấu hình upsd.conf và upsd.users
sudo nano /etc/nut/upsd.conf
# Chỉ lắng nghe local nếu standalone
LISTEN 127.0.0.1 3493
# Mở ra network nếu có máy khác cần kết nối
# LISTEN 0.0.0.0 3493
Tạo credentials cho upsmon xác thực với upsd:
sudo nano /etc/nut/upsd.users
[upsmon]
password = your_secure_password_here
upsmon master
Bước 7: Cấu hình upsmon.conf — bộ não ra lệnh shutdown
sudo nano /etc/nut/upsmon.conf
MONITOR myups@localhost 1 upsmon your_secure_password_here master
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h now"
POLLFREQ 5
POLLFREQALERT 5
HOSTSYNC 15
DEADTIME 15
POWERDOWNFLAG /etc/killpower
RBWARNTIME 43200
NOCOMMWARNTIME 300
FINALDELAY 5
SHUTDOWNCMD là trái tim của toàn bộ setup — lệnh này chạy khi NUT quyết định đã đến lúc tắt máy an toàn. Trước khi áp dụng, chạy which shutdown để confirm đường dẫn đúng trên hệ thống của bạn.
Bước 8: Khởi động và kiểm tra
sudo systemctl start nut-server
sudo systemctl start nut-client
sudo systemctl enable nut-server nut-client
# Khởi động driver kết nối UPS
sudo upsdrvctl start
# Đọc thông số UPS real-time
upsc myups@localhost
Output khi kết nối thành công:
battery.charge: 100
battery.charge.low: 10
battery.runtime: 1428
input.voltage: 229.0
output.voltage: 229.0
ups.load: 23
ups.status: OL
ups.status: OL = On Line (đang dùng điện lưới, bình thường). OB = On Battery (đang chạy battery). Thấy OL là setup đã thành công.
Alert fatigue: Bài học đau thương từ thực tế
Mình gặp đúng bẫy này khi mới cài — phải tune threshold nhiều lần mới ổn. Lần đầu set POLLFREQ 2 và DEADTIME 10 rất aggressive. Kết quả là nhận alert liên tục mỗi khi điện lưới dao động nhẹ — ở Nhật thỉnh thoảng vẫn có voltage sag ngắn vài giây. Telegram bot ping lúc 3 giờ sáng đến mức mình tắt thông báo, và rồi bỏ lỡ alert thật tuần sau đó.
Các giá trị hoạt động tốt sau nhiều lần điều chỉnh:
# Trong upsmon.conf
POLLFREQ 5 # Check mỗi 5 giây khi bình thường
POLLFREQALERT 5 # Check mỗi 5 giây khi đang on-battery
DEADTIME 15 # Coi là mất kết nối sau 15 giây
# Override battery low threshold (mặc định thường là 10%)
# Thêm vào ups.conf nếu muốn shutdown sớm hơn:
# override.battery.charge.low = 30
Cấu hình cảnh báo khi điện mất
Không ai muốn biết server tắt qua tin nhắn từ khách hàng lúc sáng sớm. Thêm phần này vào upsmon.conf để NUT tự cảnh báo trước:
NOTIFYCMD /etc/nut/notify.sh
NOTIFYFLAG ONLINE SYSLOG+WALL+EXEC
NOTIFYFLAG ONBATT SYSLOG+WALL+EXEC
NOTIFYFLAG LOWBATT SYSLOG+WALL+EXEC
NOTIFYFLAG COMMBAD SYSLOG+WALL+EXEC
Tạo script gửi email (hoặc thay bằng curl gửi Telegram):
sudo nano /etc/nut/notify.sh
#!/bin/bash
# $1 = message từ NUT (ví dụ: "UPS myups on battery")
echo "$1" | mail -s "[UPS Alert] $(hostname)" [email protected]
sudo chmod +x /etc/nut/notify.sh
Kiểm tra toàn bộ setup hoạt động đúng
Test an toàn nhất là dùng lệnh này — KHÔNG rút điện thật:
# Xem log realtime
sudo journalctl -u nut-server -f
sudo journalctl -u nut-client -f
# Kiểm tra từng thông số
upsc myups@localhost ups.status
upsc myups@localhost battery.charge
upsc myups@localhost battery.runtime
# Force shutdown để test path shutdown (CẢNH BÁO: máy sẽ tắt thật)
# sudo upsmon -c fsd
Sau khi verify xong, mình thêm cron check hàng tuần:
# crontab -e
0 9 * * 1 /usr/bin/upsc myups@localhost battery.charge | mail -s "Weekly UPS Battery: $(hostname)" [email protected]
Mỗi thứ hai nhận email với con số battery health. Không nhận được email là biết NUT hoặc UPS đang có vấn đề — đơn giản mà hiệu quả hơn nhiều so với chờ incident xảy ra mới phát hiện.
Tóm lại
NUT không phức tạp nếu hiểu kiến trúc: driver đọc data từ UPS, daemon upsd expose data qua network, và upsmon watch rồi ra lệnh shutdown khi cần. Mất khoảng 30 phút để có setup hoạt động từ đầu, sau đó gần như không cần maintenance. Cái mình tiếc nhất là không làm sớm hơn — trước cái đêm PostgreSQL corrupt đó.

