Docker MySQL 5.7 全库备份到异地服务器实践记录_20260427

复制代码
说明:本文中的服务器 IP、数据库密码、主机名等敏感信息均已脱敏处理,统一使用占位符表示。
原始操作过程以本次执行日志和脚本内容为依据,
备份脚本最终实现了本地备份、异地传输、MD5 校验、日志留存和 7 天自动清理。

一、实施背景

本次目标是为一套运行在 Docker 容器中的 MySQL 5.7 数据库配置自动化备份。数据库容器长期运行在业务服务器上,属于生产环境,因此备份方案需要满足以下要求:

  1. 尽量不影响现有业务运行;

  2. 支持 MySQL 全库逻辑备份;

  3. 本地保留一份备份文件;

  4. 异地备份服务器保留一份副本;

  5. 本地和异地均保留 7 天;

  6. 备份过程有日志记录;

  7. 备份文件具备完整性校验能力;

  8. 通过 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

至此,异地传输链路验证完成。


八、确认低资源优先级工具

为了减少备份过程对生产业务的影响,使用 ionicenice 降低备份进程的 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
手工验证 成功
异地传输 成功
相关推荐
gmaajt2 小时前
html怎么转astro island模式_Astro Islands如何隔离HTML组件
jvm·数据库·python
剩下了什么2 小时前
dockerfile-知识概念介绍
linux·运维·服务器
四维迁跃2 小时前
CSS如何使用Less的Merge功能合并多个属性值_通过逗号或空格组织css参数
jvm·数据库·python
Young soul22 小时前
docker-compose安装elasticsearch、kibana、logstash以及ik分词器
elasticsearch·docker·jenkins
m0_741481782 小时前
C#怎么实现全文搜索 C#如何集成Elasticsearch或Lucene.Net实现全文检索功能【数据库】
jvm·数据库·python
Elastic 中国社区官方博客2 小时前
Elasticsearch:智能搜索 - AI builder,workflow 及 skills
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
u0109147602 小时前
如何通过后端 API 同时向两个 Webhook 发送表单数据
jvm·数据库·python
运维全栈笔记2 小时前
K8S部署MySQL主从复制实现高可用数据库
mysql·adb·云原生·容器·系统架构·kubernetes·kubelet
勤劳的进取家2 小时前
如何配置服务器代理转发
运维·服务器