新增MySQL备份恢复脚本并完成演练
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# 数据备份策略与恢复演练
|
||||
|
||||
对应 issue:`#12 [P1][T9] 备份策略与恢复演练`
|
||||
|
||||
## 1. 备份策略
|
||||
|
||||
- 备份脚本:`scripts/ops/backup_mysql.sh`
|
||||
- 备份格式:`<db>_YYYYmmdd_HHMMSS.sql.gz`
|
||||
- 默认保留:`KEEP_DAYS=7`
|
||||
- 支持告警:`OPS_ALERT_WEBHOOK`
|
||||
|
||||
推荐 cron(每天 02:00):
|
||||
|
||||
```bash
|
||||
0 2 * * * MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD=*** MYSQL_DATABASE=wx_service BACKUP_DIR=/var/backups/wx_service KEEP_DAYS=7 OPS_ALERT_WEBHOOK="https://example.com/webhook" /path/to/wx_service/scripts/ops/backup_mysql.sh >> /var/log/wx_service-backup.log 2>&1
|
||||
```
|
||||
|
||||
## 2. 恢复流程
|
||||
|
||||
- 恢复脚本:`scripts/ops/restore_mysql.sh`
|
||||
- 为避免误操作,必须显式设置:`CONFIRM_RESTORE=1`
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
CONFIRM_RESTORE=1 MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD=*** MYSQL_DATABASE=wx_service /path/to/wx_service/scripts/ops/restore_mysql.sh /var/backups/wx_service/wx_service_20260228_020000.sql.gz
|
||||
```
|
||||
|
||||
## 3. 演练建议
|
||||
|
||||
1. 先在演练库执行恢复,不要直接对生产库恢复。
|
||||
2. 恢复后执行关键表行数与抽样数据校验。
|
||||
3. 记录耗时、失败点、回滚步骤,形成固定 runbook。
|
||||
@@ -0,0 +1,28 @@
|
||||
# 备份恢复演练记录(2026-02-28)
|
||||
|
||||
对应 issue:`#12 [P1][T9] 备份策略与恢复演练`
|
||||
|
||||
## 演练环境
|
||||
|
||||
- MySQL:`mysql:8.0` 临时容器
|
||||
- 数据库:`wx_service`
|
||||
- 备份目录:`/tmp/wx_service_backup_drill`
|
||||
|
||||
## 演练步骤
|
||||
|
||||
1. 初始化库并导入 `docs/sql/smoke.sql`
|
||||
2. 插入一条样本数据到 `fa_smoke_log`
|
||||
3. 执行备份脚本:
|
||||
- `scripts/ops/backup_mysql.sh`
|
||||
4. 清空 `fa_smoke_log`
|
||||
5. 执行恢复脚本:
|
||||
- `scripts/ops/restore_mysql.sh`
|
||||
6. 对比恢复前后行数
|
||||
|
||||
## 演练结果
|
||||
|
||||
- 备份文件:`/tmp/wx_service_backup_drill/wx_service_20260228_163926.sql.gz`
|
||||
- 清空后行数:`0`
|
||||
- 恢复后行数:`1`
|
||||
|
||||
结论:本次恢复演练成功,备份文件可用于恢复关键业务表数据。
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 支持两种模式:
|
||||
# 1) 本机 mysql/mysqldump:
|
||||
# MYSQL_HOST MYSQL_PORT MYSQL_USER MYSQL_PASSWORD MYSQL_DATABASE
|
||||
# 2) 容器内执行(推荐在无本地 mysql 客户端时使用):
|
||||
# MYSQL_CONTAINER=<container_name>
|
||||
|
||||
MYSQL_HOST="${MYSQL_HOST:-127.0.0.1}"
|
||||
MYSQL_PORT="${MYSQL_PORT:-3306}"
|
||||
MYSQL_USER="${MYSQL_USER:-root}"
|
||||
MYSQL_PASSWORD="${MYSQL_PASSWORD:-}"
|
||||
MYSQL_DATABASE="${MYSQL_DATABASE:-wx_service}"
|
||||
MYSQL_CONTAINER="${MYSQL_CONTAINER:-}"
|
||||
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/wx_service}"
|
||||
KEEP_DAYS="${KEEP_DAYS:-7}"
|
||||
OPS_ALERT_WEBHOOK="${OPS_ALERT_WEBHOOK:-}"
|
||||
ALERT_TITLE="${ALERT_TITLE:-[wx_service] 数据备份失败}"
|
||||
|
||||
send_alert() {
|
||||
local message="$1"
|
||||
if [[ -z "${OPS_ALERT_WEBHOOK}" ]]; then
|
||||
echo "ALERT: ${message}" >&2
|
||||
return
|
||||
fi
|
||||
curl -fsS -X POST "${OPS_ALERT_WEBHOOK}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"title\":\"${ALERT_TITLE}\",\"message\":\"${message}\"}" >/dev/null || true
|
||||
}
|
||||
|
||||
dump_cmd() {
|
||||
if [[ -n "${MYSQL_CONTAINER}" ]]; then
|
||||
if [[ -n "${MYSQL_PASSWORD}" ]]; then
|
||||
docker exec "${MYSQL_CONTAINER}" mysqldump -h127.0.0.1 -P"${MYSQL_PORT}" -u"${MYSQL_USER}" "-p${MYSQL_PASSWORD}" --single-transaction --quick --set-gtid-purged=OFF "${MYSQL_DATABASE}"
|
||||
else
|
||||
docker exec "${MYSQL_CONTAINER}" mysqldump -h127.0.0.1 -P"${MYSQL_PORT}" -u"${MYSQL_USER}" --single-transaction --quick --set-gtid-purged=OFF "${MYSQL_DATABASE}"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${MYSQL_PASSWORD}" ]]; then
|
||||
mysqldump -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -u"${MYSQL_USER}" "-p${MYSQL_PASSWORD}" --single-transaction --quick --set-gtid-purged=OFF "${MYSQL_DATABASE}"
|
||||
else
|
||||
mysqldump -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -u"${MYSQL_USER}" --single-transaction --quick --set-gtid-purged=OFF "${MYSQL_DATABASE}"
|
||||
fi
|
||||
}
|
||||
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
timestamp="$(date +%Y%m%d_%H%M%S)"
|
||||
outfile="${BACKUP_DIR}/${MYSQL_DATABASE}_${timestamp}.sql.gz"
|
||||
|
||||
if ! dump_cmd | gzip -9 > "${outfile}"; then
|
||||
send_alert "MySQL 备份失败,数据库=${MYSQL_DATABASE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "${BACKUP_DIR}" -type f -name "${MYSQL_DATABASE}_*.sql.gz" -mtime +"${KEEP_DAYS}" -delete || true
|
||||
echo "backup created: ${outfile}"
|
||||
|
||||
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 用法:
|
||||
# CONFIRM_RESTORE=1 ./scripts/ops/restore_mysql.sh /path/to/backup.sql.gz
|
||||
#
|
||||
# 支持两种模式:
|
||||
# 1) 本机 mysql 客户端
|
||||
# 2) MYSQL_CONTAINER=<container_name>(在容器内执行 mysql)
|
||||
|
||||
if [[ "${CONFIRM_RESTORE:-0}" != "1" ]]; then
|
||||
echo "restore blocked: set CONFIRM_RESTORE=1 to continue" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "usage: $0 <backup.sql|backup.sql.gz>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
backup_file="$1"
|
||||
if [[ ! -f "${backup_file}" ]]; then
|
||||
echo "backup file not found: ${backup_file}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MYSQL_HOST="${MYSQL_HOST:-127.0.0.1}"
|
||||
MYSQL_PORT="${MYSQL_PORT:-3306}"
|
||||
MYSQL_USER="${MYSQL_USER:-root}"
|
||||
MYSQL_PASSWORD="${MYSQL_PASSWORD:-}"
|
||||
MYSQL_DATABASE="${MYSQL_DATABASE:-wx_service}"
|
||||
MYSQL_CONTAINER="${MYSQL_CONTAINER:-}"
|
||||
|
||||
run_mysql() {
|
||||
if [[ -n "${MYSQL_CONTAINER}" ]]; then
|
||||
if [[ -n "${MYSQL_PASSWORD}" ]]; then
|
||||
docker exec -i "${MYSQL_CONTAINER}" mysql -h127.0.0.1 -P"${MYSQL_PORT}" -u"${MYSQL_USER}" "-p${MYSQL_PASSWORD}" "${MYSQL_DATABASE}"
|
||||
else
|
||||
docker exec -i "${MYSQL_CONTAINER}" mysql -h127.0.0.1 -P"${MYSQL_PORT}" -u"${MYSQL_USER}" "${MYSQL_DATABASE}"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "${MYSQL_PASSWORD}" ]]; then
|
||||
mysql -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -u"${MYSQL_USER}" "-p${MYSQL_PASSWORD}" "${MYSQL_DATABASE}"
|
||||
else
|
||||
mysql -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" -u"${MYSQL_USER}" "${MYSQL_DATABASE}"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${backup_file}" == *.gz ]]; then
|
||||
zcat "${backup_file}" | run_mysql
|
||||
else
|
||||
cat "${backup_file}" | run_mysql
|
||||
fi
|
||||
|
||||
echo "restore completed: ${backup_file}"
|
||||
|
||||
Reference in New Issue
Block a user