Qadam Roadmap
проектdocs/operations/backup-restore-runbook.md

Runbook резервного копирования и восстановления

Обновлён 1 апр. 2026 г., 12:41 · 0 комментариев

Runbook резервного копирования и восстановления

Паспорт документа

  • Статус документа: working reference
  • Актуально на: 31 марта 2026 года
  • Владелец: backend/platform-команда
  • Пересмотр: при изменении runtime topology, storage-модели, путей данных или стратегии backup
  • Область применения: текущий production-контур Qadam на host-based runtime, автоматизированном backup baseline и off-host retention
  • Связанные документы:

Цель документа

Этот документ фиксирует канонический backup/restore-контур Qadam для текущего production-сервера. На сегодня базовый backup уже автоматизирован через qadam-backup.service и qadam-backup.timer, но restore по-прежнему остаётся осознанной операционной процедурой, которую нужно выполнять аккуратно и вручную.

1. Текущий production backup-контур

  • Автоматический backup запускается через qadam-backup.service.
  • Планировщик работает через qadam-backup.timer ежедневно в 03:30 UTC с небольшим RandomizedDelaySec.
  • Канонический исполняемый runtime находится в /data/qadam-core/scripts/backup-runtime.mjs.
  • Локальные snapshot-каталоги хранятся в /var/backups/qadam/<STAMP>.
  • Последний результат backup фиксируется в /var/lib/qadam-backup/state.json.
  • Off-host archive выгружается в S3-compatible storage по префиксу backups/runtime/<STAMP>.tar.gz.
  • Текущая retention policy:
    • локальные snapshot-каталоги: 7 дней;
    • off-host archive в object storage: 30 дней.
  • Telegram alerting работает без лишнего шума: backup шлёт сообщение при первом переходе в FAIL и отдельное RECOVERY после следующего успешного прогона; обычные success-run'ы молчат, если специально не включён QADAM_BACKUP_NOTIFY_ON_SUCCESS=true.

2. Что обязательно защищать

Данные PostgreSQL

  • Канонический источник: DATABASE_URL из /etc/qadam/qadam.env
  • Текущий production runtime использует локальный PostgreSQL на хосте
  • Критичность: максимальная

API uploads

  • Каталог: /var/lib/qadam-core/uploads/images
  • Содержимое: изображения, загруженные через POST /api/v1/upload/image
  • Критичность: высокая

Данные roadmap-сервиса

  • Загруженные markdown-файлы: /var/lib/qadam-roadmap/uploads
  • Комментарии: /var/lib/qadam-roadmap/comments.json
  • Feedback overlay frontend-команды: /var/lib/qadam-roadmap/frontend-package-feedback.json
  • Критичность: средняя

Конфигурация runtime

  • /etc/qadam/qadam.env
  • /etc/qadam/qadam-roadmap.env
  • /etc/qadam/qadam-monitor.env
  • /etc/qadam/qadam-backup.env
  • /etc/systemd/system/qadam-api.service
  • /etc/systemd/system/qadam-web.service
  • /etc/systemd/system/qadam-roadmap.service
  • /etc/systemd/system/qadam-monitor.service
  • /etc/systemd/system/qadam-monitor.timer
  • /etc/systemd/system/qadam-backup.service
  • /etc/systemd/system/qadam-backup.timer
  • /etc/nginx/sites-available/qadam.2fab.app.conf
  • /etc/nginx/sites-available/qadam-roadmap.2fab.app.conf
  • Критичность: высокая

3. Когда backup обязателен

  • Перед любой Prisma migration на production, даже если ночной backup уже работает по timer.
  • Перед релизом, который меняет auth, registration, billing, upload/storage или другую критичную бизнес-логику.
  • Перед ручным ремонтом PostgreSQL, Redis, systemd или nginx.
  • Перед изменением storage paths, env-файлов или TLS-контура.
  • Перед rollback, если текущее состояние уже могло изменить данные.
  • После существенного изменения backup policy, env-модели или storage account boundaries.

4. Состав backup-пакета

Каждый backup-пакет должен содержать:

  1. postgres.dump — custom-format dump production БД.
  2. api-uploads.tar.gz — архив API uploads.
  3. roadmap-uploads.tar.gz — архив roadmap uploads.
  4. comments.json — comments storage roadmap-сервиса.
  5. frontend-package-feedback.json — operational overlay со статусами frontend adoption.
  6. metadata.json — служебное описание snapshot, host и off-host object key.
  7. Копии актуальных env, systemd и nginx файлов в config/.
  8. SHA256SUMS — контрольные суммы файлов backup-пакета.

5. Как запускать backup

Канонический способ

systemctl start qadam-backup.service
systemctl status qadam-backup.service --no-pager
cat /var/lib/qadam-backup/state.json

Ручной запуск тем же runtime без systemd

set -a
source /etc/qadam/qadam.env
source /etc/qadam/qadam-backup.env
set +a

cd /data/qadam-core
pnpm backup:run

Что делает скрипт автоматически

  • снимает pg_dump production PostgreSQL;
  • архивирует API uploads и roadmap uploads;
  • копирует roadmap comments и frontend feedback overlay;
  • копирует актуальные env, systemd и nginx файлы;
  • пишет metadata.json и SHA256SUMS;
  • локально верифицирует dump через pg_restore --list;
  • локально проверяет контрольные суммы;
  • собирает off-host archive и выгружает его в S3-compatible storage;
  • подрезает локальные и off-host backup'ы по retention policy;
  • пишет последний status/result в /var/lib/qadam-backup/state.json.

6. Минимальная верификация backup

systemctl status qadam-backup.timer qadam-backup.service --no-pager
cat /var/lib/qadam-backup/state.json
find /var/backups/qadam -maxdepth 1 -mindepth 1 | sort

Минимум, который должен подтвердиться:

  • qadam-backup.timer находится в active (waiting);
  • последний qadam-backup.service завершился status=0/SUCCESS; состояние inactive (dead) после этого нормально, потому что сервис Type=oneshot;
  • в state.json есть status: "ok", backupDir и offsiteObjectKey;
  • каталог snapshot существует локально в /var/backups/qadam/<STAMP>;
  • off-host object существует в S3-compatible storage.

7. Процедура восстановления

До восстановления

  1. Зафиксировать причину restore и момент времени.
  2. Остановить запись в систему.
  3. Снять аварийный backup текущего состояния, если это ещё возможно.

Остановить приложения

systemctl stop qadam-web qadam-roadmap qadam-api

Если восстанавливаемся из off-host archive

  1. Скачать backups/runtime/<STAMP>.tar.gz из object storage любым S3-compatible client или через provider console.
  2. Развернуть архив в локальный backup root:
mkdir -p /var/backups/qadam
tar -xzf "/path/to/20260331T100420Z.tar.gz" -C /var/backups/qadam

После этого используется тот же локальный путь /var/backups/qadam/<STAMP>, как и для локального snapshot.

Восстановить PostgreSQL

set -a
source /etc/qadam/qadam.env
set +a

pg_restore \
  --clean \
  --if-exists \
  --no-owner \
  --no-privileges \
  --dbname="${DATABASE_URL}" \
  "/var/backups/qadam/<STAMP>/postgres.dump"

Восстановить файловые артефакты

rm -rf /var/lib/qadam-core/uploads/*
tar -xzf "/var/backups/qadam/<STAMP>/api-uploads.tar.gz" \
  -C /var/lib/qadam-core/uploads

rm -rf /var/lib/qadam-roadmap/uploads/*
tar -xzf "/var/backups/qadam/<STAMP>/roadmap-uploads.tar.gz" \
  -C /var/lib/qadam-roadmap/uploads

cp "/var/backups/qadam/<STAMP>/comments.json" /var/lib/qadam-roadmap/comments.json
cp "/var/backups/qadam/<STAMP>/frontend-package-feedback.json" \
  /var/lib/qadam-roadmap/frontend-package-feedback.json

Восстановить конфигурацию при необходимости

Если проблема затрагивала env, systemd или nginx, вернуть файлы из backup-пакета и затем выполнить:

systemctl daemon-reload
systemctl start qadam-api qadam-web qadam-roadmap
systemctl reload nginx

8. Проверка после восстановления

systemctl status qadam-api qadam-web qadam-roadmap nginx
curl -I https://qadam.2fab.app
curl https://qadam.2fab.app/api/v1/health
curl -I https://qadam.2fab.app/api/docs
curl -I -u <login>:<password> https://qadam-roadmap.2fab.app
curl -u <login>:<password> https://qadam-roadmap.2fab.app/api/health

Минимум, который должен подтвердиться:

  • qadam-api, qadam-web, qadam-roadmap, nginx находятся в active;
  • https://qadam.2fab.app/api/v1/health отвечает 200;
  • https://qadam-roadmap.2fab.app/api/health отвечает 200 после basic auth;
  • критичные пользовательские данные, uploads и roadmap feedback state на месте.

9. Ограничения текущей модели

  • Product data и roadmap storage по-прежнему сначала живут на том же хосте, что и runtime; backup лишь снижает риск потери, а не устраняет shared-failure domain.
  • Off-host backup использует те же object storage account boundaries, что и текущий S3-compatible storage для product media; отдельный backup-only bucket или отдельные credentials пока не выделены.
  • Restore регулярно не прогоняется на staging/backup-контуре, поэтому recovery-time по-настоящему подтверждён только частично.

10. Что ещё нужно улучшить

  • Вынести backup в отдельный bucket и отдельные credentials, если requirements по разделению доступов станут жёстче.
  • Добавить archive-level encryption, если backup-контур начнёт жить вне доверенного object storage perimeter.
  • Добавить регулярную проверку restore-процедуры на staging/backup-контуре.