PayrollManagementSystem/scripts/backup_full.sh
liuwenxin b5e27810fd feat(backup): mysqldump 定时备份/恢复演练/健康检查脚本与手册
- scripts/backup_full.sh:容器内 mysqldump --single-transaction,gzip 落盘
  db/backups/,保留 30 天;凭据 source docker/.env 不硬编码;实测备份成功
- scripts/restore_drill.sh:恢复到临时库 payroll_drill_* 并逐表核对行数
  (16 张关键表),默认不 DROP、--drop-exec 才删——绝不覆盖生产库;
  实测 16 表全 ✓。规避嵌套 shell 吞反引号(mysqladmin create/drop、
  双引号包裹表名)
- scripts/health_check.sh:六项检查(容器健康×2/磁盘水位/备份新鲜度/
  Redis 探活/24h 登录失败阈值),PASS/WARN/FAIL 报告,退出码供监控;
  Redis 密码经宿主机 .env 验证(容器内无该环境变量),实测六项全 PASS
- docs/备份恢复手册.md:备份/演练/生产恢复七步/定时 schtasks 两则,
  db/backups/ 已入 .gitignore

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 09:44:50 +08:00

59 lines
1.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# 衍水工资系统 —— MySQL 全量备份(⑨ 备份恢复与运维检查)
#
# 思路:在跑中的容器内执行 mysqldump,免暴露 3306 端口给宿主机工具,
# 也避免宿主机装 MySQL 客户端。凭据从 docker/.env 读取(不入脚本、不入版本库)。
#
# 用法:在项目根目录执行 bash scripts/backup_full.sh
# 产物:db/backups/payroll_YYYYMMDD_HHMMSS.sql.gz,保留最近 30 天。
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
if [[ -f docker/.env ]]; then
# shellcheck disable=SC1091
source docker/.env
else
echo "ERROR: docker/.env 不存在(请先创建 docker/.env 并填入 MySQL 凭据)" >&2
exit 1
fi
: "${MYSQL_DATABASE:?docker/.env 缺 MYSQL_DATABASE}"
: "${MYSQL_ROOT_PASSWORD:?docker/.env 缺 MYSQL_ROOT_PASSWORD}"
BACKUP_DIR="$ROOT/db/backups"
mkdir -p "$BACKUP_DIR"
STAMP="$(date +%Y%m%d_%H%M%S)"
OUT="$BACKUP_DIR/payroll_${STAMP}.sql.gz"
# 容器名固定(compose 显式 container_name),与 docker/docker-compose.dev.yml 保持一致
CONTAINER="ys-mysql-dev"
if ! docker inspect "$CONTAINER" >/dev/null 2>&1; then
echo "ERROR: 容器 $CONTAINER 未运行(先 docker compose up -d)" >&2
exit 1
fi
echo "开始备份 $MYSQL_DATABASE → $(basename "$OUT")"
docker exec "$CONTAINER" sh -c \
"mysqldump -uroot -p\"\$MYSQL_ROOT_PASSWORD\" --single-transaction --routines --triggers \"$MYSQL_DATABASE\"" \
| gzip > "$OUT"
if [[ ! -s "$OUT" ]]; then
echo "ERROR: 备份文件为空(数据库名或凭据可能不对)" >&2
rm -f "$OUT"
exit 1
fi
SIZE="$(du -h "$OUT" | cut -f1)"
echo "备份完成:$(basename "$OUT") ($SIZE)"
# 保留 30 天
find "$BACKUP_DIR" -maxdepth 1 -name 'payroll_*.sql.gz' -mtime +30 -print -delete
echo "已清理 30 天前的旧备份"
echo ""
echo "最新备份:"
ls -lh "$BACKUP_DIR"/*.sql.gz 2>/dev/null | tail -3