Поставил ИБП и научил сервер гаситься сам

Свет у меня моргает редко, но метко. Пару раз в год — короткое отключение на минуту-две, и каждый раз сервер падал жёстко, как будто выдернули вилку. А он так и было, по сути. После одного такого падения ext4 ругнулся при загрузке, а у Postgres пришлось чистить WAL. Ничего не потерял, но понервничал. После второго — поехал за ИБП.

Купил обычный линейно-интерактивный UPS с USB. Задача простая: продержать сервер пару минут и, если свет не вернулся, корректно его погасить — чтобы файловая система и базы закрывались штатно, а не аварийно.

Софт для этого — NUT (Network UPS Tools). Он общается с ИБП по USB, понимает заряд и состояние сети, и дёргает shutdown по заданным правилам.

Ставится из репозитория:

sudo apt install nut

Дальше три конфига. Сначала описываем сам ИБП в /etc/nut/ups.conf:

[homeups]
    driver = usbhid-ups
    port = auto
    desc = "Home server UPS"

Проверяем, что NUT его видит:

sudo upsdrvctl start
upsc homeups
# battery.charge: 100
# ups.status: OL        <- OL = на сети, OB = на батарее

Самое интересное — логика выключения. Живёт в /etc/nut/upssched.conf и в скрипте-обработчике. Я не глушу сервер при первом же чихе сети: даю отлежаться, и реагирую либо по таймеру на батарее, либо по проценту заряда — что наступит раньше.

# /etc/nut/upssched.conf
CMDSCRIPT /etc/nut/upssched-cmd

# свет пропал — НЕ паникуем, ждём 60 секунд
AT ONBATT * START-TIMER onbatt_grace 60
# свет вернулся раньше — отменяем таймер
AT ONLINE * CANCEL-TIMER onbatt_grace
# UPS сам кричит "заряд низкий" — гасимся немедленно
AT LOWBATT * EXECUTE ups_shutdown

А вот сам обработчик, где добавляю ещё и порог по проценту — не хочу дожидаться, пока батарея сядет в ноль:

#!/bin/bash
# /etc/nut/upssched-cmd
case $1 in
    onbatt_grace)
        CHARGE=$(upsc homeups battery.charge)
        logger "NUT: на батарее, заряд ${CHARGE}%"
        # ниже 50% — не рискуем, гасимся
        if [ "$CHARGE" -le 50 ]; then
            /usr/sbin/upsmon -c fsd   # инициируем shutdown
        fi
        ;;
    ups_shutdown)
        logger "NUT: LOWBATT, аварийное выключение"
        /usr/sbin/upsmon -c fsd
        ;;
esac

Идея такая: пропал свет — ждём минуту (вдруг это короткое моргание и всё вернётся). Если через минуту мы всё ещё на батарее и заряд просел до 50% — гасим заранее, с запасом. А если ИБП сам сигналит LOWBATT — выключаемся не раздумывая.

Команда upsmon -c fsd (forced shutdown) запускает штатное выключение: systemd останавливает сервисы, контейнеры получают SIGTERM, базы закрываются как надо, ext4 размонтируется чисто.

Проверять это удобно, выдернув ИБП из розетки и глядя в логи (journalctl -fu nut-monitor). У меня сервер на батарее в простое живёт минут двадцать, так что 50%-порог срабатывает сильно раньше — времени на корректное гашение вагон.

С тех пор отключения света — не событие. Свет мигнул, сервер пережил, я даже не заметил. А если надолго — он сам аккуратно ляжет спать и утром поднимется без единого fsck.