Закрыта слепая зона «бэкапы есть, но восстановление не проверялось». - scripts/ops/pg_restore_verify.sh — берёт последний дамп из MinIO backups/pg/, restore в ЭФЕМЕРНЫЙ postgres:16, проверяет ключевые таблицы (users/documents/ tasks). Прод не трогает, всё в одноразовом контейнере. Под cron раз в неделю. - docs/DR-HA.md — runbook: бэкапы, проверка восстановления, потоковая репликация PG, Redis-реплика+Sentinel, таблица SPOF со статусом митигаций. Провижн реплик PG/Redis — на Proxmox (нужен новый LXC), это работа на железе, не в репозитории; runbook даёт конкретные шаги. Векторный SPOF уже снимается Qdrant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
72 lines
3.3 KiB
Bash
Executable File
72 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# DR-тест: проверка ВОССТАНОВИМОСТИ бэкапа PostgreSQL.
|
|
#
|
|
# Берёт последний дамп из MinIO (backups/pg/), поднимает ЭФЕМЕРНЫЙ контейнер
|
|
# postgres:16, восстанавливает в него дамп и проверяет, что ключевые таблицы не
|
|
# пусты. Прод не трогается — всё в одноразовом контейнере/сети, удаляются на выходе.
|
|
#
|
|
# Зачем: бэкапы делаются (cron на 1.38), но восстановление раньше никто не проверял —
|
|
# «непроверенный бэкап = отсутствие бэкапа». Запускать по cron раз в неделю.
|
|
#
|
|
# Требуется docker + доступ до MinIO. Креды берутся из .env (или окружения):
|
|
# MINIO_ENDPOINT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY,
|
|
# MINIO_BUCKET_BACKUPS (=backups), POSTGRES_DB, POSTGRES_USER.
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
if [ -f "$ROOT/.env" ]; then set -a; . "$ROOT/.env"; set +a; fi
|
|
|
|
: "${MINIO_ENDPOINT:?нужен MINIO_ENDPOINT}"
|
|
: "${MINIO_ACCESS_KEY:?нужен MINIO_ACCESS_KEY}"
|
|
: "${MINIO_SECRET_KEY:?нужен MINIO_SECRET_KEY}"
|
|
BUCKET="${MINIO_BUCKET_BACKUPS:-backups}"
|
|
PREFIX="${PG_BACKUP_PREFIX:-pg/}"
|
|
PGDB="${POSTGRES_DB:-antiplagiator}"
|
|
PGUSER="${POSTGRES_USER:-antiplagiator}"
|
|
|
|
SUFFIX="$$-$RANDOM"
|
|
PG="drtest-pg-$SUFFIX"
|
|
WORK="$(mktemp -d)"
|
|
MC_HOST="http://${MINIO_ACCESS_KEY}:${MINIO_SECRET_KEY}@${MINIO_ENDPOINT}"
|
|
|
|
cleanup() { docker rm -f "$PG" >/dev/null 2>&1 || true; rm -rf "$WORK"; }
|
|
trap cleanup EXIT
|
|
|
|
echo "▶ Поиск последнего дампа в minio://$BUCKET/$PREFIX"
|
|
LATEST="$(docker run --rm -e MC_HOST_dr="$MC_HOST" minio/mc:latest \
|
|
sh -c "mc ls --recursive dr/$BUCKET/$PREFIX | awk '{print \$NF}' | grep -E '\.(sql\.)?gz$' | sort | tail -1")"
|
|
[ -n "$LATEST" ] || { echo "❌ дампы не найдены"; exit 1; }
|
|
FNAME="$(basename "$LATEST")"
|
|
echo " последний: $FNAME"
|
|
|
|
echo "▶ Скачивание дампа"
|
|
docker run --rm -e MC_HOST_dr="$MC_HOST" -v "$WORK":/w minio/mc:latest \
|
|
sh -c "mc cp 'dr/$BUCKET/$PREFIX$FNAME' /w/dump.gz"
|
|
|
|
echo "▶ Эфемерный postgres:16"
|
|
docker run -d --name "$PG" \
|
|
-e POSTGRES_PASSWORD=drtest -e POSTGRES_DB="$PGDB" -e POSTGRES_USER="$PGUSER" \
|
|
postgres:16 >/dev/null
|
|
for _ in $(seq 1 30); do
|
|
docker exec "$PG" pg_isready -U "$PGUSER" -d "$PGDB" >/dev/null 2>&1 && break
|
|
sleep 1
|
|
done
|
|
|
|
echo "▶ Восстановление"
|
|
gunzip -c "$WORK/dump.gz" | docker exec -i "$PG" psql -v ON_ERROR_STOP=0 -U "$PGUSER" -d "$PGDB" >/dev/null 2>&1
|
|
|
|
echo "▶ Проверка ключевых таблиц"
|
|
rc=0
|
|
for tbl in users documents tasks; do
|
|
n="$(docker exec "$PG" psql -U "$PGUSER" -d "$PGDB" -tAc "SELECT count(*) FROM $tbl" 2>/dev/null | tr -d '[:space:]' || echo ERR)"
|
|
echo " $tbl: ${n:-ERR}"
|
|
{ [ -z "$n" ] || [ "$n" = "ERR" ]; } && rc=1
|
|
done
|
|
|
|
if [ "$rc" -eq 0 ]; then
|
|
echo "✅ Бэкап восстанавливается, ключевые таблицы на месте ($FNAME)"
|
|
else
|
|
echo "❌ Восстановление не прошло проверку — разобраться СРОЧНО"
|
|
exit 1
|
|
fi
|