说明:本文中的服务器 IP、数据库密码、主机名等敏感信息均已脱敏处理,统一使用占位符表示。
原始操作过程以本次执行日志和脚本内容为依据,
备份脚本最终实现了本地备份、异地传输、MD5 校验、日志留存和 7 天自动清理。
一、实施背景
本次目标是为一套运行在 Docker 容器中的 MySQL 5.7 数据库配置自动化备份。数据库容器长期运行在业务服务器上,属于生产环境,因此备份方案需要满足以下要求:
-
尽量不影响现有业务运行;
-
支持 MySQL 全库逻辑备份;
-
本地保留一份备份文件;
-
异地备份服务器保留一份副本;
-
本地和异地均保留 7 天;
-
备份过程有日志记录;
-
备份文件具备完整性校验能力;
-
通过 crontab 定时自动执行。
最终采用的方案是:
Docker MySQL 容器
↓
mysqldump 全库逻辑备份
↓
本地生成 SQL 文件
↓
gzip 压缩
↓
生成 MD5 校验文件
↓
scp 传输至异地备份服务器
↓
本地和异地自动清理 7 天前备份
二、环境信息
| 项目 | 脱敏后说明 |
|---|---|
| 数据库类型 | MySQL |
| 数据库版本 | MySQL 5.7.x |
| 部署方式 | Docker 容器 |
| 容器名称 | mysql |
| 本地备份目录 | /home/mysqlbackup |
| 异地备份服务器 | <REMOTE_BACKUP_IP> |
| 异地备份目录 | /home/<DB_HOST>_mysql |
| 备份脚本 | /home/mysqlbackup/dbbackup.sh |
| 备份方式 | mysqldump 全库逻辑备份 |
| 保留策略 | 本地 7 天,异地 7 天 |
| 定时任务 | 每天凌晨 03:10 执行 |
三、确认 MySQL Docker 容器状态
首先在数据库服务器上确认 MySQL 容器是否正常运行:
docker ps
确认当前存在 MySQL 容器,容器名称为:
mysql
同时进入容器验证 MySQL 版本:
docker exec -it mysql /bin/bash
mysql --version
确认数据库版本为 MySQL 5.7.x,后续脚本按 MySQL 5.7 方式编写。
四、创建本地备份目录
在数据库服务器上创建本地备份目录:
mkdir -p /home/mysqlbackup/logs
chmod 750 /home/mysqlbackup
目录规划如下:
/home/mysqlbackup
├── dbbackup.sh # 备份脚本
├── logs # 备份日志目录
├── *_mysql_full.sql.gz # 压缩后的备份文件
└── *_mysql_full.sql.gz.md5 # MD5 校验文件
五、配置 SSH 免密登录
为了实现自动异地传输,需要从数据库服务器免密登录异地备份服务器。
1. 生成 SSH 密钥
ssh-keygen -t rsa -b 2048
一路回车即可,不设置 passphrase,避免定时任务执行时卡住。
2. 分发公钥到异地备份服务器
ssh-copy-id root@<REMOTE_BACKUP_IP>
首次连接时输入 yes 确认主机指纹,然后输入一次远程服务器密码。
3. 验证免密登录
ssh root@<REMOTE_BACKUP_IP>
如果可以直接登录,不再要求输入密码,说明免密配置成功。
退出远程服务器:
exit
六、创建异地备份目录
在数据库服务器上远程创建异地备份目录:
ssh root@<REMOTE_BACKUP_IP> "mkdir -p /home/<DB_HOST>_mysql && chmod 750 /home/<DB_HOST>_mysql"
确认目录:
ssh root@<REMOTE_BACKUP_IP> "ls -ld /home/<DB_HOST>_mysql"
七、测试 scp 异地传输
在正式写脚本前,先测试文件能否正常传到异地服务器。
echo test > /tmp/test_mysql_backup.txt
scp /tmp/test_mysql_backup.txt root@<REMOTE_BACKUP_IP>:/home/<DB_HOST>_mysql/
检查异地文件:
ssh root@<REMOTE_BACKUP_IP> "ls -lh /home/<DB_HOST>_mysql/"
确认测试文件存在后,清理测试文件:
ssh root@<REMOTE_BACKUP_IP> "rm -f /home/<DB_HOST>_mysql/test_mysql_backup.txt"
rm -f /tmp/test_mysql_backup.txt
至此,异地传输链路验证完成。
八、确认低资源优先级工具
为了减少备份过程对生产业务的影响,使用 ionice 和 nice 降低备份进程的 IO 和 CPU 优先级。
检查 ionice 是否存在:
which ionice
如果返回类似:
/usr/bin/ionice
说明可以直接使用。
九、编写备份脚本
创建脚本:
vi /home/mysqlbackup/dbbackup.sh
脚本内容如下,敏感信息已脱敏:
#!/bin/bash
# MySQL 5.7 Docker 全量备份脚本
# 本地路径:/home/mysqlbackup
# 异地路径:<REMOTE_BACKUP_IP>:/home/<DB_HOST>_mysql
# 保留策略:本地7天,异地7天
# 资源控制:降低CPU和IO优先级,尽量减少对生产业务影响
CONTAINER_NAME="mysql"
BACKUP_DIR="/home/mysqlbackup"
REMOTE_HOST="<REMOTE_BACKUP_IP>"
REMOTE_USER="root"
REMOTE_DIR="/home/<DB_HOST>_mysql"
MYSQL_USER="root"
MYSQL_PASSWORD="<MYSQL_ROOT_PASSWORD>"
RETENTION_DAYS=7
LOG_DIR="$BACKUP_DIR/logs"
DATE=$(date +"%Y%m%d_%H%M%S")
BACKUP_FILE="$BACKUP_DIR/${DATE}_mysql_full.sql"
COMPRESSED_FILE="$BACKUP_DIR/${DATE}_mysql_full.sql.gz"
MD5_FILE="$BACKUP_DIR/${DATE}_mysql_full.sql.gz.md5"
LOG_FILE="$LOG_DIR/${DATE}.log"
mkdir -p "$BACKUP_DIR" "$LOG_DIR"
exec > >(tee -a "$LOG_FILE") 2>&1
echo "========== [START] $(date '+%F %T') =========="
echo "[INFO] 本地备份目录: $BACKUP_DIR"
echo "[INFO] 日志文件: $LOG_FILE"
echo "[INFO] Docker容器: $CONTAINER_NAME"
echo "[INFO] 异地备份路径: $REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR"
echo "[INFO] 保留策略: 本地${RETENTION_DAYS}天,异地${RETENTION_DAYS}天"
docker ps --format '{{.Names}}' | grep -w "$CONTAINER_NAME" >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "[ERROR] MySQL容器 $CONTAINER_NAME 未运行,备份终止"
exit 1
fi
echo "[INFO] MySQL容器运行正常"
echo "[INFO] 开始执行 MySQL 全实例备份..."
ionice -c2 -n7 nice -n 19 docker exec -e MYSQL_PWD="$MYSQL_PASSWORD" "$CONTAINER_NAME" \
mysqldump -u"$MYSQL_USER" \
--single-transaction \
--quick \
--routines \
--events \
--triggers \
--all-databases \
--default-character-set=utf8mb4 \
--max-allowed-packet=1G \
--hex-blob \
> "$BACKUP_FILE"
if [ $? -eq 0 ]; then
echo "[SUCCESS] 全量备份成功: $BACKUP_FILE"
else
echo "[ERROR] 全量备份失败"
rm -f "$BACKUP_FILE"
exit 1
fi
echo "[INFO] 开始压缩备份文件..."
ionice -c2 -n7 nice -n 19 gzip -1 -c "$BACKUP_FILE" > "$COMPRESSED_FILE"
if [ $? -eq 0 ]; then
echo "[SUCCESS] 压缩成功: $COMPRESSED_FILE"
rm -f "$BACKUP_FILE"
else
echo "[ERROR] 压缩失败"
exit 1
fi
echo "[INFO] 开始校验压缩包..."
gzip -t "$COMPRESSED_FILE"
if [ $? -eq 0 ]; then
echo "[SUCCESS] gzip文件校验通过"
else
echo "[ERROR] gzip文件校验失败"
exit 1
fi
echo "[INFO] 开始生成MD5校验文件..."
cd "$BACKUP_DIR" || exit 1
md5sum "$(basename "$COMPRESSED_FILE")" > "$(basename "$MD5_FILE")"
if [ $? -eq 0 ]; then
echo "[SUCCESS] MD5校验文件生成成功: $MD5_FILE"
else
echo "[ERROR] MD5生成失败"
exit 1
fi
echo "[INFO] 开始传输备份文件到异地服务器..."
ssh "$REMOTE_USER@$REMOTE_HOST" "mkdir -p $REMOTE_DIR"
if [ $? -ne 0 ]; then
echo "[ERROR] 异地目录创建失败: $REMOTE_HOST:$REMOTE_DIR"
exit 1
fi
ionice -c2 -n7 nice -n 19 scp "$COMPRESSED_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/"
if [ $? -eq 0 ]; then
echo "[SUCCESS] 备份文件传输成功"
else
echo "[ERROR] 备份文件传输失败"
exit 1
fi
scp "$MD5_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/"
if [ $? -eq 0 ]; then
echo "[SUCCESS] MD5文件传输成功"
else
echo "[ERROR] MD5文件传输失败"
exit 1
fi
echo "[INFO] 清理本地超过 $RETENTION_DAYS 天的旧备份..."
find "$BACKUP_DIR" -type f -name "*_mysql_full.sql.gz" -mtime +$RETENTION_DAYS -exec rm -f {} \;
find "$BACKUP_DIR" -type f -name "*_mysql_full.sql.gz.md5" -mtime +$RETENTION_DAYS -exec rm -f {} \;
if [ $? -eq 0 ]; then
echo "[INFO] 本地旧备份清理完成"
else
echo "[WARN] 本地旧备份清理异常"
fi
echo "[INFO] 清理异地超过 $RETENTION_DAYS 天的旧备份..."
ssh "$REMOTE_USER@$REMOTE_HOST" "find $REMOTE_DIR -type f \( -name '*_mysql_full.sql.gz' -o -name '*_mysql_full.sql.gz.md5' \) -mtime +$RETENTION_DAYS -exec rm -f {} \;"
if [ $? -eq 0 ]; then
echo "[INFO] 异地旧备份清理完成"
else
echo "[WARN] 异地旧备份清理异常"
fi
echo "[INFO] 清理本地超过 $RETENTION_DAYS 天的旧日志..."
find "$LOG_DIR" -type f -name "*.log" -mtime +$RETENTION_DAYS -exec rm -f {} \;
if [ $? -eq 0 ]; then
echo "[INFO] 本地旧日志清理完成"
else
echo "[WARN] 本地旧日志清理异常"
fi
BACKUP_SIZE=$(du -sh "$COMPRESSED_FILE" | awk '{print $1}')
echo "[SUCCESS] 备份完成"
echo "[INFO] 备份文件: $COMPRESSED_FILE"
echo "[INFO] MD5文件: $MD5_FILE"
echo "[INFO] 备份大小: $BACKUP_SIZE"
echo "[INFO] 异地路径: $REMOTE_HOST:$REMOTE_DIR"
echo "=========== [END] $(date '+%F %T') ==========="
exit 0
十、脚本权限设置
由于脚本中包含数据库密码,必须收紧权限:
chmod 700 /home/mysqlbackup/dbbackup.sh
chown root:root /home/mysqlbackup/dbbackup.sh
检查权限:
ls -lh /home/mysqlbackup/dbbackup.sh
正常应类似:
-rwx------ 1 root root 5.2K dbbackup.sh
十一、脚本语法检查
执行:
bash -n /home/mysqlbackup/dbbackup.sh
如果没有任何输出,说明脚本语法正常。
十二、手工执行备份验证
在加入定时任务前,必须先手工执行一次:
/bin/bash /home/mysqlbackup/dbbackup.sh
执行成功后,日志应包含以下关键信息:
[SUCCESS] 全量备份成功
[SUCCESS] 压缩成功
[SUCCESS] gzip文件校验通过
[SUCCESS] MD5校验文件生成成功
[SUCCESS] 备份文件传输成功
[SUCCESS] MD5文件传输成功
[SUCCESS] 备份完成
本次手工验证中,备份文件大小约为 308M,执行耗时约 1 分钟,说明备份过程正常完成。
十三、检查本地备份文件
执行:
ls -lh /home/mysqlbackup
正常应看到类似文件:
YYYYMMDD_HHMMSS_mysql_full.sql.gz
YYYYMMDD_HHMMSS_mysql_full.sql.gz.md5
dbbackup.sh
logs
查看日志:
tail -n 100 /home/mysqlbackup/logs/*.log
十四、检查异地备份文件
执行:
ssh root@<REMOTE_BACKUP_IP> "ls -lh /home/<DB_HOST>_mysql"
正常应看到:
YYYYMMDD_HHMMSS_mysql_full.sql.gz
YYYYMMDD_HHMMSS_mysql_full.sql.gz.md5
十五、校验备份文件完整性
本地校验:
cd /home/mysqlbackup
md5sum -c YYYYMMDD_HHMMSS_mysql_full.sql.gz.md5
异地校验:
ssh root@<REMOTE_BACKUP_IP> "cd /home/<DB_HOST>_mysql && md5sum -c YYYYMMDD_HHMMSS_mysql_full.sql.gz.md5"
如果返回:
YYYYMMDD_HHMMSS_mysql_full.sql.gz: OK
或中文系统返回:
YYYYMMDD_HHMMSS_mysql_full.sql.gz: 确定
说明备份文件完整。
十六、配置 crontab 定时任务
手工验证成功后,配置定时任务:
crontab -e
加入:
# 备份 <DB_HOST> Docker MySQL 5.7,本地和异地保留7天
10 3 * * * /bin/bash /home/mysqlbackup/dbbackup.sh
查看定时任务:
crontab -l
最终定时任务表示:
每天凌晨 03:10 自动执行 MySQL 全库备份。
实际操作中,crontab 已配置每日 03:10 自动执行备份脚本。
十七、备份参数说明
1. --all-databases
表示备份当前 MySQL 实例下的所有数据库,是全库备份的核心参数。
2. --single-transaction
用于 InnoDB 表一致性备份,减少锁表影响,适合生产环境。
3. --quick
逐行读取数据,避免一次性把大表加载到内存,降低内存压力。
4. --routines --events --triggers
用于备份存储过程、函数、事件和触发器。
5. --default-character-set=utf8mb4
指定字符集,减少中文或特殊字符导出异常风险。
6. --max-allowed-packet=1G
提高客户端单包大小限制,避免大字段、大文本、大 BLOB 导出失败。
7. --hex-blob
以十六进制方式导出二进制字段,适合包含 BLOB 数据的业务库。
8. ionice -c2 -n7 nice -n 19
降低备份进程 IO 和 CPU 优先级,让业务优先,备份靠后。
9. gzip -1
使用较低压缩等级,减少压缩时 CPU 消耗,适合生产环境低影响备份。
十八、日常巡检命令
每天巡检时重点看三项:本地文件、日志、异地文件。
1. 查看本地备份
ls -lh /home/mysqlbackup
2. 查看备份日志
tail -n 100 /home/mysqlbackup/logs/*.log
3. 查看异地备份
ssh root@<REMOTE_BACKUP_IP> "ls -lh /home/<DB_HOST>_mysql"
4. 校验最新备份文件
cd /home/mysqlbackup
md5sum -c 最新备份文件.sql.gz.md5
十九、实施结果
本次 Docker MySQL 5.7 备份配置已完成,最终实现如下:
| 项目 | 结果 |
|---|---|
| 备份方式 | mysqldump 全库逻辑备份 |
| 备份范围 | 当前 MySQL 实例全部数据库 |
| 本地备份 | 已实现 |
| 异地备份 | 已实现 |
| 备份压缩 | 已实现 |
| gzip 校验 | 已实现 |
| MD5 校验 | 已实现 |
| 低资源优先级 | 已实现 |
| 本地保留 | 7 天 |
| 异地保留 | 7 天 |
| 自动执行 | 每天 03:10 |
| 手工验证 | 成功 |
| 异地传输 | 成功 |