8 lines
17 KiB
JSON
8 lines
17 KiB
JSON
{
|
||
"index": 259,
|
||
"slug": "editorial-2020-10-field-backup-recovery",
|
||
"title": "Резервная копия не равна восстановлению: как проверить backup без риска для источника",
|
||
"excerpt": "Архив можно увидеть и скачать, но это ещё не доказательство восстановления. Разбираем manifest, checksum, область данных, изолированную цель и проверку результата на учебном PostgreSQL-сценарии.",
|
||
"contentHtml": "<p>Симптом знакомый: в хранилище лежит свежий архив, команда видит его размер и дату, но никто не может уверенно сказать, что произойдёт после <code>restore</code>. Неясно, какие схемы попали в файл, совпадают ли его байты с теми, что указаны в журнале, куда пойдёт восстановление и чем считать результат успешным.</p>\n<p>Цена такой ошибки появляется в аварии. Оператор выбирает архив по имени, запускает привычную команду и обнаруживает неполный набор таблиц. Или восстанавливает данные поверх источника. В этот момент резервная копия не помогает: она превращается в ещё один объект, которому нужно доверять без проверки.</p>\n<p>Тезис статьи простой: backup готов к применению только тогда, когда команда может проверить его scope, целостность, безопасность цели и смысл результата. Наличие файла доказывает наличие файла. Оно не доказывает, что приложение сможет продолжить работу.</p>\n<h2>Что именно нужно доказать</h2>\n<p>Восстановление состоит из нескольких независимых вопросов. Первый — что архив выбран правильно. Второй — что его содержимое не изменилось после создания. Третий — что в него попала нужная область базы. Четвёртый — что команда не затронет источник. Пятый — что после восстановления появились ожидаемые схемы, таблицы и данные.</p>\n<p>Эти вопросы связывает короткий manifest. В нём хранят идентификатор архива, формат, область данных, исключения, SHA-256 и проверки результата. Manifest не заменяет сам архив и не делает восстановление автоматическим. Он задаёт договор, с которым можно сравнить фактический файл и фактический кандидат.</p>\n<pre><code>{\n "backupId": "training-catalog-2020-10-a",\n "format": "pg_dump custom",\n "artifact": {\n "file": "training-catalog.dump",\n "sha256": "..."\n },\n "scope": {\n "includes": ["schema:catalog", "schema:reference"],\n "excludes": ["cluster roles", "tablespaces", "external files"]\n },\n "checks": {\n "relations": ["catalog.items", "reference.codes"],\n "rows": { "catalog.items": 3, "reference.codes": 2 }\n },\n "target": "isolated training candidate only"\n}</code></pre>\n<p>Пример учебный. Имена, числа и значение хеша вымышлены. Поле <code>excludes</code> здесь не декоративно: логический dump одной базы не следует выдавать за копию всего кластера. Роли, tablespaces и внешние файлы требуют отдельного решения и отдельной проверки.</p>\n<figure><img src='/assets/editorial/2020/backup-recovery-diagnosis-2020.svg' alt='Схема проверки резервного архива: scope, checksum, безопасная цель и проверки восстановленных данных' loading='lazy' /><figcaption>Сначала проверяется договор архива и его байты, затем цель восстановления, затем структура и данные кандидата.</figcaption></figure>\n<h2>Почему checksum не заменяет restore</h2>\n<p>SHA-256 помогает ответить на узкий вопрос: совпадают ли байты фактического файла с ожидаемым digest. Если значение изменилось, файл нельзя считать тем же артефактом. Причиной может быть неполная передача, повреждение, подмена или выбор другого файла под похожим именем.</p>\n<p>Но одинаковый digest не доказывает правильный scope. Можно безошибочно сохранить неполный dump и получить идеальное совпадение checksum. Поэтому проверка идёт в два слоя: сначала bytes, потом содержимое архива и результат восстановления.</p>\n<h2>Симптомы и безопасные действия</h2>\n<div class='table-scroll'><table><caption>Первая классификация проблемы до повторного restore</caption><thead><tr><th scope='col'>Симптом</th><th scope='col'>Причина</th><th scope='col'>Проверка</th><th scope='col'>Действие</th></tr></thead><tbody><tr><td>Архив есть, но scope не описан</td><td>Файл создавали без явного договора</td><td>Сверить список ожидаемых схем и исключений с командой создания</td><td>Остановить drill и уточнить scope</td></tr><tr><td>SHA-256 не совпадает</td><td>Выбран другой или повреждённый файл</td><td>Заново вычислить digest фактического файла</td><td>Не читать и не восстанавливать файл; получить доверенный артефакт</td></tr><tr><td>В списке нет нужной relation</td><td>Она не попала в dump или фильтр сузил область</td><td>Сравнить <code>pg_restore --list</code> с manifest</td><td>Исправить создание архива или оформить зависимость отдельно</td></tr><tr><td>Цель совпадает с источником</td><td>Runbook не закрепил изоляцию</td><td>Проверить имя базы и запрет перезаписи до команды</td><td>Не запускать; создать отдельного кандидата</td></tr><tr><td>Restore завершился, но таблица пуста</td><td>Неполный scope, неверная версия или слабый check</td><td>Выполнить read-only запросы к кандидату и сравнить expected rows</td><td>Зафиксировать отрицательный verdict и исправить договор</td></tr></tbody></table></div>\n<h2>Конкретный маршрут для PostgreSQL</h2>\n<p>Для учебного custom archive можно сначала получить список объектов, не меняя базу. Команда <code>pg_restore --list</code> показывает, что инструмент видит внутри non-plain archive. Этот список нужно сравнить с manifest, а не с памятью оператора. Если ожидалась <code>reference.codes</code>, а её нет, повторный запуск restore не добавит объект.</p>\n<pre><code># Учебные имена. Команды не выполнялись этой статьёй.\nsha256sum training-catalog.dump\npg_restore --list training-catalog.dump\n\n# Только после проверок — отдельный кандидат.\ncreatedb training_restore_candidate\npg_restore --dbname=training_restore_candidate training-catalog.dump</code></pre>\n<p>Перед последней командой нужно подтвердить, что <code>training_restore_candidate</code> не является источником и не содержит нужных рабочих данных. Если безопасной цели нет, правильный результат — «restore не проверен». Нельзя превращать отсутствие стенда в разрешение на риск.</p>\n<p>После восстановления проверяют не только код возврата процесса. Нужны конкретные read-only проверки: существуют ли ожидаемые схемы, совпадает ли набор relations, имеют ли таблицы ожидаемый тип и не пусты ли обязательные справочники. Количество строк — учебный критерий, а не обещание для настоящей базы. В production оно меняется, поэтому ожидаемое значение должно зависеть от снимка и бизнес-условия.</p>\n<h2>Порядок действий</h2>\n<ol><li>Назвать backup ID и прочитать manifest целиком: формат, файл, scope, исключения, digest, цель и checks.</li><li>Сверить область восстановления с вопросом. Если нужная схема не включена, назвать это пробелом дизайна, а не ошибкой <code>pg_restore</code>.</li><li>Вычислить SHA-256 фактического файла. При несовпадении остановить маршрут до просмотра содержимого.</li><li>Получить список архива через <code>pg_restore --list</code> и сравнить его с ожидаемыми relations.</li><li>Подтвердить изолированную базу-кандидат и запретить перезапись источника. Проверку цели выполнить до команды restore.</li><li>Восстановить архив только в кандидата. Сохранить время начала, время окончания, версию инструмента и итог команды.</li><li>Выполнить структурные и смысловые read-only checks. Сравнить фактический результат с manifest, не меняя expected values после факта.</li><li>Записать непокрытые области: роли, tablespaces, внешние файлы, журналы изменений, требования к потере данных и допустимое время восстановления.</li></ol>\n<h2>Отрицательный путь важнее зелёного</h2>\n<p>Хорошая проверка должна остановиться на плохом входе. Изменённый файл должен отклоняться по checksum. Суженный scope должен отклоняться по сравнению manifest и списка объектов. Источник должен быть недопустимой целью. Пустая relation должна приводить к отрицательному verdict, если приложение требует данные.</p>\n<p>Нельзя исправлять отрицательный результат подменой evidence. Не следует менять хеш, чтобы пройти gate, добавлять строки вручную после восстановления или переписывать expected count после обнаружения расхождения. Эти действия скрывают причину и делают следующий запуск менее надёжным.</p>\n<h2>Ограничения метода</h2>\n<p>Логический dump PostgreSQL покрывает не все способы восстановления. Копирование файлов кластера, continuous archiving и point-in-time recovery имеют другие предпосылки. Команда, которая проверила custom archive одной базы, не доказала восстановление всего кластера и не измерила готовность приложения.</p>\n<p>Учебный сценарий не проверяет сеть, права, секреты, размер production-данных, скорость передачи, репликацию, WAL, внешние object storage и работу зависимых сервисов. Он также не даёт RPO или RTO. Время учебной команды становится RTO только после измерения на разрешённой цели с описанными условиями. Дата архива сама по себе не является RPO.</p>\n<p>Если drill не может затронуть реальное окружение, это не недостаток статьи. Нужно отделить проверенный учебный контракт от непроверенной инфраструктуры и назначить следующий безопасный эксперимент. Такой итог точнее, чем заявление о полной готовности к аварии.</p>\n<h2>Критерий готовности</h2>\n<p>Минимальный проверяемый критерий выглядит так: команда предъявляет manifest и фактический archive; SHA-256 совпадает; список объектов покрывает заявленный scope; restore выполнен только в изолированной цели; источник не изменился; структурные и смысловые checks прошли; время и версия инструмента записаны; непокрытые области явно перечислены.</p>\n<p>Если хотя бы один пункт неизвестен, verdict должен быть ограниченным: «архив найден», «байты совпали» или «учебная база восстановилась». Нельзя сокращать его до «резервное копирование работает». Копия становится рабочим механизмом только там, где команда может повторить путь, увидеть отрицательный результат и безопасно остановиться.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href='https://www.postgresql.org/docs/current/backup.html' target='_blank' rel='noopener noreferrer'>PostgreSQL: Backup and Restore</a> — официально разделяет логические dump, файловое копирование и continuous archiving.</li><li><a href='https://www.postgresql.org/docs/current/app-pgrestore.html' target='_blank' rel='noopener noreferrer'>PostgreSQL: pg_restore</a> — описывает просмотр и восстановление архивов, созданных в форматах pg_dump.</li><li><a href='https://csrc.nist.gov/pubs/fips/180-4/final' target='_blank' rel='noopener noreferrer'>NIST FIPS 180-4: Secure Hash Standard</a> — официальный стандарт семейства SHA-2; digest проверяет bytes, но не семантическую полноту восстановления.</li></ul>"
|
||
}
|