Hướng dẫn cài đặt NUT (Network UPS Tools) để giám sát UPS và bảo vệ Linux Server khi mất điện

Monitoring tutorial - IT technology blog
Monitoring tutorial - IT technology blog

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 2DEADTIME 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 đó.

Share: