Как мониторить бэкапы базы данных
Бэкапы ломаются буднично. Заканчивается место на диске, меняется пароль от базы, сервер пересобирают, а crontab на новый не переносят. Никто не жалуется, и проблема вскрывается в тот день, когда нужно восстанавливаться. Решение простое: пусть бэкап отчитывается о каждом запуске, а что-то другое замечает, если отчёт не пришёл.
Что может пойти не так
- Задача вообще не запускается. Cron остановлен, сервер заменили, строка из crontab потерялась при переезде.
- Дамп падает, а скрипт «успешен». В
pg_dump | gzip > fileкод выхода берётся от gzip, поэтому упавший дамп всё равно завершается с 0, если в скрипте нетset -o pipefail. - Дамп пустой или обрезанный. Не та база, нет прав, закончилось место.
- Загрузка в хранилище не удалась. Истекли ключи, переполнен бакет, сбой сети.
- Задача зависает. Блокировка или медленная сеть держат её бесконечно: она не завершается ни успехом, ни ошибкой.
Скрипт бэкапа, который отчитывается
Этот Bash-скрипт для PostgreSQL закрывает все пять случаев. Он сообщает Heartcron о старте, отправляет любую ошибку с номером строки, проверяет размер дампа и при успехе шлёт пинг с размером в теле запроса:
#!/usr/bin/env bash set -euo pipefail PING=https://ping.heartcron.com/<uuid> FILE=/var/backups/app-$(date +%F).sql.gz # «запустился»: запуск, который так и не завершился, считается зависшим curl -fsS -m 10 --retry 3 -o /dev/null "$PING/start" || true # любая упавшая команда сразу сообщает об ошибке trap 'curl -fsS -m 10 --retry 3 -o /dev/null --data-raw "failed at line $LINENO" "$PING/fail"' ERR pg_dump "$DATABASE_URL" | gzip > "$FILE" # дамп меньше 1 МБ — что-то не так; подберите порог под свою базу SIZE=$(stat -c %s "$FILE") if [ "$SIZE" -lt 1000000 ]; then echo "dump is only $SIZE bytes" >&2 false fi aws s3 cp "$FILE" "s3://my-backups/$(basename "$FILE")" curl -fsS -m 10 --retry 3 -o /dev/null --data-raw "size=$SIZE" "$PING"
Здесь важны детали:
set -euo pipefailостанавливает скрипт на первой упавшей команде, в том числе внутри конвейера.- Ловушка
ERRотправляет/fail, и алерт приходит через секунды, а не после следующего дедлайна. - Пинг о старте заканчивается на
|| true: если сервис мониторинга недоступен, бэкап всё равно выполнится. - Текст из
--data-rawсохраняется вместе с пингом, и в кабинете виден размер дампа за каждую ночь. stat -c %s— вариант для Linux; на macOS и BSD используйтеstat -f %z.
Запускайте скрипт из cron в спокойную минуту, а не ровно в полночь, и пишите лог:
17 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
Настройте проверку
- Создайте проверку с cron-расписанием
17 3 * * *и часовым поясом сервера. - Задайте запас времени больше обычной длительности. Если бэкап идёт 20 минут, разумно поставить 45–60 минут. Скрипт шлёт
/start, поэтому запуск, который начался, но не закончился за это время, будет отмечен как зависший. - Выберите, куда приходят алерты: Telegram, Slack, Discord, почта или вебхук.
- Подставьте адрес проверки в переменную
PING.
MySQL и MariaDB
Меняется только строка дампа. --single-transaction снимает согласованный снимок таблиц InnoDB без блокировок, а --quick выгружает большие таблицы потоком, не загружая их в память:
mysqldump --single-transaction --quick app | gzip > "$FILE"
Храните пароль в ~/.my.cnf с правами 600, а не в командной строке, где его видят другие пользователи в списке процессов.
Проверьте алерты один раз
- Запустите скрипт с неправильным
DATABASE_URL. Алерт об ошибке должен прийти через секунды. - Закомментируйте строку в crontab на одну ночь. Алерт должен прийти после запланированного времени плюс запас.
Мониторьте и восстановление
Успешный бэкап значит лишь то, что файл записан. Раз в месяц восстанавливайте свежий дамп во временную базу и выполняйте проверочный запрос, например считайте строки в таблице, которая никогда не бывает пустой. Сделайте это задачей по расписанию со своей проверкой — тогда вы будете знать, что бэкапы действительно рабочие.