feat: verify production backup restore
This commit is contained in:
@@ -124,11 +124,15 @@ services:
|
|||||||
limits: {cpus: "0.75", memory: 512M}
|
limits: {cpus: "0.75", memory: 512M}
|
||||||
|
|
||||||
storage-tool:
|
storage-tool:
|
||||||
image: alpine:3.22
|
image: minio/mc:RELEASE.2025-07-21T05-28-08Z
|
||||||
profiles: [tools]
|
profiles: [tools]
|
||||||
|
environment:
|
||||||
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY:?Set S3_ACCESS_KEY}
|
||||||
|
S3_SECRET_KEY: ${S3_SECRET_KEY:?Set S3_SECRET_KEY}
|
||||||
|
S3_BUCKET: ${S3_BUCKET:-catch-screenshots}
|
||||||
volumes:
|
volumes:
|
||||||
- minio_data:/data
|
|
||||||
- ${BACKUP_DIRECTORY:-./backups}:/backup
|
- ${BACKUP_DIRECTORY:-./backups}:/backup
|
||||||
|
networks: [backend]
|
||||||
security_opt: [no-new-privileges:true]
|
security_opt: [no-new-privileges:true]
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
+7
-2
@@ -80,11 +80,16 @@ CONFIRM_RESTORE=rf4-spotter ./deploy/restore.sh /srv/rf4-backups/20260906T120000
|
|||||||
curl -fsS https://rf4spotter.ru/ready
|
curl -fsS https://rf4spotter.ru/ready
|
||||||
```
|
```
|
||||||
|
|
||||||
Перед приглашением пользователей проведите учебное восстановление на отдельном сервере или с отдельным Compose project name. Одной только успешной архивации недостаточно. После проверки настройте ежедневный запуск `backup.sh`, шифрование/выгрузку копий во внешнее хранилище и уведомление при ошибке; храните минимум 7 ежедневных и 4 еженедельных копии.
|
Репозиторий содержит изолированный drill, который создаёт отдельные Compose volumes, портит тестовые данные, восстанавливает их и сравнивает PostgreSQL и MinIO:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./deploy/test-backup-restore.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Drill успешно пройден 6 сентября 2026 года. На целевом сервере всё равно проведите учебное восстановление с реальной зашифрованной копией перед приглашением пользователей. Затем настройте ежедневный запуск `backup.sh`, выгрузку копий во внешнее хранилище и уведомление при ошибке; храните минимум 7 ежедневных и 4 еженедельных копии.
|
||||||
|
|
||||||
## 7. Что ещё блокирует приглашение альфа-пользователей
|
## 7. Что ещё блокирует приглашение альфа-пользователей
|
||||||
|
|
||||||
- учебное восстановление PostgreSQL и MinIO на изолированном контуре;
|
|
||||||
- проверка полного bootstrap на пустых volumes;
|
- проверка полного bootstrap на пустых volumes;
|
||||||
- политика хранения и удаления пользовательских данных;
|
- политика хранения и удаления пользовательских данных;
|
||||||
- базовый мониторинг `/ready`, диска и срока TLS-сертификата.
|
- базовый мониторинг `/ready`, диска и срока TLS-сертификата.
|
||||||
|
|||||||
+10
-3
@@ -14,13 +14,20 @@ esac
|
|||||||
backup_root=${1%/}
|
backup_root=${1%/}
|
||||||
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
stamp=$(date -u +%Y%m%dT%H%M%SZ)
|
||||||
target="$backup_root/$stamp"
|
target="$backup_root/$stamp"
|
||||||
|
if [ -e "$target" ]; then
|
||||||
|
echo "Backup target already exists: $target" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
mkdir -p "$target"
|
mkdir -p "$target"
|
||||||
chmod 700 "$target"
|
chmod 700 "$target"
|
||||||
export BACKUP_DIRECTORY="$backup_root"
|
export BACKUP_DIRECTORY="$backup_root"
|
||||||
|
|
||||||
compose="docker compose --env-file .env.production -f compose.production.yaml"
|
env_file=${COMPOSE_ENV_FILE:-.env.production}
|
||||||
|
compose="docker compose --env-file $env_file -f compose.production.yaml"
|
||||||
$compose exec -T db sh -c 'pg_dump --format=custom --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > "$target/postgres.dump"
|
$compose exec -T db sh -c 'pg_dump --format=custom --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB"' > "$target/postgres.dump"
|
||||||
$compose run --rm storage-tool tar -czf "/backup/$stamp/minio.tar.gz" -C /data .
|
$compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null && mc mb --ignore-existing "local/$S3_BUCKET" >/dev/null && mc mirror --overwrite "local/$S3_BUCKET" "/backup/'"$stamp"'/minio" && chmod -R a+rwX "/backup/'"$stamp"'/minio"'
|
||||||
sha256sum "$target/postgres.dump" "$target/minio.tar.gz" > "$target/SHA256SUMS"
|
tar -czf "$target/minio.tar.gz" -C "$target" minio
|
||||||
|
rm -rf "$target/minio"
|
||||||
|
(cd "$target" && sha256sum postgres.dump minio.tar.gz > SHA256SUMS)
|
||||||
chmod 600 "$target/postgres.dump" "$target/minio.tar.gz" "$target/SHA256SUMS"
|
chmod 600 "$target/postgres.dump" "$target/minio.tar.gz" "$target/SHA256SUMS"
|
||||||
echo "Backup created: $target"
|
echo "Backup created: $target"
|
||||||
|
|||||||
+9
-5
@@ -16,12 +16,16 @@ test -f "$source_dir/postgres.dump"
|
|||||||
test -f "$source_dir/minio.tar.gz"
|
test -f "$source_dir/minio.tar.gz"
|
||||||
(cd "$source_dir" && sha256sum -c SHA256SUMS)
|
(cd "$source_dir" && sha256sum -c SHA256SUMS)
|
||||||
backup_root=$(dirname "$source_dir")
|
backup_root=$(dirname "$source_dir")
|
||||||
stamp=$(basename "$source_dir")
|
|
||||||
export BACKUP_DIRECTORY="$backup_root"
|
export BACKUP_DIRECTORY="$backup_root"
|
||||||
|
restore_tmp=$(mktemp -d "$backup_root/.rf4-restore.XXXXXX")
|
||||||
|
trap 'rm -rf "$restore_tmp"' EXIT INT TERM
|
||||||
|
tar -xzf "$source_dir/minio.tar.gz" -C "$restore_tmp"
|
||||||
|
restore_name=$(basename "$restore_tmp")
|
||||||
|
|
||||||
compose="docker compose --env-file .env.production -f compose.production.yaml"
|
env_file=${COMPOSE_ENV_FILE:-.env.production}
|
||||||
$compose stop api web minio
|
compose="docker compose --env-file $env_file -f compose.production.yaml"
|
||||||
|
$compose stop api web
|
||||||
$compose exec -T db sh -c 'pg_restore --clean --if-exists --exit-on-error --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < "$source_dir/postgres.dump"
|
$compose exec -T db sh -c 'pg_restore --clean --if-exists --exit-on-error --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB"' < "$source_dir/postgres.dump"
|
||||||
$compose run --rm storage-tool sh -c 'find /data -mindepth 1 -delete && tar -xzf "/backup/'"$stamp"'/minio.tar.gz" -C /data'
|
$compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null && mc rm --recursive --force "local/$S3_BUCKET" >/dev/null 2>&1 || true; mc mb --ignore-existing "local/$S3_BUCKET" >/dev/null && mc mirror --overwrite "/backup/'"$restore_name"'/minio" "local/$S3_BUCKET"'
|
||||||
$compose up -d minio api web proxy
|
$compose up -d ${RESTORE_START_SERVICES:-minio api web proxy}
|
||||||
echo "Restore completed from: $source_dir"
|
echo "Restore completed from: $source_dir"
|
||||||
|
|||||||
Executable
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||||
|
cd "$repo"
|
||||||
|
drill_root=$(mktemp -d /tmp/rf4-restore-drill.XXXXXX)
|
||||||
|
project="rf4-restore-drill-$$"
|
||||||
|
export COMPOSE_PROJECT_NAME="$project"
|
||||||
|
export COMPOSE_ENV_FILE=.env.production.example
|
||||||
|
export BACKUP_DIRECTORY="$drill_root"
|
||||||
|
compose="docker compose --env-file $COMPOSE_ENV_FILE -f compose.production.yaml"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
$compose down --volumes --remove-orphans >/dev/null 2>&1 || true
|
||||||
|
rm -rf "$drill_root"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
$compose up -d --wait db minio
|
||||||
|
$compose exec -T db sh -c 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "create table restore_drill (value text primary key); insert into restore_drill values ('"'"'database-ok'"'"');"' >/dev/null
|
||||||
|
$compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null; mc mb --ignore-existing "local/$S3_BUCKET" >/dev/null; printf object-ok | mc pipe "local/$S3_BUCKET/restore-drill.txt"' >/dev/null
|
||||||
|
|
||||||
|
./deploy/backup.sh "$drill_root"
|
||||||
|
backup=$(find "$drill_root" -mindepth 1 -maxdepth 1 -type d ! -name '.rf4-restore.*' | head -n 1)
|
||||||
|
$compose exec -T db sh -c 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "drop table restore_drill;"' >/dev/null
|
||||||
|
$compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null; mc rm --force "local/$S3_BUCKET/restore-drill.txt"' >/dev/null
|
||||||
|
|
||||||
|
CONFIRM_RESTORE=rf4-spotter RESTORE_START_SERVICES=minio ./deploy/restore.sh "$backup"
|
||||||
|
db_value=$($compose exec -T db sh -c 'psql -At -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select value from restore_drill;"')
|
||||||
|
object_value=$($compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null; mc cat "local/$S3_BUCKET/restore-drill.txt"')
|
||||||
|
test "$db_value" = "database-ok"
|
||||||
|
test "$object_value" = "object-ok"
|
||||||
|
echo "Backup/restore drill passed for PostgreSQL and MinIO"
|
||||||
+2
-2
@@ -66,7 +66,7 @@
|
|||||||
- [ ] Провести security-проверку admin-аутентификации, CORS, security headers, загрузок и управления секретами; вынести допустимые origins в конфигурацию и исключить демонстрационные секреты в production-режиме.
|
- [ ] Провести security-проверку admin-аутентификации, CORS, security headers, загрузок и управления секретами; вынести допустимые origins в конфигурацию и исключить демонстрационные секреты в production-режиме.
|
||||||
- [x] Проверить авторизацию повторной загрузки скриншота: используется отдельный одноразовый случайный токен, в БД хранится только SHA-256, UUID заявки недостаточно.
|
- [x] Проверить авторизацию повторной загрузки скриншота: используется отдельный одноразовый случайный токен, в БД хранится только SHA-256, UUID заявки недостаточно.
|
||||||
- [ ] Определить сроки хранения ников, исходных payload, staging-наблюдений, moderation events и submission attempts; добавить документированную очистку/анонимизацию.
|
- [ ] Определить сроки хранения ников, исходных payload, staging-наблюдений, moderation events и submission attempts; добавить документированную очистку/анонимизацию.
|
||||||
- [ ] Добавить резервное копирование и документированное восстановление PostgreSQL и MinIO: скрипты, контрольные суммы и runbook готовы; остаётся учебное восстановление на отдельных временных volumes.
|
- [x] Добавить резервное копирование и документированное восстановление PostgreSQL и MinIO: консистентные `pg_dump` и MinIO API mirror, контрольные суммы, runbook и успешный изолированный drill с намеренным удалением данных (6 сентября 2026).
|
||||||
- [ ] Проверить доступность интерфейса: клавиатура, focus states, контраст, подписи полей и семантика таблиц/карточек.
|
- [ ] Проверить доступность интерфейса: клавиатура, focus states, контраст, подписи полей и семантика таблиц/карточек.
|
||||||
- [ ] Провести Lighthouse-проверку основных страниц и устранить критические проблемы производительности.
|
- [ ] Провести Lighthouse-проверку основных страниц и устранить критические проблемы производительности.
|
||||||
- [x] Провести UI/UX-аудит desktop/mobile и сформировать приоритетный план (`docs/UI_UX_AUDIT.md`).
|
- [x] Провести UI/UX-аудит desktop/mobile и сформировать приоритетный план (`docs/UI_UX_AUDIT.md`).
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
- [ ] Добавить smoke-проверку административной очереди внешних источников на desktop/mobile без публикации реальных записей.
|
- [ ] Добавить smoke-проверку административной очереди внешних источников на desktop/mobile без публикации реальных записей.
|
||||||
- [ ] Обновить README: архитектура, все переменные окружения, импорт, модерация, backup/restore, эксплуатация логов и известные ограничения.
|
- [ ] Обновить README: архитектура, все переменные окружения, импорт, модерация, backup/restore, эксплуатация логов и известные ограничения.
|
||||||
- [ ] Выбрать лицензию кода и политику использования данных.
|
- [ ] Выбрать лицензию кода и политику использования данных.
|
||||||
- [ ] Завершить production-профиль для `rf4spotter.ru`: Compose, Caddy/TLS, закрытые внутренние сервисы, CORS, resource limits, fail-fast секреты и runbook добавлены; остаются backup/restore и проверка на целевом сервере.
|
- [ ] Завершить production-профиль для `rf4spotter.ru`: Compose, Caddy/TLS, закрытые внутренние сервисы, CORS, resource limits, fail-fast секреты, backup/restore и runbook добавлены; остаются полный bootstrap и проверка на целевом сервере.
|
||||||
- [ ] Заменить демонстрационные секреты и определить целевое размещение перед внешней публикацией.
|
- [ ] Заменить демонстрационные секреты и определить целевое размещение перед внешней публикацией.
|
||||||
|
|
||||||
## Источники данных и согласование
|
## Источники данных и согласование
|
||||||
|
|||||||
Reference in New Issue
Block a user