fix: persist media decisions across production restores
This commit is contained in:
+3
-3
@@ -116,20 +116,20 @@ curl -fsS https://rf4spotter.ru/ready
|
||||
|
||||
## 6. Резервное копирование и восстановление
|
||||
|
||||
Храните копии вне диска приложения. Скрипт создаёт PostgreSQL dump, архив MinIO и контрольные суммы в новом каталоге с UTC-временем:
|
||||
Храните копии вне диска приложения. Скрипт создаёт PostgreSQL dump, архив MinIO, архив persistent media store и контрольные суммы в новом каталоге с UTC-временем. Media manifest и файлы решений восстанавливаются вместе с provenance:
|
||||
|
||||
```bash
|
||||
./deploy/backup.sh /srv/rf4-backups
|
||||
```
|
||||
|
||||
Восстановление заменяет содержимое PostgreSQL и MinIO данными из выбранной копии, временно останавливая API и web. Это намеренно защищённая подтверждением операция:
|
||||
Восстановление заменяет содержимое PostgreSQL, MinIO и persistent media store данными из выбранной копии, временно останавливая API и web. Это намеренно защищённая подтверждением операция:
|
||||
|
||||
```bash
|
||||
CONFIRM_RESTORE=rf4-spotter ./deploy/restore.sh /srv/rf4-backups/20260906T120000Z
|
||||
curl -fsS https://rf4spotter.ru/ready
|
||||
```
|
||||
|
||||
Репозиторий содержит изолированный drill, который создаёт отдельные Compose volumes, портит тестовые данные, восстанавливает их и сравнивает PostgreSQL и MinIO:
|
||||
Репозиторий содержит изолированный drill, который создаёт отдельные Compose volumes, портит тестовые данные, восстанавливает их и сравнивает PostgreSQL, MinIO и media store:
|
||||
|
||||
```bash
|
||||
./deploy/test-backup-restore.sh
|
||||
|
||||
+7
-1
@@ -21,6 +21,9 @@ fi
|
||||
mkdir -p "$target"
|
||||
chmod 700 "$target"
|
||||
export BACKUP_DIRECTORY="$backup_root"
|
||||
media_tmp=$(mktemp -d "$backup_root/.rf4-media-backup.XXXXXX")
|
||||
cleanup() { rm -rf "$media_tmp"; }
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
env_file=${COMPOSE_ENV_FILE:-.env.production}
|
||||
compose="docker compose --env-file $env_file -f compose.production.yaml"
|
||||
@@ -28,6 +31,9 @@ $compose exec -T db sh -c 'pg_dump --format=custom --no-owner --no-acl -U "$POST
|
||||
$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 stat "local/$S3_BUCKET" >/dev/null && mc mirror --overwrite "local/$S3_BUCKET" "/backup/'"$stamp"'/minio" && chmod -R a+rwX "/backup/'"$stamp"'/minio"'
|
||||
tar -czf "$target/minio.tar.gz" -C "$target" minio
|
||||
rm -rf "$target/minio"
|
||||
(cd "$target" && sha256sum postgres.dump minio.tar.gz > SHA256SUMS)
|
||||
$compose cp api:/app/runtime-media/. "$media_tmp/"
|
||||
tar -czf "$target/media.tar.gz" -C "$media_tmp" .
|
||||
(cd "$target" && sha256sum postgres.dump minio.tar.gz media.tar.gz > SHA256SUMS)
|
||||
chmod 600 "$target/postgres.dump" "$target/minio.tar.gz" "$target/SHA256SUMS"
|
||||
chmod 600 "$target/media.tar.gz"
|
||||
echo "Backup created: $target"
|
||||
|
||||
@@ -14,12 +14,15 @@ esac
|
||||
source_dir=${1%/}
|
||||
test -f "$source_dir/postgres.dump"
|
||||
test -f "$source_dir/minio.tar.gz"
|
||||
test -f "$source_dir/media.tar.gz"
|
||||
(cd "$source_dir" && sha256sum -c SHA256SUMS)
|
||||
backup_root=$(dirname "$source_dir")
|
||||
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"
|
||||
mkdir -p "$restore_tmp/media"
|
||||
tar -xzf "$source_dir/media.tar.gz" -C "$restore_tmp/media"
|
||||
restore_name=$(basename "$restore_tmp")
|
||||
|
||||
env_file=${COMPOSE_ENV_FILE:-.env.production}
|
||||
@@ -27,5 +30,6 @@ 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 run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null && mc stat "local/$S3_BUCKET" >/dev/null && mc rm --recursive --force "local/$S3_BUCKET" >/dev/null 2>&1 && mc mirror --overwrite "/backup/'"$restore_name"'/minio" "local/$S3_BUCKET"'
|
||||
tar -cf - -C "$restore_tmp/media" . | $compose run --rm --no-deps -T api sh -c 'find /app/runtime-media -mindepth 1 -delete && tar -xf - -C /app/runtime-media'
|
||||
$compose up -d ${RESTORE_START_SERVICES:-minio api web proxy}
|
||||
echo "Restore completed from: $source_dir"
|
||||
|
||||
@@ -8,6 +8,8 @@ project="rf4-restore-drill-$$"
|
||||
export COMPOSE_PROJECT_NAME="$project"
|
||||
export COMPOSE_ENV_FILE=.env.production.example
|
||||
export BACKUP_DIRECTORY="$drill_root"
|
||||
export POSTGRES_PASSWORD=restore-drill-postgres-password
|
||||
export DATABASE_URL=postgresql+psycopg://rf4:restore-drill-postgres-password@db:5432/rf4_spotter
|
||||
compose="docker compose --env-file $COMPOSE_ENV_FILE -f compose.production.yaml"
|
||||
|
||||
cleanup() {
|
||||
@@ -18,8 +20,11 @@ trap cleanup EXIT INT TERM
|
||||
|
||||
$compose up -d --wait db minio
|
||||
$compose run --rm minio-init
|
||||
$compose run --rm media-init
|
||||
$compose up -d --wait api
|
||||
$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 stat "local/$S3_BUCKET" >/dev/null; printf object-ok | mc pipe "local/$S3_BUCKET/restore-drill.txt"' >/dev/null
|
||||
$compose exec -T api sh -c 'printf media-ok > /app/runtime-media/restore-drill.txt'
|
||||
|
||||
./deploy/backup.sh "$drill_root"
|
||||
backup=$(find "$drill_root" -mindepth 1 -maxdepth 1 -type d ! -name '.rf4-restore.*' | head -n 1)
|
||||
@@ -29,6 +34,8 @@ $compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http:
|
||||
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"')
|
||||
media_value=$($compose run --rm --no-deps api sh -c 'cat /app/runtime-media/restore-drill.txt')
|
||||
test "$db_value" = "database-ok"
|
||||
test "$object_value" = "object-ok"
|
||||
echo "Backup/restore drill passed for PostgreSQL and MinIO"
|
||||
test "$media_value" = "media-ok"
|
||||
echo "Backup/restore drill passed for PostgreSQL, MinIO, and media decisions"
|
||||
|
||||
@@ -24,6 +24,10 @@ trap cleanup EXIT INT TERM
|
||||
$compose up --build -d --wait db minio api web
|
||||
curl -fsS "http://127.0.0.1:$BOOTSTRAP_API_PORT/ready" >/dev/null
|
||||
curl -fsS "http://127.0.0.1:$BOOTSTRAP_WEB_PORT/" >/dev/null
|
||||
manifest_digest=$($compose exec -T api sha256sum /app/runtime-media/manifest.json | awk '{print $1}')
|
||||
test -n "$manifest_digest"
|
||||
$compose up -d --force-recreate --wait api
|
||||
test "$manifest_digest" = "$($compose exec -T api sha256sum /app/runtime-media/manifest.json | awk '{print $1}')"
|
||||
curl -fsS -D - -o /dev/null "http://127.0.0.1:$BOOTSTRAP_API_PORT/health" | grep -qi '^x-frame-options: DENY'
|
||||
curl -fsS -D - -o /dev/null "http://127.0.0.1:$BOOTSTRAP_API_PORT/health" | grep -qi '^cross-origin-opener-policy: same-origin'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user