背景
在 Linux 机器上用 Docker 部署了 MySQL 数据库,现在需要定期备份数据。备份策略是先在本地备份一份,然后传输到远程服务器。
环境信息:
- MySQL 容器:mysql-db
- 数据卷路径:
/home/docker/work_root/volumes/dev-db_server_mysql_data/_data
- 数据库:dev-db
- Linux:centos
备份方案选择
常见的备份方式有两种:
逻辑备份(mysqldump):导出 SQL 语句,可跨版本恢复,适合中小型数据库,灵活性高。
物理备份:直接复制数据文件,速度快,适合大型数据库,但 MySQL 版本需一致。
这里选择 mysqldump 方式,因为更灵活,后续恢复也方便。
备份脚本编写
本地备份
首先实现本地备份功能:
1 2 3 4 5 6 7 8 9 10 11 12 13
| mkdir -p ~/mysql_backups
docker exec mysql-db mysqldump -uroot -p'password' \ --single-transaction \ --routines \ --triggers \ --events \ --all-databases > ~/mysql_backups/backup_$(date +%Y%m%d_%H%M%S).sql
gzip ~/mysql_backups/backup_*.sql
|
参数说明:
--single-transaction:保证数据一致性
--routines:包含存储过程和函数
--triggers:包含触发器
--events:包含事件
--all-databases:备份所有数据库
远程传输
备份完成后需要传输到远程服务器。使用 scp 命令,但遇到一个问题:scp 本身不支持密码参数。
解决方案是使用 sshpass 工具:
1 2 3 4 5 6
| sudo yum install sshpass -y
sshpass -p 'password' scp -o StrictHostKeyChecking=no \ backup.sql.gz root@远程IP地址:/home/autotest/
|
这里踩了个坑:第一次传输时遇到 “Host key verification failed” 错误,返回值是 6。原因是 SSH 主机密钥验证失败,需要添加 -o StrictHostKeyChecking=no 参数跳过验证。
完整备份脚本
将上述功能整合成一个完整的脚本 /opt/scripts/mysql_backup.sh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| #!/bin/bash
CONTAINER_NAME="mysql-db" MYSQL_ROOT_PASSWORD="password" DATABASE_NAME="all_databases" BACKUP_DIR="$HOME/mysql_backups" REMOTE_USER="root" REMOTE_HOST="IP地址" REMOTE_PASSWORD="password" REMOTE_PATH="/home/autotest/" RETENTION_DAYS=7
mkdir -p $BACKUP_DIR
TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/${DATABASE_NAME}_${TIMESTAMP}.sql"
echo "==========================================" echo "MySQL 数据库备份脚本" echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" echo "=========================================="
echo "" echo "[1/5] 开始备份数据库 $DATABASE_NAME ..." docker exec $CONTAINER_NAME mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" \ --single-transaction \ --routines \ --triggers \ --events \ --set-gtid-purged=OFF \ --all-databases > $BACKUP_FILE
if [ $? -eq 0 ] && [ -s $BACKUP_FILE ]; then echo "✓ 数据库备份成功" ls -lh $BACKUP_FILE else echo "✗ 数据库备份失败" exit 1 fi
echo "" echo "[2/5] 压缩备份文件..." gzip $BACKUP_FILE BACKUP_FILE="${BACKUP_FILE}.gz"
if [ -f "$BACKUP_FILE" ]; then echo "✓ 压缩完成" ls -lh $BACKUP_FILE else echo "✗ 压缩失败" exit 1 fi
echo "" echo "[3/5] 测试远程服务器连接..." sshpass -p "$REMOTE_PASSWORD" ssh \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o ConnectTimeout=10 \ ${REMOTE_USER}@${REMOTE_HOST} "echo '连接成功'" 2>/dev/null
if [ $? -eq 0 ]; then echo "✓ 远程服务器连接正常" else echo "✗ 无法连接到远程服务器" exit 1 fi
echo "" echo "[4/5] 传输文件到远程服务器..." sshpass -p "$REMOTE_PASSWORD" scp \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ $BACKUP_FILE ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH} 2>/dev/null
if [ $? -eq 0 ]; then echo "✓ 文件传输成功" FILENAME=$(basename $BACKUP_FILE) REMOTE_CHECK=$(sshpass -p "$REMOTE_PASSWORD" ssh \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ ${REMOTE_USER}@${REMOTE_HOST} \ "ls -lh ${REMOTE_PATH}${FILENAME} 2>/dev/null") if [ $? -eq 0 ]; then echo "远程文件信息:" echo "$REMOTE_CHECK" fi else echo "✗ 文件传输失败" exit 1 fi
echo "" echo "[5/5] 清理旧备份文件..." DELETED=$(find $BACKUP_DIR -name "${DATABASE_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l) echo "✓ 已删除 $DELETED 个超过 $RETENTION_DAYS 天的旧备份"
echo "" echo "==========================================" echo "✓ 备份任务完成!" echo "本地备份: $BACKUP_FILE" echo "远程备份: ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}$(basename $BACKUP_FILE)" echo "=========================================="
|
测试脚本:
1 2
| chmod +x /opt/scripts/mysql_backup.sh /opt/scripts/mysql_backup.sh
|
配置定时任务
使用 systemd 实现定时备份,需要创建两个文件:service 文件和 timer 文件。
创建 service 文件
编辑 /etc/systemd/system/mysql-backup.service:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| [Unit] Description=MySQL Database Backup Service After=network.target docker.service Requires=docker.service
[Service] Type=oneshot User=root Group=root WorkingDirectory=/opt/scripts ExecStart=/opt/scripts/mysql_backup.sh StandardOutput=journal StandardError=journal SyslogIdentifier=mysql-backup TimeoutStartSec=1800 Restart=no
[Install] WantedBy=multi-user.target
|
创建 timer 文件
编辑 /etc/systemd/system/mysql-backup.timer:
1 2 3 4 5 6 7 8 9 10 11 12
| [Unit] Description=MySQL Database Backup Timer Requires=mysql-backup.service
[Timer] OnCalendar=*-*-* 02:00:00 Persistent=true RandomizedDelaySec=300 OnBootSec=1min
[Install] WantedBy=timers.target
|
配置说明:
OnCalendar=*-*-* 02:00:00:每天凌晨 2 点执行
Persistent=true:如果错过执行时间,立即执行
RandomizedDelaySec=300:随机延迟 0-5 分钟
OnBootSec=1min:启动后 1 分钟执行一次
启用定时任务
1 2 3 4 5 6 7
| systemctl daemon-reload systemctl enable mysql-backup.timer systemctl start mysql-backup.timer
systemctl status mysql-backup.timer systemctl list-timers --all | grep mysql
|
查看日志
1 2 3 4 5 6 7 8 9 10 11
| journalctl -u mysql-backup.timer -n 20
journalctl -u mysql-backup.service -n 50
journalctl -u mysql-backup.service -f
journalctl -u mysql-backup.service --since today
|
手动触发测试
1 2 3 4 5
| systemctl start mysql-backup.service
systemctl status mysql-backup.service
|
非 Docker 环境适配
如果 MySQL 不是用 Docker 安装的,需要做以下调整:
调整 service 文件
将:
1 2
| After=network.target docker.service Requires=docker.service
|
改为:
1 2
| After=network.target mysql.service Requires=mysql.service
|
调整备份脚本
删除:
1
| CONTAINER_NAME="mysql-db"
|
将:
1
| docker exec $CONTAINER_NAME mysqldump
|
改为:
1
| /opt/mysql/bin/mysqldump
|
同时建议删除 2>/dev/null,这样能看到完整的错误信息。
踩坑记录
systemd 执行失败
手动执行脚本正常,但 systemd 执行失败,报错:
1
| Failed at step EXEC spawning /opt/scripts/mysql_backup.sh: Exec format error
|
原因是脚本第一行缺少 shebang #!/bin/bash,systemd 不知道用什么解释器执行。
解决方法:确保脚本第一行是 #!/bin/bash。
mysqldump 命令找不到
日志显示:
1
| /opt/scripts/mysql_backup.sh: line 27: mysqldump: command not found
|
但在命令行手动执行是正常的。原因是 systemd service 的 PATH 环境变量不包含 mysqldump 路径。
解决方法有三种:
- 使用绝对路径(推荐):将
mysqldump 改为 /opt/mysql/bin/mysqldump (通过 which mysqldump 或 whereis mysqldump 查看路径)
- 在 service 文件中设置 PATH 环境变量
- 在脚本开头添加
export PATH=/opt/mysql/bin:$PATH
我选择了第一种方式,最简单直接。
数据恢复
如果需要恢复数据:
1 2 3 4 5 6 7 8
| gunzip backup_20260113_225511.sql.gz
docker exec -i mysql-db mysql -uroot -p'password' < backup_20260113_225511.sql
mysql -uroot -p'password' < backup_20260113_225511.sql
|
总结
整个备份方案包含本地备份、远程传输、定时执行三个核心功能。使用 systemd 管理定时任务比 cron 更现代化,日志查看也更方便。
关键点:
- mysqldump 备份灵活可靠
- sshpass 解决密码传输问题
- systemd 实现自动化调度
- 定期清理旧备份节省空间