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

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

MAATRIX

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

Почему «бэкапы настроены» в наследство — это гипотеза, а не факт

У любой унаследованной схемы бэкапа есть особенность, которой нет у бэкапа, который вы настраивали сами: вы не видели, как она создавалась, и не знаете, какие решения принимались по пути. Может быть, полгода назад добавили новый docker-volume с файлами пользователей, а скрипт бэкапа не обновили. Может быть, хранилище сменили, а старые упоминания в документации остались. Может быть, автор скрипта в какой-то момент сам перестал доверять cron и завёл параллельный ручной процесс, о котором никто, кроме него, не знает.

Это не то же самое, что общая проверка бэкапа, который вы сами настроили и понимаете. Универсальные приёмы контроля целостности архива и валидности дампа разобраны в статье как проверить, что бэкап рабочий, не восстанавливая всё — четыре уровня от размера файла до содержимого дампа, применимые к любому бэкапу. Здесь же задача уже, но не проще: сначала выяснить, что вообще происходит в чужой, непрозрачной схеме — где искать, чему не верить и какие несостыковки типичны именно для наследства.

Три источника лжи, с которыми вы столкнётесь в первую очередь:

  • Документация отстаёт от реальности. README-backup.txt или комментарий в скрипте описывает схему, какой она была на момент написания, а не сейчас.
  • Скрипт формально работает, но не делает того, что от него ждут. Завершается с кодом 0, но по сути создаёт мусор — разбирали на конкретном примере в статье скрипт бэкапа падал молча четыре месяца: код возврата в пайпе относился не к той команде.
  • Предыдущий администратор сам ошибался или полагал, что достаточно «настроить один раз». Он не обманывал вас намеренно — возможно, и сам не проверял схему годами.

Задача следующих шагов — заменить веру фактами.

Шаг 1: ищем, где бэкапы лежат на самом деле — не там, где написано в конфиге

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

Начните с поиска по всей файловой системе, а не только там, где «должно быть»:

# файлы и каталоги с "backup", "bak", "dump" в имени — по всей системе
find / -xdev -iname "*backup*" -o -iname "*.bak" -o -iname "*dump*" 2>/dev/null | grep -v proc

# архивы БД и типичные форматы бэкапа независимо от каталога
find / -xdev \( -iname "*.sql.gz" -o -iname "*.dump" -o -iname "*.tar.gz" -o -iname "*.tar.zst" \) 2>/dev/null | grep -v proc

Дальше — источники, которые описывают, что должно происходить по расписанию, и их нужно сверить между собой: легаси-схемы часто накапливают дублирующие или противоречащие друг другу задачи:

# crontab всех пользователей, не только текущего
for u in $(cut -f1 -d: /etc/passwd); do echo "== $u =="; crontab -u "$u" -l 2>/dev/null; done

# системные cron-каталоги
ls /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ 2>/dev/null

# systemd timers — легко упустить, если ищете только cron
systemctl list-timers --all

Если находите строку вида /opt/scripts/backup.sh или rclone sync /data remote:backups, не останавливайтесь на самом факте существования задачи — откройте скрипт и посмотрите, куда он реально пишет:

cat /opt/scripts/backup.sh
grep -E "rclone|rsync|scp|aws s3|restic|borg" /opt/scripts/backup.sh

Отдельно проверьте конфиги специализированных инструментов, если они используются, — там назначение хранилища указано явно, но само по себе наличие конфига не значит, что он актуален:

# restic — репозиторий обычно в переменной окружения или в systemd-юните
grep -r RESTIC_REPOSITORY /etc/systemd/system/ /etc/environment ~/.bashrc 2>/dev/null

# rclone — список удалённых хранилищ
rclone listremotes

Если в схеме упоминается внешнее хранилище (S3-совместимое, SFTP, второй сервер), обязательно зайдите туда и посмотрите список файлов напрямую — не полагаясь на то, что скрипт «наверное» туда что-то кладёт:

# для S3-совместимого через rclone
rclone lsl remote:backups --max-age 90d

# для restic-репозитория
restic -r s3:s3.example.com/backups snapshots

# для SFTP
sftp backup-user@storage.example.com

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Шаг 2: возраст и полнота — бэкап есть, но какой из него прок

Найдя реальные файлы, следующий вопрос не «есть ли бэкап», а «насколько он свежий и всё ли в нём есть». Формально существующая, но полугодовой давности копия — это иллюзия защиты, а не защита.

Проверка даты тривиальна, но именно её пропускают чаще всего — сам факт находки файла успокаивает:

# последние по времени модификации файлы бэкапа в каталоге
ls -lt /path/to/backups/ | head -10

# то же самое для удалённого restic-репозитория — колонка time в snapshots
restic -r s3:s3.example.com/backups snapshots --compact

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

Дальше — вопрос полноты, который специфичен именно для унаследованной схемы: бэкапится ли всё, что реально важно, а не только то, что бэкапилось на момент написания скрипта. Составьте список того, что фактически работает на сервере (сайты, базы, docker-volume, конфиги), и сверьте его со списком того, что попадает в архив:

# что реально есть на сервере из баз данных и docker-volume
psql -U postgres -c "\l" 2>/dev/null
docker volume ls

# что бэкапится по факту — смотрим содержимое последнего архива
tar -tzf /path/to/latest-backup.tar.gz | head -30

Типичное расхождение легаси-схемы: скрипт бэкапит одну базу данных явно по имени (mysqldump myapp_db), а на сервере с тех пор завели вторую базу для нового сервиса — и она молча выпадает из защиты, потому что никто не редактировал скрипт при добавлении. Такое не обнаружить проверкой кода возврата — только сверкой «что есть» со «что бэкапится».

Если хранилище использует ротацию (хранит последние N копий), проверьте, сколько версий реально доступно прямо сейчас, а не сколько должно быть по настройке — битые запуски за последние недели могли незаметно вытолкнуть из ротации единственную рабочую копию: restic snapshots | wc -l или borg list /path/to/repo.

Шаг 3: пробное восстановление — единственное реальное доказательство

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

Для унаследованной схемы это особенно важно: вы не знаете, снимался ли дамп базы согласованно (с --single-transaction или остановкой записи), не битые ли файлы внутри архива, и не зависит ли восстановление от того, чего у вас просто нет — например, ключа шифрования, который хранился только у прежнего админа. Историю именно такого исхода — бэкапы шли исправно два года, а восстановиться не смогли из-за утраченного ключа — разбирали в статье бэкап был, а ключа шифрования не было.

Минимальный сценарий пробного восстановления для унаследованной схемы:

# поднимаем одноразовое окружение — отдельный VPS или контейнер,
# НЕ поверх продакшена и не поверх единственной рабочей копии
mkdir -p /restore-test && cd /restore-test

# restic: восстановление последнего снапшота во временную папку
restic -r s3:s3.example.com/backups restore latest --target /restore-test

# borg: то же самое
borg extract /path/to/repo::latest

# tar-архив
tar -xzf /path/to/latest-backup.tar.gz -C /restore-test

Дальше — не просто «файлы распаковались», а именно контроль содержимого:

# для дампа БД — реально накатить его в тестовую базу и свериться со счётчиками
createdb test_restore
psql test_restore < /restore-test/dump.sql
psql test_restore -c "SELECT count(*) FROM users;"

# сравнить с ожидаемым порядком величины на проде (не точным числом —
# оно расходится из-за времени между снятием бэкапа и проверкой)
psql production_db -c "SELECT count(*) FROM users;"

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

Разовая проверка снимает вопрос «работает ли схема прямо сейчас», но не отвечает на вопрос «будет ли она работать через полгода», когда состав данных изменится снова. Регулярный ритуал такой проверки для схемы, которой вы уже доверяете, — отдельная тема, разобранная в статье проверка восстановления бэкапа раз в месяц. Разница простая: там — регламент для схемы, в которую вы уже поверили, здесь — первичная проверка схемы, полученной в наследство.

Шаг 4: проверяем скрипты бэкапа на битые пути и молчаливые сбои

Отдельная категория проблем унаследованной схемы — скрипт, который годами исправно завершается с кодом 0, но по сути не делает свою работу, потому что пути, на которые он ссылается, перестали существовать, а сам скрипт этого не проверяет.

Выпишите все пути, на которые ссылается скрипт:

grep -nE "^\s*(cd |cp |tar |rsync|mysqldump|pg_dump)" /opt/scripts/backup.sh

Для каждого исходного пути (что бэкапится) и каждого целевого (куда пишется) проверьте, что путь реально существует и доступен для записи прямо сейчас, а не существовал на момент написания скрипта:

# исходный каталог — реально ли он там, где скрипт думает
ls -ld /var/www/old-project-name/uploads

# смонтирован ли сетевой диск, на который якобы льётся бэкап
mount | grep backup
df -h /mnt/backup-storage

Классический тихий сбой легаси-схемы: скрипт пишет в /mnt/backup-storage/, который раньше был примонтированной NFS-шарой, а после переустановки или миграции сервера точка монтирования не восстановилась. Каталог /mnt/backup-storage/ при этом продолжает существовать как обычная пустая папка на локальном диске — команда cp или rsync в неё пишет без единой ошибки, файлы там действительно появляются, только это уже не резервная копия на отдельном хранилище, а копия рядом с оригиналом на том же физическом диске. Проверить это одной командой: findmnt /mnt/backup-storage — если путь не точка монтирования, вывод будет пустым.

Ещё один типичный источник молчаливых сбоев — пайпы без проверки кода возврата каждой команды. Конвейер вида mysqldump mydb | gzip > backup.sql.gz в bash по умолчанию возвращает код выхода последней команды (gzip), а не mysqldump: если дамп упал из-за проблем с доступом, а gzip благополучно заархивировал пустой поток, весь конвейер отрапортует об успехе. Проверьте, есть ли в начале скрипта set -o pipefail:

head -5 /opt/scripts/backup.sh | grep -E "set -.*pipefail|set -euo pipefail"

Если строки нет — это явный признак, что скрипт мог годами маскировать сбои источника данных, и стоит перепроверить архивы за длительный период, а не только последний. Проверьте также права на запись в целевой каталог от имени того пользователя, под которым реально запускается cron-задача, а не от вашего текущего: sudo -u backup-user touch /path/to/backups/test-write.

Что делать с результатами проверки — три сценария

По итогам шагов 1–4 унаследованная схема обычно попадает в один из трёх сценариев.

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

Схема частично рабочая. Например, база бэкапится исправно, а новый docker-volume с файлами — нет; или бэкап свежий, но ключ шифрования утрачен и старые архивы не восстановить. Здесь не нужно ломать то, что работает — точечно закройте конкретный пробел, зафиксировав недостающее сразу в надёжном месте.

Схема нерабочая или отсутствует по факту. Найденные файлы оказались мусором, путь — локальной папкой вместо сетевого хранилища, восстановление стабильно падает. Чинить самописный скрипт с накопившимися огрехами обычно дороже по времени, чем настроить схему заново на специализированном инструменте (restic, borg, urbackup) — сразу с проверенным хранилищем и рабочим пробным восстановлением.

Независимо от сценария, снимите один ручной бэкап прямо сейчас, до того как продолжите разбираться дальше:

mysqldump --all-databases | gzip > ~/emergency-backup-$(date +%Y%m%d).sql.gz

И держите в уме отдельный риск, не связанный со свежестью или полнотой архива: если продакшн-сервер имеет доступ на запись и удаление к хранилищу бэкапов, при компрометации шифровальщиком он доберётся и до копии на той же учётке. Проверьте права доступа к хранилищу снаружи, под учёткой бэкапа, а не под root продакшена.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Сколько времени занимает полная проверка унаследованной схемы бэкапа?

Для типичного сервера с одной-двумя базами и несколькими docker-volume — от двух до четырёх часов, включая пробное восстановление. Если схема состоит из нескольких разрозненных скриптов без документации, закладывайте день: основное время уходит не на восстановление, а на поиск всех мест, где вообще что-то бэкапится.

Что делать, если я вообще не нашёл никаких следов бэкапа?

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

Пробное восстановление обязательно делать на отдельном сервере, а не локально?

Крайне желательно — восстановление поверх продакшена рискует затереть рабочие данные сомнительной копией, а локальный контейнер на той же машине не проверяет сценарий «сервера больше нет», который и происходит при настоящей аварии. Недорогой VPS с почасовой оплатой для разового теста обычно дешевле, чем кажется, и снимается сразу после проверки.

Ключ шифрования от бэкапа не найден — можно ли считать архивы потерянными?

Не обязательно сразу. Поищите его в переменных окружения systemd-юнита, в password-менеджере компании, в конфиге инструмента (~/.config/restic/ и подобных) и в заметках прежнего администратора. Если ключ утрачен безвозвратно — старые зашифрованные архивы нужно списать как нерабочие и настроить новую схему шифрования с самого начала, сразу зафиксировав ключ в доступном команде месте.

Скрипт использует инструмент, которым я никогда не пользовался — стоит ли сразу переписывать на знакомый?

Не сразу. Сначала проверьте, что текущая схема на самом деле рабочая — если да, разумнее разобраться в существующем инструменте, чем терять время на миграцию рабочей схемы ради личного удобства. Замену планируйте, только если проверка показала реальные проблемы в самой схеме.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →