Резервное копирование и восстановление
Что копировать, куда, как проверять и как восстанавливаться. Документ исходит из того, что резервная копия существует не сама по себе, а ради восстановления: непроверенная копия равнозначна её отсутствию.
6 минутЧто копировать, куда, как проверять и как восстанавливаться. Документ исходит из того, что резервная копия существует не сама по себе, а ради восстановления: непроверенная копия равнозначна её отсутствию.
Что нужно до начала: работающий сервер с приложением, доступ к хранилищу вне этого сервера.
Место в цепочке
| Откуда пришли | Этот документ | Куда ведёт |
|---|---|---|
| приложение выкатывается и работает | что копируем, куда, как восстанавливаем и как это проверяется | наблюдение за системой, включая контроль свежести копий |
Что предыдущее звено обязано обеспечить: перед миграциями базы делается снимок — выкат с изменением схемы без свежей копии не имеет пути назад.
Что копировать, а что нет
| Данные | Копировать | Почему |
|---|---|---|
| база данных | да | единственный экземпляр, пересоздать нельзя |
| загруженные файлы (тома) | да | то же самое |
| секреты и файлы окружения | да | не лежат в репозитории; без них восстановленная система не запустится |
| конфигурация сервера вне репозитория | да | воспроизводится по памяти дорого |
| исходный код | нет | лежит в репозитории |
| образы контейнеров | нет | пересобираются из кода |
| логи | обычно нет | восстановлению не помогают; хранятся отдельно и недолго |
Проверка полноты — мысленный опыт: сервер уничтожен целиком. Что нужно, чтобы поднять систему заново? Всё из этого списка должно быть в копии либо в репозитории.
Куда класть
Копия хранится вне сервера, который копируется. Копия на том же диске защищает только от случайного удаления файла и не защищает ни от отказа диска, ни от потери доступа к серверу, ни от шифровальщика.
Минимально достаточная схема: свежие копии на сервере (быстрое восстановление) + отправка во внешнее хранилище (защита от потери сервера).
Доступ к внешнему хранилищу выдаётся отдельным ключом только на запись и чтение своего каталога. Ключ с правом удаления, лежащий на сервере, означает, что тот, кто получил сервер, удалит и копии.
Как копировать базу
Логический снимок переносим между версиями и читаем человеком:
pg_dump --format=custom --no-owner --no-privileges "$DATABASE_URL" > db-$(date -u +%Y%m%dT%H%M%SZ).dump--format=custom— сжатый формат, из которого можно восстановить выборочно;--no-owner,--no-privileges— снимок не тянет за собой пользователей и права конкретной установки, поэтому восстанавливается на другом сервере.
Снимать копию с работающей базы допустимо: команда видит согласованное состояние на момент старта.
Если база работает в контейнере, снимок делается клиентом той же основной версии — иначе формат может оказаться несовместим.
Проверка: файл ненулевого размера и читается инструментом восстановления:
ls -lh db-*.dump
pg_restore --list db-*.dump | head # ожидается перечень объектов, а не ошибка форматаКак копировать файлы и секреты
Тома контейнеров и каталоги с секретами — архивом, права и владельцы сохраняются:
tar -czf files-$(date -u +%Y%m%dT%H%M%SZ).tar.gz -C /var/lib/app uploads
tar -czf secrets-$(date -u +%Y%m%dT%H%M%SZ).tar.gz -C /opt/app .secretsАрхив с секретами шифруется до отправки во внешнее хранилище: там он лежит долго и доступен всем, у кого есть доступ к хранилищу.
gpg --symmetric --cipher-algo AES256 secrets-*.tar.gz # результат: .gpg, исходный файл удалитьПароль шифрования хранится не на этом сервере — иначе он теряется вместе с ним.
Расписание и срок хранения
Частота определяется тем, потерю какого объёма работы вы готовы принять: суточная копия означает, что в худшем случае теряются сутки.
Срок хранения задаётся так, чтобы пережить незамеченную порчу данных. Если ошибка обнаруживается через неделю, а копии хранятся три дня, восстанавливать будет нечего. Распространённая схема: ежедневные копии за последние две недели плюс еженедельные за несколько месяцев.
Старые копии удаляются автоматически: ручная чистка либо не делается, либо однажды удаляет нужное.
Проверка восстановлением
Единственное доказательство, что копия рабочая, — восстановление из неё. Проверка делается регулярно и не на боевой машине.
# 1. поднять пустую базу для проверки
docker run --rm -d --name restore-check -e POSTGRES_PASSWORD=check -p 15432:5432 postgres:18-alpine
# 2. восстановить копию
pg_restore --clean --if-exists --no-owner -d "postgresql://postgres:check@127.0.0.1:15432/postgres" db-*.dump
# 3. убедиться, что данные на месте
psql "postgresql://postgres:check@127.0.0.1:15432/postgres" -c "\dt"
psql "postgresql://postgres:check@127.0.0.1:15432/postgres" -c "SELECT count(*) FROM <ключевая таблица>"
# 4. убрать проверочную базу
docker rm -f restore-checkОжидается: восстановление без ошибок, таблицы на месте, число записей близко к боевому.
Проверять стоит и свежесть: копия, которая перестала создаваться месяц назад, выглядит как работающая, пока её не понадобится восстановить. Возраст последней копии — хороший повод для оповещения (см. документ о наблюдении).
Восстановление
Порядок при потере сервера:
- поднять чистый сервер, поставить Docker;
- получить копии из внешнего хранилища;
- расшифровать и распаковать секреты и файлы;
- развернуть код из репозитория, собрать образы;
- поднять базу, восстановить снимок;
- запустить приложение, проверить служебные ручки;
- направить домен на новый сервер, выпустить сертификат.
Порядок важен: приложение стартует после восстановления базы, а домен переключается последним, когда система уже отвечает.
Частичное восстановление — когда потеряны отдельные данные, а система работает. Снимок в пользовательском формате позволяет восстановить одну таблицу, не трогая остальные:
pg_restore --data-only --table=<таблица> -d "$DATABASE_URL" db-*.dumpПеред этим стоит сделать копию текущего состояния: восстановление поверх живых данных необратимо.
Типичные отказы
| Признак | Причина | Что делать |
|---|---|---|
| копия создаётся, но пустая | у пользователя нет прав на чтение данных | проверить права; контролировать размер файла, а не факт запуска |
pg_restore не читает файл | снимок снят клиентом новее сервера | использовать клиент той же основной версии |
| восстановление падает на правах и владельцах | снимок снят без --no-owner | пересоздать снимок с нужными флагами |
| копий нет за последние недели | задание перестало выполняться молча | оповещение по возрасту последней копии |
| копии есть, но не восстанавливаются | никогда не проверялись | ввести регулярную проверку восстановлением |
| копии удалены вместе с сервером | ключ доступа к хранилищу разрешал удаление | ключ только на запись; хранение с защитой от удаления |
| восстановили, приложение не стартует | не скопированы секреты и окружение | включить их в копию и в проверку |
Откройте исходник документа по ссылке «Предложить правку» — там же видно, что и когда в нём менялось.