chore(release): script database backup runbook
This commit is contained in:
@@ -31,7 +31,19 @@
|
||||
|
||||
## 3. 数据库备份
|
||||
|
||||
生产 Alembic 升级前必须创建数据库备份:
|
||||
生产 Alembic 升级前必须创建数据库备份。优先使用脚本:
|
||||
|
||||
```bash
|
||||
BACKUP_ROOT=/root/xiaoxia-backups \
|
||||
POSTGRES_CONTAINER=xiaoxia-postgres \
|
||||
POSTGRES_USER=xiaoxia \
|
||||
POSTGRES_DB=xiaoxia_saas \
|
||||
scripts/backup_postgres.sh
|
||||
```
|
||||
|
||||
脚本会输出备份目录,并写入 `manifest.txt` 与 `alembic_version.txt`。
|
||||
|
||||
手动等价命令:
|
||||
|
||||
```bash
|
||||
BACKUP_DIR=/root/xiaoxia-backups/$(date +%Y%m%d-%H%M%S)
|
||||
@@ -94,6 +106,12 @@ docker exec xiaoxia-postgres psql -U xiaoxia -d xiaoxia_saas -Atc 'select versio
|
||||
|
||||
## 7. 回滚策略
|
||||
|
||||
生成恢复计划(不会自动恢复数据库):
|
||||
|
||||
```bash
|
||||
scripts/restore_postgres_plan.sh /root/xiaoxia-backups/<backup-dir>
|
||||
```
|
||||
|
||||
应用回滚:
|
||||
|
||||
- 若数据库未发生不可逆迁移,优先回滚到上一稳定 tag 并重新部署。
|
||||
@@ -104,7 +122,7 @@ docker exec xiaoxia-postgres psql -U xiaoxia -d xiaoxia_saas -Atc 'select versio
|
||||
- Alembic downgrade 只有在 revision 明确支持且已验证时才能执行。
|
||||
- 首次生产接入阶段默认使用备份恢复作为最终兜底。
|
||||
- 恢复前必须二次确认备份路径、目标数据库、停机窗口。
|
||||
- 恢复步骤需按生产数据库实际容器名、DB 名、volume 策略另写一次性 runbook;不要在紧急状态下即兴拼命令。
|
||||
- 恢复步骤必须先由 `scripts/restore_postgres_plan.sh` 生成并复核,不允许紧急状态下即兴拼命令。
|
||||
|
||||
## 8. 发布记录
|
||||
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_ROOT="${BACKUP_ROOT:-/root/xiaoxia-backups}"
|
||||
POSTGRES_CONTAINER="${POSTGRES_CONTAINER:-xiaoxia-postgres}"
|
||||
POSTGRES_USER="${POSTGRES_USER:-xiaoxia}"
|
||||
POSTGRES_DB="${POSTGRES_DB:-xiaoxia_saas}"
|
||||
|
||||
TS="$(date +%Y%m%d-%H%M%S)"
|
||||
BACKUP_DIR="$BACKUP_ROOT/$TS"
|
||||
DUMP_PATH="$BACKUP_DIR/${POSTGRES_DB}.dump"
|
||||
VERSION_PATH="$BACKUP_DIR/alembic_version.txt"
|
||||
MANIFEST_PATH="$BACKUP_DIR/manifest.txt"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
docker exec "$POSTGRES_CONTAINER" pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc > "$DUMP_PATH"
|
||||
docker exec "$POSTGRES_CONTAINER" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc 'select version_num from alembic_version;' > "$VERSION_PATH" 2>/dev/null || true
|
||||
|
||||
if [ ! -s "$DUMP_PATH" ]; then
|
||||
echo "ERROR: backup dump is empty: $DUMP_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
{
|
||||
echo "created_at=$TS"
|
||||
echo "postgres_container=$POSTGRES_CONTAINER"
|
||||
echo "postgres_user=$POSTGRES_USER"
|
||||
echo "postgres_db=$POSTGRES_DB"
|
||||
echo "dump_path=$DUMP_PATH"
|
||||
echo "alembic_version=$(cat "$VERSION_PATH" 2>/dev/null || true)"
|
||||
echo "dump_size_bytes=$(wc -c < "$DUMP_PATH")"
|
||||
} > "$MANIFEST_PATH"
|
||||
|
||||
ls -lh "$DUMP_PATH"
|
||||
echo "OK backup created: $BACKUP_DIR"
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_DIR="${1:-}"
|
||||
POSTGRES_CONTAINER="${POSTGRES_CONTAINER:-xiaoxia-postgres}"
|
||||
POSTGRES_USER="${POSTGRES_USER:-xiaoxia}"
|
||||
POSTGRES_DB="${POSTGRES_DB:-xiaoxia_saas}"
|
||||
|
||||
if [ -z "$BACKUP_DIR" ]; then
|
||||
echo "Usage: $0 <backup-dir>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
DUMP_PATH="$BACKUP_DIR/${POSTGRES_DB}.dump"
|
||||
MANIFEST_PATH="$BACKUP_DIR/manifest.txt"
|
||||
|
||||
if [ ! -s "$DUMP_PATH" ]; then
|
||||
echo "ERROR: backup dump not found or empty: $DUMP_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
Restore plan for $POSTGRES_DB
|
||||
|
||||
Backup directory:
|
||||
$BACKUP_DIR
|
||||
|
||||
Manifest:
|
||||
$(cat "$MANIFEST_PATH" 2>/dev/null || echo ' <missing manifest>')
|
||||
|
||||
Manual restore commands (requires maintenance window):
|
||||
|
||||
docker stop xiaoxia-api-production xiaoxia-worker-production || true
|
||||
docker exec $POSTGRES_CONTAINER pg_restore -U $POSTGRES_USER -d $POSTGRES_DB --clean --if-exists --no-owner --no-privileges < $DUMP_PATH
|
||||
docker start xiaoxia-api-production xiaoxia-worker-production
|
||||
docker exec $POSTGRES_CONTAINER psql -U $POSTGRES_USER -d $POSTGRES_DB -Atc 'select version_num from alembic_version;'
|
||||
|
||||
This script intentionally does not execute restore automatically.
|
||||
EOF
|
||||
@@ -0,0 +1,20 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_restore_postgres_plan_is_non_destructive():
|
||||
script = Path("scripts/restore_postgres_plan.sh").read_text(encoding="utf-8")
|
||||
executable_prefix = script.split("cat <<EOF", 1)[0]
|
||||
|
||||
assert "pg_restore" in script
|
||||
assert "This script intentionally does not execute restore automatically." in script
|
||||
assert "pg_restore" not in executable_prefix
|
||||
|
||||
|
||||
def test_backup_postgres_writes_manifest_and_version():
|
||||
script = Path("scripts/backup_postgres.sh").read_text(encoding="utf-8")
|
||||
|
||||
assert "pg_dump" in script
|
||||
assert "alembic_version.txt" in script
|
||||
assert "manifest.txt" in script
|
||||
assert "test -s" not in script
|
||||
assert 'if [ ! -s "$DUMP_PATH" ]' in script
|
||||
Reference in New Issue
Block a user