第 1 步:准备工作 - 设置 SSH 免密登录
为了让脚本能自动运行而不需要手动输入密码,你需要设置从源服务器(运行 Docker 的服务器)到目标备份服务器的 SSH 免密登录。
假设:
- 源服务器:你当前操作的服务器,IP 为
SOURCE_IP。 - 目标服务器:用于存放备份的服务器,IP 为
TARGET_IP,你将使用backup_user用户登录。
在源服务器上执行以下步骤:
生成 SSH 密钥对(如果还没有的话):
bashssh-keygen -t rsa -b 4096一路按回车,不要设置密码。这会在
~/.ssh/目录下生成id_rsa(私钥) 和id_rsa.pub(公钥)。将公钥复制到目标服务器: 这是最关键的一步。使用
ssh-copy-id命令可以轻松完成。bashssh-copy-id backup_user@TARGET_IP系统会提示你输入一次
backup_user在目标服务器上的密码。成功后,ssh-copy-id会自动将你的公钥追加到目标服务器上~/.ssh/authorized_keys文件中。测试免密登录: 尝试从源服务器 SSH 到目标服务器,看是否还需要输入密码。
bashssh backup_user@TARGET_IP如果直接登录成功,没有提示输入密码,那么准备工作就完成了!
目标文件结构
/opt/backup/
├── backup.conf # <-- 新的配置文件,你只需要修改这个文件
└── backup.sh # <-- 主执行脚本,通常无需修改第 2 步:创建配置文件 backup.conf
在 /opt/backup/ 目录下创建一个名为 backup.conf 的文件。将以下内容复制进去,并根据你的实际情况修改这些值。
文件: /opt/backup/backup.conf
# =======================================================
# 数据库备份脚本的配置文件 (backup.conf)
# =======================================================
# --- Docker & 数据库配置 ---
CONTAINER_NAME=""
DB_USER="root"
DB_PASS=""
DB_NAME=""
# --- 本地备份配置 ---
# 本地备份文件的临时存储目录 (请使用绝对路径)
# 脚本会自动将备份文件保存在这个目录的 'temp' 子目录中
# 例如,如果这里设置为 /opt/backup,实际文件会存在 /opt/backup/temp
BACKUP_BASE_DIR="/opt/backup"
# 本地备份的保留天数
LOCAL_RETENTION_DAYS=2
# --- rsync 远程同步与清理配置 ---
# 远程服务器上用于登录的用户名
REMOTE_USER="backup_user"
# 远程服务器的 IP 地址或域名
REMOTE_HOST="target_server_ip_or_domain"
# 远程服务器上用于存放备份文件的目录 (请使用绝对路径)
REMOTE_BACKUP_DIR="/path/on/remote/server/for/backups"
# 远程服务器上备份的保留天数 (例如 30 天)
REMOTE_RETENTION_DAYS=30
# --- 通知配置 ---
# 企业微信 Webhook URL (留空则不发送通知)
WEBHOOK_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key= "第 3 步:创建主脚本 backup.sh
现在,在同一目录 /opt/backup/ 下创建主脚本 backup.sh。这个脚本被设计为可移植的,它会自动查找并加载与它在同一目录下的 backup.conf 文件。
文件: /opt/backup/backup.sh
#!/bin/bash
# ==============================================================================
# Docker 数据库备份、rsync 同步及远程清理的脚本
#
# 此脚本会自动加载同目录下的 backup.conf 配置文件。
# ==============================================================================
# --- 核心逻辑开始,通常无需修改 ---
# 获取脚本所在的绝对路径
# 这使得脚本可以从任何地方被调用(例如在 cron 中)
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
CONFIG_FILE="${SCRIPT_DIR}/backup.conf"
# 检查并加载配置文件
if [ -f "$CONFIG_FILE" ]; then
# shellcheck source=/dev/null
source "$CONFIG_FILE"
else
echo "!!! 致命错误: 配置文件未找到于 ${CONFIG_FILE}"
exit 1
fi
# 检查关键配置是否已设置
if [ -z "$DB_USER" ] || [ -z "$REMOTE_HOST" ] || [ -z "$BACKUP_BASE_DIR" ]; then
echo "!!! 致命错误: 配置文件中的关键变量(如 DB_USER, REMOTE_HOST, BACKUP_BASE_DIR)未设置。"
exit 1
fi
# 脚本健壮性设置:任何命令失败则立即退出
set -e
# --- 动态定义变量 ---
# 定义本地临时备份目录
BACKUP_DIR="${BACKUP_BASE_DIR}/temp"
# --- 函数定义 ---
# 函数:发送通知
send_notification() {
if [ -z "$WEBHOOK_URL" ]; then return; fi
local status="$1" message="$2" color="info" title="✅ 数据库备份及远程同步成功"
[ "$status" = "failure" ] && { color="warning"; title="🔥 数据库备份/同步失败"; }
local hostname; hostname=$(hostname)
local json_payload; json_payload=$(printf '{"msgtype": "markdown", "markdown": {"content": "**%s**\n>源主机: `%s`\n>数据库: `%s`\n>%s"}}' "$title" "$hostname" "$DB_NAME" "$message")
curl -s -X POST "$WEBHOOK_URL" -H 'Content-Type: application/json' -d "$json_payload" > /dev/null
}
# 错误处理
handle_error() {
local error_message="错误发生在脚本的第 $1 行。操作已中断。"
echo "!!! $error_message"
send_notification "failure" "$error_message"
exit 1
}
trap 'handle_error $LINENO' ERR
# --- 脚本主流程 ---
echo "==> [$(date +'%Y-%m-%d %H:%M:%S')] 开始执行备份、同步和清理任务..."
# 1. 依赖检查
if ! command -v rsync &> /dev/null; then echo "!!! 错误: 未找到 'rsync' 命令。"; exit 1; fi
if ! command -v ssh &> /dev/null; then echo "!!! 错误: 未找到 'ssh' 命令。"; exit 1; fi
# 2. 创建本地和远程备份目录
echo "--> 确保本地备份目录存在: ${BACKUP_DIR}"
mkdir -p "$BACKUP_DIR"
echo "--> 确保远程备份目录存在: ${REMOTE_BACKUP_DIR}"
ssh "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p ${REMOTE_BACKUP_DIR}"
# 3. 执行本地数据库备份
DATE_FORMAT=$(date +'%Y%m%d_%H%M%S')
BACKUP_FILENAME="${DB_NAME}_${DATE_FORMAT}.sql.gz"
LOCAL_BACKUP_FILE="${BACKUP_DIR}/${BACKUP_FILENAME}"
echo "--> 正在创建本地备份: ${LOCAL_BACKUP_FILE}"
docker exec -e MYSQL_PWD="$DB_PASS" "$CONTAINER_NAME" mysqldump -u "$DB_USER" --databases "$DB_NAME" | gzip > "$LOCAL_BACKUP_FILE"
LOCAL_BACKUP_SIZE=$(du -sh "$LOCAL_BACKUP_FILE" | awk '{print $1}')
echo "--> 本地备份创建成功,大小: ${LOCAL_BACKUP_SIZE}"
# 4. 使用 rsync 同步到远程服务器
REMOTE_PATH="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_BACKUP_DIR}"
echo "--> 正在同步到远程服务器: ${REMOTE_PATH}"
rsync -avz --progress "$LOCAL_BACKUP_FILE" "$REMOTE_PATH"
echo "--> 同步成功。"
# 5. 清理远程服务器上的旧备份
echo "--> 正在清理远程服务器上 ${REMOTE_RETENTION_DAYS} 天前的旧备份..."
ssh "${REMOTE_USER}@${REMOTE_HOST}" "find ${REMOTE_BACKUP_DIR} -name '*.sql.gz' -mtime +${REMOTE_RETENTION_DAYS} -exec rm -f {} \;"
echo "--> 远程清理指令已发送。"
# 6. 清理本地旧备份
echo "--> 正在清理本地 ${LOCAL_RETENTION_DAYS} 天前的旧备份..."
LOCAL_DELETED_COUNT=$(find "$BACKUP_DIR" -name "*.sql.gz" -mtime +"$LOCAL_RETENTION_DAYS" -print | wc -l)
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +"$LOCAL_RETENTION_DAYS" -exec rm -f {} \;
echo "--> 本地清理完成,共删除 ${LOCAL_DELETED_COUNT} 个文件。"
# 7. 发送成功通知
SUCCESS_MSG=$(printf "本地备份: `%s (%s)`\n>同步至: `%s`\n>本地清理: `%d` 个\n>远程保留: 最近 `%d` 天" \
"$LOCAL_BACKUP_FILE" "$LOCAL_BACKUP_SIZE" "${REMOTE_HOST}:${REMOTE_BACKUP_DIR}" "$LOCAL_DELETED_COUNT" "$REMOTE_RETENTION_DAYS")
send_notification "success" "$SUCCESS_MSG"
echo "--> 成功通知已发送。"
echo "==> [$(date +'%Y-%m-%d %H:%M:%S')] 所有操作完成!"
exit 0第 4 步:如何使用
创建目录和文件:
bash# 创建主目录 mkdir -p /opt/backup # 进入目录 cd /opt/backup # 创建并编辑配置文件 nano backup.conf # (将上面的配置内容粘贴进去并修改) # 创建并编辑主脚本 nano backup.sh # (将上面的脚本内容粘贴进去)授予执行权限: 你只需要给主脚本
backup.sh添加执行权限。bashchmod +x /opt/backup/backup.sh测试运行: 直接运行主脚本。它会自动加载旁边的
backup.conf文件。bash/opt/backup/backup.sh更新定时任务 (Cron Job): 最后,确保你的
cron任务指向的是这个主脚本的绝对路径。bashcrontab -e在编辑器中,你的定时任务应该看起来像这样:
crontab30 3 * * * /opt/backup/backup.sh >> /opt/backup/backup.log 2>&1这里我将日志文件也放在了
/opt/backup/目录下,方便集中管理。
现在你的设置就非常清晰和专业了:配置和代码完全分离,日常维护只需要修改 backup.conf 文件,而无需触碰核心的 backup.sh 脚本。
