OpsPulse

Бэкап создался, но рабочий ли он: 6 проверок, которые стоит автоматизировать

01.11.2026 4 мин чтения бэкапыpostgresqlbash

Самый неприятный сценарий с резервными копиями выглядит так: бэкапы делались каждую ночь, файлы лежат в нужной папке, а в день аварии выясняется, что последние три недели архивы весили по 20 килобайт и содержали одну строку с ошибкой.

Скрипт при этом «отработал». Cron его запустил, файл появился, никто ничего не заметил.

Ниже шесть проверок, которые превращают «файл есть» в «из этого можно восстановиться». Все они автоматизируются за вечер.

1. Файл свежий

Банально, но с этого начинаются почти все истории потери данных: скрипт перестал запускаться, а в папке по-прежнему лежит старый архив, и на первый взгляд всё в порядке.

find /backup -name 'db-*.dump' -mmin -1500 | head -1

-mmin -1500 — изменён меньше 25 часов назад. Для ежедневного бэкапа это день плюс запас. Пустой вывод означает, что свежего файла нет.

2. Размер не аномальный

Пустой или обрезанный дамп — частая история. pg_dump упал на середине, диск закончился, у пользователя БД отозвали права — а файл всё равно создан.

Минимальная защита — нижняя граница:

size=$(stat -c %s "$latest")
[ "$size" -ge $((10*1024*1024)) ] || echo "подозрительно маленький бэкап: $size байт"

Лучше — сравнение с предыдущим. Если сегодняшний бэкап вдвое меньше вчерашнего, это повод разобраться, даже если формально всё прошло без ошибок.

prev=$(cat /backup/.last_size 2>/dev/null || echo 0)
if [ "$prev" -gt 0 ] && [ "$size" -lt $((prev / 2)) ]; then
  echo "размер упал больше чем вдвое: было $prev, стало $size"
fi
echo "$size" > /backup/.last_size

3. Архив не битый

Для сжатых файлов есть встроенная проверка целостности:

gzip -t backup.sql.gz          # gzip
tar -tzf backup.tar.gz > /dev/null   # tar.gz: читаем список файлов

Обе команды возвращают ненулевой код, если архив повреждён или обрезан.

4. Дамп базы читается

Для PostgreSQL в формате custom (pg_dump -Fc) есть удобная проверка — вывести оглавление без восстановления:

pg_restore --list db.dump > /dev/null

Если файл обрезан или повреждён, pg_restore завершится с ошибкой.

Для текстового формата (.sql) можно проверить последнюю строку. Полный дамп PostgreSQL заканчивается комментарием о завершении:

tail -n 5 db.sql | grep -q 'PostgreSQL database dump complete'

Нет этой строки — дамп оборвался.

5. Пробное восстановление

Единственная проверка, которая отвечает на главный вопрос: можно ли из этого бэкапа поднять данные. Всё остальное — косвенные признаки.

createdb restore_test
pg_restore -d restore_test --no-owner db.dump
psql -d restore_test -tAc "SELECT count(*) FROM orders"
dropdb restore_test

Последний SELECT подставьте под свою схему: важно убедиться, что ключевые таблицы не пустые.

Пробное восстановление тяжёлое, гонять его каждую ночь на большой базе не обязательно. Разумный компромисс — раз в неделю, отдельной задачей.

6. Копия ушла за пределы сервера

Бэкап на том же диске, что и база, защищает от ошибки в данных, но не от потери сервера. Проверьте, что файл действительно появился во внешнем хранилище:

aws s3 ls "s3://my-backups/db-$(date +%F).dump"

Для rclone — rclone lsf remote:backups/ | grep "$(date +%F)". Команда завершится с ошибкой, если файла нет.

Собираем в один скрипт

Логика простая: все проверки подряд, при первой неудаче скрипт останавливается. Сигнал «всё хорошо» отправляется только в самом конце.

#!/usr/bin/env bash
set -euo pipefail

DIR=/backup
PING="https://opspulse.ru/ping/<uuid>"
MIN_SIZE=$((10*1024*1024))

# 1. свежий файл
latest=$(find "$DIR" -name 'db-*.dump' -mmin -1500 -printf '%T@ %p\n' \
         | sort -n | tail -1 | cut -d' ' -f2-)
[ -n "$latest" ] || { echo "свежего бэкапа нет" >&2; exit 1; }

# 2. размер
size=$(stat -c %s "$latest")
[ "$size" -ge "$MIN_SIZE" ] || { echo "маленький бэкап: $size" >&2; exit 1; }

# 4. дамп читается
pg_restore --list "$latest" > /dev/null

# 6. копия во внешнем хранилище
aws s3 ls "s3://my-backups/$(basename "$latest")" > /dev/null

# всё прошло — сообщаем
curl -fsS -m 10 --retry 3 "$PING"

set -euo pipefail здесь обязателен: без него скрипт продолжит выполняться после ошибки и дойдёт до curl, сообщив об успехе, которого не было.

Кто проверит проверку

Остаётся последний вопрос: что если сам проверочный скрипт перестанет запускаться? Тогда тишина будет выглядеть так же, как успех.

Поэтому сигнал устроен наоборот: не «сообщить, когда плохо», а «сообщить, когда хорошо». Если подтверждение не пришло вовремя — значит, что-то сломалось: бэкап, проверка, cron или весь сервер. Причину вы узнаете из логов, но сам факт сбоя — сразу.

Именно так работает OpsPulse: создаёте проверку, вставляете её адрес в последнюю строку скрипта, и если пинг не пришёл к ожидаемому времени, получаете уведомление в Telegram или на почту. Для задач, которые идут по расписанию, можно указать то же cron-выражение, что и в crontab, — например, 30 4 * * *, если проверка бэкапа запускается в 04:30.

Бесплатного тарифа хватает, чтобы закрыть ночной бэкап и еженедельное пробное восстановление.

OpsPulse

Проверьте, что ваши задачи реально работают

Одна строка в crontab — и вы узнаете о сбое раньше клиента. Бесплатный тариф навсегда.

Начать бесплатно