Почему «у нас есть бэкапы» часто означает «восстановиться не сможем»
Резервное копирование почти всегда «работает» ровно до первого реального инцидента. В панели всё зелёное, задачи выполняются, но в день X внезапно выясняется, что копии неполные, зашифрованные архивы не расшифровать (ключи потеряны), точки восстановления не совпадают с ожиданиями бизнеса, а процедура восстановления не запускалась годами.
Подход backup 3-2-1 полезен тем, что заставляет мыслить не «куда складывать дампы», а «как гарантировать восстановление при разных типах отказов». Дальше разберём практичную схему: где хранить копии, как выбирать retention, как привязать всё к RPO/RTO и как поставить регулярный restore test так, чтобы он не превратился в ручную пытку.
Правило 3-2-1: что именно означают цифры
Классическая формулировка:
- 3 копии данных: рабочая + минимум две резервные.
- 2 разных типа носителей/хранилищ: например, локальный диск и удалённое объектное хранилище; либо разные storage-классы/провайдеры; либо снапшоты и файловые архивы.
- 1 копия вне основной площадки (offsite): переживает пожар/потерю узла/удаление данных/сбой СХД/«случайный rm -rf».
В 2025-м «носители» чаще означают не ленты и HDD, а разные домены риска: разные учётки и ключи доступа, разные механизмы хранения (дампы vs физические бэкапы vs журналы), разные зоны отказа.
3-2-1 не отменяет здравый смысл
Если данные критичны, правило расширяют до 3-2-1-1-0: дополнительная неизменяемая копия (immutability/WORM) и 0 ошибок по результатам верификации. Но начинать проще всего с 3-2-1 и дисциплины restore test.

RPO и RTO: переводим «хочу надёжно» в измеримые цифры
Два параметра задают рамки стратегии:
- RPO (Recovery Point Objective) — сколько данных вы готовы потерять по времени. Пример: RPO=15 минут означает, что после аварии допустима потеря изменений не более чем за 15 минут.
- RTO (Recovery Time Objective) — за сколько времени сервис должен вернуться в строй. Пример: RTO=60 минут — через час после инцидента система должна работать.
Если RPO и RTO не зафиксированы, вы почти неизбежно строите «бэкап ради бэкапа»: он будет либо слишком дорогим, либо не выдержит ожиданий бизнеса.
Как RPO влияет на тип бэкапа для баз данных
Для MySQL и PostgreSQL RPO напрямую определяет, хватит ли периодических «полных» копий или нужен журнал изменений:
- RPO в часы: часто достаточно ежедневной полной копии (дамп или физический бэкап).
- RPO в минуты: почти всегда нужен PITR (point-in-time recovery) через журналы:
binlogдля MySQL и WAL-архивация для PostgreSQL. - RPO близкий к нулю: нужна репликация, но репликация не заменяет бэкап (она тиражирует и ошибку/порчу данных).
Если вы только начинаете, полезно зафиксировать «целевой RPO» и «переходный RPO». Например: сейчас RPO=24 часа (пока только ночной дамп), цель через месяц — RPO=30 минут (дамп + журналы).
Как RTO влияет на формат хранения и автоматизацию
Даже при одинаковом RPO, RTO может сильно отличаться. Для RTO=15–30 минут обычно нужно заранее иметь:
- быстрый доступ к «последнему полному» бэкапу (локально или в пределах одной сети);
- отработанную процедуру восстановления с заранее подготовленными конфигами, правами, секретами;
- проверенные шаги переключения приложения на восстановленную БД (DNS/строка подключения/балансировщик).
Если RTO «день-два», можно использовать более дешёвые и медленные уровни хранения. Но это не отменяет регулярного restore test.
Retention: сколько хранить и почему «30 дней» — почти всегда случайная цифра
Retention — политика хранения копий: сколько держим ежедневных, недельных, месячных, иногда годовых. Её нельзя выбирать «по привычке»: она должна отвечать на два разных типа инцидентов.
- Операционные сбои: случайно удалили таблицу, выкатили миграцию с ошибкой, испортили данные импортом. Тут нужны частые точки восстановления за последние дни/недели.
- Долгоживущие инциденты: тихая порча данных, компрометация, вредоносная правка, обнаруженная через месяц. Тут помогают недельные/месячные копии и отдельный offsite.
Стартовый шаблон retention для небольших проектов:
- ежедневные: 14–30;
- еженедельные: 8–12;
- ежемесячные: 6–12.
Но корректнее идти от требований: «какую самую старую ошибку мы реально хотим уметь откатить» и «сколько стоит хранение». Для баз данных объём растёт быстро, поэтому заранее продумайте, какие типы копий будут полными, а какие — инкрементальными (или основанными на журналах).
Архитектура 3-2-1 на практике: минимальная рабочая схема
Ниже — вариант, который хорошо масштабируется и закрывает типовые риски.
Копия №1: локальный быстрый слой (для RTO)
Цель слоя — быстрое восстановление при «обычных» проблемах: неудачный релиз, ошибочная миграция, человеческий фактор. Обычно здесь хранят:
- последние полные бэкапы;
- инкременты/журналы для PITR (если нужен малый RPO);
- короткий retention (например, 7–14 дней), чтобы не забить диск.
Важный принцип: локальный слой не должен быть единственным. Он уязвим к тем же событиям, что и прод.
Копия №2: удалённое хранилище (offsite)
Это «парашют», который должен переживать потерю узла/площадки и часть сценариев компрометации. Практики, которые действительно работают:
- отдельные ключи доступа и отдельная учётка, по возможности в другом контуре;
- минимальные права (для загрузки — без массового удаления);
- сетевые ограничения;
- по возможности неизменяемость (WORM/immutability) или хотя бы версионирование.
Если проект живёт на виртуальных машинах, offsite удобно организовать на отдельном узле/площадке: например, выделить отдельную VDS под приём бэкапов и периодические restore test (с ограниченным доступом и раздельными ключами).
«2 разных хранилища» как разные домены отказа
Формально выполнить правило легко: «локальный диск и удалённый диск». Но если оба завязаны на один аккаунт и одну пару ключей, это не разные домены риска. Разные домены — это когда одно событие не уничтожает всё сразу: другая учётка, другой провайдер, другой независимый storage-слой, отдельная политика доступа.

Restore test: единственный способ доказать, что бэкап существует
Restore test — регулярная проверка восстановления. Не «проверка, что файл есть», а полный цикл: скачать, проверить целостность, расшифровать, развернуть, поднять сервис и выполнить контрольные проверки.
Рекомендованная частота (как ориентир):
- критичная БД: еженедельно (частичный тест) + ежемесячно полный прогон;
- обычные проекты: ежемесячно;
- после изменений в схеме бэкапа, ключах/правах, версиях СУБД: внепланово.
Что именно проверять в restore test
- архив скачивается и проходит проверку целостности (checksum);
- ключи/пароли доступны по аварийной процедуре;
- восстановление заканчивается успешным стартом СУБД;
- данные «похожи на правду»: размеры, количество таблиц, контрольные выборки;
- приложение может подключиться (в тестовой среде);
- RTO реально достижим: измеряете время по шагам.
«Бэкап успешен» без restore test означает только одно: скрипт отработал без ошибок. О восстановлении это может не говорить ничего.
MySQL: практический набор для 3-2-1 (дамп + binlog для RPO в минуты)
Для MySQL распространены две логики: логический дамп (mysqldump) и/или физический бэкап. В базовом универсальном варианте можно стартовать с «дамп + binlog», чтобы получить PITR между дампами.
Включаем binlog и задаём срок хранения
Проверьте, что бинарные логи включены (обычно параметр log_bin), и настройте удержание. Для MySQL 8 ключевой параметр — binlog_expire_logs_seconds:
mysql -e "SHOW VARIABLES LIKE 'log_bin%';"
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';"
Идея простая: срок хранения binlog должен перекрывать интервал между полными бэкапами с запасом, иначе PITR окажется невозможен.
Если вам важен предсказуемый PITR, полезно отдельно разобраться с GTID и восстановлением по binlog (с практическими сценариями): PITR в MySQL/MariaDB через binlog и GTID.
Дамп + компрессия + шифрование (пример)
Шифруйте бэкапы до отправки в offsite. Ниже — пример потока «дамп → сжатие → шифрование». В примере используется gpg (ключ/получатель замените на ваш).
mkdir -p /backups/mysql
mysqldump --single-transaction --routines --events --triggers --all-databases | gzip -1 > /backups/mysql/mysql-$(date +%F).sql.gz
gpg --batch --yes --encrypt --recipient backup@company.example /backups/mysql/mysql-$(date +%F).sql.gz
Локально держите короткий retention, а в offsite — полный срок по политике хранения. Важно: «ключи бэкапов» должны жить по отдельному регламенту, иначе одна потеря секретов отменит всю стратегию.
Restore test для MySQL (в тестовой VM)
Минимально полезная проверка: развернуть чистый инстанс, залить дамп, выполнить контрольные запросы. Примерный скелет команд:
gpg --batch --yes --decrypt /backups/mysql/mysql-2025-12-25.sql.gz.gpg > /tmp/mysql.sql.gz
gzip -d -c /tmp/mysql.sql.gz > /tmp/mysql.sql
mysql -uroot -p -e "SELECT 1;"
mysql -uroot -p < /tmp/mysql.sql
mysql -uroot -p -e "SHOW DATABASES;"
Если у вас заявлен RPO «в минуты», вы обязаны периодически тестировать восстановление «дамп + binlog» до выбранной точки времени (PITR), иначе это только декларация на бумаге.
PostgreSQL: практический набор для 3-2-1 (base backup + WAL для PITR)
В PostgreSQL путь к малому RPO — базовый бэкап плюс WAL-архивация. Тогда можно восстановиться на конкретное время (PITR) между полными копиями.
Проверяем, включена ли WAL-архивация
Ключевые параметры: wal_level, archive_mode, archive_command. Быстро посмотреть:
psql -c "SHOW wal_level;"
psql -c "SHOW archive_mode;"
psql -c "SHOW archive_command;"
archive_command должен надёжно сохранять WAL в отдельное место. Критично: команда должна возвращать успешный код только если WAL действительно сохранён, иначе вы получите «дырки» и невозможность PITR.
Базовый бэкап (pg_basebackup) как «полная точка»
pg_basebackup — базовый инструмент. Пример создания полной копии в каталог:
mkdir -p /backups/pg/base
pg_basebackup -D /backups/pg/base/$(date +%F) -Fp -Xs -P -R
Дальше каталог обычно упаковывают, шифруют и отправляют в offsite. Для крупных баз часто применяют специализированные решения, но логика 3-2-1 и restore test не меняется.
Restore test для PostgreSQL: проверяем старт и контрольные запросы
Частая проблема: файлы восстановили, но сервер не стартует из-за прав, несовместимой версии, отсутствия WAL или неверных параметров. Поэтому тест должен включать именно запуск.
sudo -u postgres pg_ctl -D /var/lib/postgresql/restored start
psql -c "SELECT now();"
psql -c "SELECT count(*)" FROM pg_catalog.pg_class;"
Если вы делаете PITR, добавьте отдельный тест восстановления на указанное время и проверку «ожидаемого состояния» (например, наличие записей после контрольной транзакции).
Безопасность бэкапов: шифрование, доступы, ключи
Бэкапы обычно содержат всё: персональные данные, токены, коммерческую информацию. Потеря бэкапов по последствиям часто хуже, чем потеря прод-сервера. Минимальные правила:
- Шифруйте до отправки в удалённое хранилище. Само хранилище не должно быть «последним рубежом» безопасности.
- Разделяйте права: аккаунт, который пишет бэкапы, не должен иметь возможность массово удалить историю. Где возможно — делайте режим «только запись», а удаление по retention выполняйте отдельным контуром.
- Ключи шифрования храните в управляемом контуре (vault/менеджер секретов) и держите офлайн-резерв по регламенту.
- Логируйте операции: кто запустил, что создал, куда выгрузил, что удалил, сколько заняло времени.
Отдельно проверьте защиту каналов доступа к БД (внутренний TLS, доверенные CA, ротация сертификатов): TLS для MySQL/PostgreSQL: CA, сертификаты и проверка клиента.
Автоматизация: как не утонуть в скриптах и не потерять контроль
Автоматизация часто начинается с cron, но зрелая схема почти всегда добавляет три вещи: наблюдаемость, безопасную ротацию и тест восстановления.
Наблюдаемость: метрики, алерты, отчёты
Минимальный набор сигналов:
- последний успешный бэкап (timestamp);
- размер бэкапа (резкие изменения — тревожный признак);
- время выполнения;
- ошибки выгрузки в offsite;
- результаты restore test (успешно/неуспешно).
Цель простая: узнать о проблеме раньше, чем о ней узнает продакшен-инцидент.
Ротация по retention без «rm -rf и молимся»
Удаление старых копий должно быть:
- детерминированным (понятно, какие файлы удаляются и почему);
- безопасным (защита от удаления «не того»);
- логируемым (что удалили, сколько освободили).
Практичный подход: структура каталогов по датам, отдельный manifest со списком копий и checksum, удаление только того, что есть в manifest и подходит под retention.
Чек-лист 3-2-1 для MySQL/PostgreSQL
- Определены RPO/RTO для базы и согласованы с владельцем сервиса.
- Есть минимум 3 копии данных (prod + 2 бэкапа).
- Используются 2 разных домена хранения/риска (не «два каталога на одном диске»).
- Есть 1 offsite копия, переживающая отказ площадки и ошибки администратора.
- Настроена понятная retention (дни/недели/месяцы) и безопасная ротация.
- Бэкапы шифруются, ключи доступны по аварийному регламенту.
- Выполняется регулярный restore test с запуском СУБД и контрольными проверками.
- Замеряется фактический RTO по шагам восстановления и периодически пересматривается.
Типовые ошибки, которые ломают стратегию
- Бэкап на тот же сервер без offsite: при потере диска вы потеряете всё.
- Одинаковые ключи к продакшену и к хранилищу бэкапов: компрометация одного контура тянет второй.
- Нет PITR при требовании малого RPO: ежедневный дамп не даст RPO=15 минут.
- Нет restore test: «зелёные джобы» не равны восстановлению.
- Retention без смысла: храните много, но не те точки восстановления, которые реально нужны.
Как начать, если сейчас бэкапы хаотичные
План внедрения, который даёт эффект быстро:
- Зафиксируйте RPO/RTO (хотя бы грубо) для 1–2 ключевых баз.
- Сделайте одну надёжную полную копию + offsite (начните с простого).
- Добавьте retention и безопасную ротацию.
- Внедрите restore test в отдельной тестовой среде.
- Если нужен малый RPO — включайте PITR: MySQL binlog, PostgreSQL WAL.
Главное — не пытайтесь сразу «идеально». Лучше стабильно выполнять 3-2-1 с проверкой восстановления, чем иметь сложную систему, которой никто не доверяет.


