- PVSM.RU - https://www.pvsm.ru -

У вас есть бэкапы? Хорошо, лёгкий вопрос. А когда вы в последний раз восстанавливали из них хотя бы один файл? Вот она неловкая пауза, понимаю… Между «бэкап есть» и «бэкап восстанавливается» находится пропасть, и в неё регулярно падают даже большие компании. Под катом две страшилки из реальной жизни и разбор того, как проверить свои бэкапы до того, как восстанавливать их придётся на боевом сервере.
Первая история из 1998 года. На финальном этапе производства «Истории игрушек 2» кто-то в Pixar выполнил /bin/rm -r -f * не в том каталоге, и примерно 90% фильма удалилось на глазах у команды. Бэкапы, конечно, были, но есть два «но». Система резервного копирования не работала [1] (переполненный диск маскировал ошибки), а восстановленная копия оказалась частично битой, и это вскрылось не сразу. Фильм спасла копия на домашнем компьютере технического директора Галин Сусман, которая работала из дома после рождения ребёнка. Машину аккуратно упаковали и привезли в студию на автомобиле, как самый ценный груз в истории компании.
Однако домашняя копия оказалась лишь самой свежей из нескольких неполных версий. Команда сравнила её с двухмесячным бэкапом и деревом, собранным из уцелевших файлов на компьютерах аниматоров, результатов тестовых рендеров и других кусков. Из условных 100 тыс. файлов принять сразу удалось около 70 тыс., а оставшиеся 30 тыс. сотрудники Pixar проверяли вручную. Даже после этого часть материалов восстановить всё равно не удалось.
Ирония в том, что спустя несколько месяцев сценарий «Истории игрушек 2» почти полностью переписали, после чего значительную часть фильма сняли заново…
Вторая история посвежее — 31 января 2017 года инженер GitLab, чиня реплику PostgreSQL, выполнил rm -rf каталога данных не на вторичном сервере, а на основном. Команду остановили через пару секунд, но из 300 ГБ осталось 4,5 ГБ. Дальше началось самое интересное, и я рекомендую прочитать их постмортем [2] целиком.
Дампы pg_dump создавались бинарниками версии 9.2 против PostgreSQL 9.6, из-за несовпадения major-версий завершались ошибкой, свежих дампов в S3 не было, а письма об ошибках отклонялись из-за DMARC. GitLab спас ручной LVM-снапшот, сделанный за шесть часов до инцидента для совершенно другой задачи. Однако были потеряны данные, созданные между 17:20 и 00:00 UTC, а это как минимум 5 тыс. проектов, 5 тыс. комментариев и около 700 пользователей.
Обратите внимание, в обеих историях бэкапы формально существовали. Проблема была в том, что никто не проверял восстановление. Так что делать?
Начнём с базы — с правила 3-2-1, которое придумал не сисадмин, а фотограф Питер Крог, автор книги The DAM Book про управление фотоархивами. Он обучал всех держать [3] три копии данных (1 основная + 2 резервные копии), на двух разных типах носителей, а одну копию удалённо.
Сейчас, так как шифровальщики первым делом уничтожают именно бэкапы, появилась версия 3-2-1-1-0. Добавляется «1» копия, которую нельзя перезаписать или удалить (в объектных хранилищах это Object Lock, а на железе это просто отключённый от сети диск), и «0» ошибок при автоматической проверке восстановления. Про эту проверку, а точнее, про её уровни, и поговорим дальше.

Самое дешёвое, что можно сделать, это убедиться, что архив вообще читается (смотрите на код возврата):
tar -tzf backup.tar.gz >/dev/null
tar -tzf backup.tar.gz | head -50
Если список файлов вывелся без ошибок, архив жив. Для серьёзных инструментов есть встроенная верификация. У restic:
restic -r /backup/restic-repo check
restic -r /backup/restic-repo check --read-data-subset=10%
Первая команда проверяет структуру репозитория, вторая реально скачивает и сверяет контрольные суммы 10% данных, что полезно для больших репо, подробности в мануале [4].
У borg аналог называется borg check [5] с флагом --verify-data, который криптографически проверяет содержимое, правда, читает при этом весь репозиторий, так что запускайте ночью:
borg check --verify-data /backup/borg-repo
Важно, целостность архива говорит только об отсутствии повреждённых байтов и ничего не сообщает о полноте данных. Например, дамп из ста строк может пройти все проверки и быть технически исправным, хотя нужных данных в нём уже не будет.
Если бэкапы у вас самописные через tar и cron, то добавьте в скрипт манифест контрольных сумм:
cd /backup
sha256sum backup.tar.gz > backup.tar.gz.sha256
sha256sum -c backup.tar.gz.sha256
Команда ловит битую запись на диске, оборванную закачку в облако или порчу при переносе. Проверять хеш лучше уже после копирования в хранилище. Если файл уехал в S3, MinIO или на другой сервер, скачайте его обратно в тестовый контур и проверьте там.
Лучший тест бэкапа — это его восстановление. Рекомендую завести ритуал, по которому раз в месяц вы будете поднимать бэкап на отдельной машине. Под это дело отлично подходит недорогая , которую можно создать на час репетиции и после удалить.
Для проверки файловых бэкапов просто восстановите архив в отдельный каталог и сравните с оригиналом:
tmpdir="$(mktemp -d)"
tar -xzf backup.tar.gz -C "$tmpdir"
find "$tmpdir" -maxdepth 2 -type f | head -50
Если бэкап делаете rsync-ом, обратную проверку можно гнать тем же rsync, он покажет различия без записи:
rsync -aHn --delete --itemize-changes /etc/ "$tmpdir/etc/"
Дальше попробуйте поднять сервис. Разверните конфиги nginx, запустите приложение и откройте сайт. Именно на этом шаге можно увидеть, что в бэкапе нет .env с секретами, что права на каталоги потерялись или что вы забыли бэкапить crontab. Можно проверить так:
curl -fsS http://127.0.0.1/health
И засеките время. GitLab в истории выше восстанавливал сервис около 18 часов, в том числе потому, что копирование со снапшота на медленных дисках заняло много времени. Если ваша база весит сто гигабайт, а вы никогда не замеряли восстановление, значит, вы не знаете своего времени простоя. Команда:
/usr/bin/time -v tar -xzf backup.tar.gz -C "$tmpdir"
Когда ритуал обкатан, его можно автоматизировать через ночной systemd timer или cron-задачу, которая восстанавливает свежую копию на тестовой машине, прогоняет пару проверок и шлёт результат в мессенджер. Это и есть тот самый «ноль» из правила.
В идеальном и прекрасном мире бэкап должен сам сообщать, что он жив. Для этого лучше использовать приём «Переключатель мертвеца», по которому задача после успешного завершения дёргает внешний URL, а если сигнала нет сутки, то сервис шлёт вам тревогу. Готовый вариант — это Healthchecks, а связку можно собрать и самому:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/backup"
HC_URL="https://hc-ping.com/your-uuid"
# Ищем свежий непустой дамп за последние 24 часа
if ! find "$BACKUP_DIR" -type f -name '*.dump*' -mtime -1 -size +1k -print -quit | grep -q .; then
echo "backup missing or empty" >&2
curl -fsS -m 10 --retry 3 -o /dev/null "$HC_URL/fail" || true
exit 1
fi
# Всё хорошо, сообщаем внешнему наблюдателю
curl -fsS -m 10 --retry 3 -o /dev/null "$HC_URL"
Проверка -size +1k отсекает дампы «в несколько байт», которые погубили GitLab. Заодно добавьте в скрипт бэкапа set -euo pipefail и проверку кода выхода pg_dump, чтобы система сигналила о падении.
Дамп базы нужно проверять восстановлением во временную базу, в идеале это отдельная ВМ!!! Используйте pg_dump той же major-версии, что и сервер, или более новой, если это поддерживается вашей версией PostgreSQL. Для проверки версий:
pg_dump --version
psql -d postgres -Atc 'SHOW server_version;'
Для custom-формата, который обычно получают через pg_dump -Fc, сначала можно посмотреть оглавление архива:
pg_restore --list dump.dump > /tmp/dump.list
head -50 /tmp/dump.list
Команда показывает, что архив читается, но не заменяет восстановление. Для нормальной проверки:
dropdb --if-exists restore_check
createdb --template=template0 restore_check
pg_restore
--exit-on-error
--no-owner
--no-privileges
-d restore_check
dump.dump
Флаги --no-owner и --no-privileges удобны для проверки данных в тестовой базе, потому что восстановление не будет падать на отсутствующих ролях и грантах. Для полноценной репетиции аварии роли и права тоже нужно проверять отдельно:
pg_dumpall --globals-only > pg_globals.sql
Если дамп обычный SQL-файл, его нужно загружать через psql, а не через pg_restore:
dropdb --if-exists restore_check
createdb --template=template0 restore_check
psql -X -v ON_ERROR_STOP=1 -d restore_check -f dump.sql
Не забудьте про ON_ERROR_STOP=1, потому что без него можно получить ошибки в середине вывода.
После загрузки проверьте не только список таблиц, а прикладные признаки:
psql -X -d restore_check -c "SELECT count(*) FROM users;"
psql -X -d restore_check -c "SELECT count(*) FROM orders;"
psql -X -d restore_check -c "SELECT max(created_at) FROM orders;"
Если в базе есть расширения, проверьте и их:
psql -X -d restore_check -c "SELECT extname, extversion FROM pg_extension ORDER BY 1;"
Для физических PostgreSQL-бэкапов есть утилита pg_verifybackup. Она проверяет base backup, созданный pg_basebackup, и если есть манифест, то backup_manifest:
pg_verifybackup /backup/postgres/base/2026-08-31
Если WAL лежит отдельно, укажите каталог с WAL:
pg_verifybackup --wal-path=/backup/postgres/wal /backup/postgres/base/2026-08-31
И помните, что логический дамп — это не единственный ваш вариант, для больших баз смотрите в сторону физических копий и PITR [7]через архив WAL.
Замерить время восстановления можно через:
/usr/bin/time -v pg_restore -d restore_check dump.dump
Для MySQL логика та же — создаёте пустую базу и наваливаете дамп — команды тут [8]. Отдельно запишите процедуру восстановления текстом и положите рядом с бэкапами и во внутреннюю вики. Поверьте, пригодится, когда горит ночью.
Отдельно проверьте секреты и конфиги — зачастую приложение после восстановления не стартует из-за того, что .env, ключи шифрования, kube secrets, crontab или конфиги nginx жили вне бэкапа. Для файлового бэкапа это можно проверить руками:
test -f /tmp/restore-test/etc/nginx/nginx.conf
test -f /tmp/restore-test/opt/app/.env
test -d /tmp/restore-test/var/www/uploads
Для приложения лучше держать короткий список обязательных файлов.
Ну и наконец, как вы любите, чеклист:
Раз в месяц восстанавливайте бэкап на отдельной машине и запускайте сервис, а не просто разворачивайте архив.
Прогоняйте встроенную верификацию: restic check --read-data-subset, borg check --verify-data или хотя бы tar -tzf.
Дампы баз проверяйте восстановлением во временную базу и сверяйте версии pg_dump и сервера.
Следите за свежестью и размером копий — файл нулевого размера это не бэкап.
Настройте мониторинг, чтобы о сломавшемся бэкапе узнавали первым вы, а не ваши пользователи.
Держите одну копию офлайн, на случай шифровальщика.
Кстати, такие проверки не нужно делать каждый день — это дорого и как бы не имеет смысла. Но хотя бы раз в месяц делайте пробное восстановление.
Когда вы в последний раз проверяли свои бэкапы? Делитесь в комментариях, заодно расскажите, какие сюрпризы нашли с помощью проверки.
© 2026 ООО «МТ ФИНАНС»
Автор: SrvTrantor
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/linux/457555
Ссылки в тексте:
[1] не работала: https://www.quora.com/Did-Pixar-accidentally-delete-Toy-Story-2-during-production
[2] постмортем: https://about.gitlab.com/blog/postmortem-of-database-outage-of-january-31/
[3] держать: https://www.sentinelone.com/cybersecurity-101/cybersecurity/3-2-1-backup-strategy/
[4] мануале: https://manpages.debian.org/testing/restic/restic-check.1.en.html
[5] borg check: https://borgbackup.readthedocs.io/en/stable/usage/check.html
[6] VDS: https://www.reg.ru/?rlink=reflink-717
[7] PITR : https://www.postgresql.org/docs/17/continuous-archiving.html
[8] тут: https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html
[9] Источник: https://habr.com/ru/companies/ruvds/articles/1076952/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1076952
Нажмите здесь для печати.