reliabilitybackupdisaster-recoverynonfunctionalqa

Тестирование восстановления из бэкапа: бэкап, который никто не восстанавливал

31 января 2017 инженер GitLab во время инцидента случайно удалил каталог с боевой базой — 300 ГБ данных исчезли на глазах. Дальше началось интересное: из пяти способов бэкапа и репликации, которые у них были, не сработал ни один — дампы оказывались размером в пару килобайт, репликация отставала, снапшоты были не там. Спасла компанию случайная копия, снятая руками за 6 часов до этого для другой задачи. Их публичный постмортем — лучший учебник по теме, чем любая статья.

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

Бэкап ≠ восстановление

«Бэкап прошёл успешно» означает ровно одно: процесс записи завершился без ошибки. Он не означает, что:

  • в архиве есть данные (а не пустой файл после mysqldump, у которого не хватило прав);
  • архив не побит и распакуется;
  • из него можно поднять рабочую систему;
  • данные внутри консистентны (снимок сделан в момент, когда половина транзакций была на полпути).

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

RTO и RPO — два числа, без которых бэкап бессмысленен

Прежде чем что-то тестировать, договоритесь о двух цифрах:

  • RPO (Recovery Point Objective) — сколько данных допустимо потерять, измеряется во времени. Бэкап раз в сутки = RPO 24 часа: при аварии теряешь до суток. Приемлемо для блога, катастрофа для платежей.
  • RTO (Recovery Time Objective) — за сколько система должна снова работать. Если восстановление 500 ГБ занимает 9 часов, а бизнес заложил RTO 1 час — у вас несоответствие, и узнать о нём во время аварии — худший вариант.

QA проверяет не «есть ли бэкап», а укладывается ли реальное восстановление в обещанные RTO/RPO. Это измеримо — берёшь и меряешь.

Что реально ломается при восстановлении

Сбои почти никогда не в самой команде restore. Они в стыках:

  • Пустой или битый архив. Дамп прошёл, но с ошибкой прав/места — внутри мусор. Ловится только распаковкой и проверкой контрольной суммы.
  • Не тот момент времени. Нужно восстановиться на «за 5 минут до аварии», а есть только вчерашний полный дамп — point-in-time recovery не настроен.
  • Разъехавшаяся схема. Бэкап старой версии БД, а код уже ушёл на новую схему — restore есть, а приложение на нём не стартует.
  • Зависимости и секреты. Восстановили БД, но потеряли ключи шифрования / переменные окружения / внешние конфиги — данные есть, доступа нет.
  • Чужое окружение. Бэкап снят на одной версии Postgres/железе, разворачивается на другой — несовместимость всплывает в самый неподходящий момент.
  • Порядок и объём. Что восстанавливать первым, чтобы сервис поднялся; и хватит ли места/времени на полный объём под нагрузкой.

Restore drill: репетиция, а не надежда

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

  • Восстановление проходит до конца, без ручных «а, тут ещё вот это допишем».
  • Данные консистентны: счётчики, суммы, связи между таблицами сходятся с эталоном (не просто «файлы на месте»).
  • Приложение на восстановленной базе реально запускается и работает — логин, ключевой сценарий, оплата.
  • Замеряем время: уложились в RTO или нет.
  • Проверяем последний бэкап и восстановление на разные моменты (вчера, неделю назад) — ротация и старые копии тоже должны быть живыми.

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

Мониторить надо восстановление, а не «бэкап прошёл»

Классическая ловушка: алерт настроен на «джоб бэкапа упал». Джоб не падал — он честно записал 2 КБ пустоты. Алерта нет, все спокойны, данных нет.

Что мониторить на самом деле:

  • размер бэкапа и его отклонение от вчерашнего (резкое падение = тревога);
  • возраст последнего успешного бэкапа (не устарел ли за пределы RPO);
  • результат последнего restore drill — зелёный только если из бэкапа реально поднялась рабочая система, а не если джоб записи вернул 0.

Чек-лист

  • Определены и согласованы с бизнесом RTO и RPO для каждой критичной системы.
  • Есть restore drill: бэкап регулярно разворачивается в изолированном окружении до рабочего состояния.
  • Проверяется консистентность данных после восстановления, а не только наличие файлов.
  • На восстановленной базе запускается приложение и проходит ключевой сценарий.
  • Замеряется время восстановления и сверяется с RTO.
  • Проверяется point-in-time восстановление (не только «вчерашний полный»).
  • Восстанавливаются и старые/ротационные копии, а не только последняя.
  • Учтены секреты, ключи шифрования, конфиги — без них данные бесполезны.
  • Мониторинг смотрит на размер/возраст/результат drill, а не на «джоб не упал».
  • Есть runbook восстановления, по которому сможет пройти дежурный в 4 утра, а не только автор.

Коротко — что забрать с собой

  • «Бэкап прошёл» доказывает только запись. Живость бэкапа доказывает единственная вещь — восстановление из него.
  • Без RTO/RPO бэкап нельзя протестировать: непонятно, что значит «успех».
  • Ломается не команда restore, а стыки: битый архив, не тот момент, схема, секреты, чужое окружение.
  • Restore drill — регулярный и по возможности автоматический — единственный способ знать, а не надеяться.
  • Мониторь размер/возраст/результат восстановления, а не зелёную галочку джоба.

Почитать: GitLab — постмортем инцидента 31 января 2017 · Google SRE Book — Data Integrity