限制:
QQ邮箱最大附件为50M,这里附件分卷大小设置为36M,编码后会增加30%,接近50M上限,并分为多个邮件发送。
数据库账号为:backup,密码保存在:/etc/my.cnf.d/backup.conf ,文件内容为:
[client]
password="123456"
邮件mail配置为 /etc/mail.rc,在最后增加内容,请勿直接复制,替换成自己的内容:
#QQ邮箱
set smtp=smtps://smtp.qq.com
set from="xxxxxx@qq.com"
set smtp-auth-user="xxxxxx@qq.com"
set smtp-auth-password="QQ邮箱TOKEN"
set smtp-auth=login
set smtp-use=starttls
备份脚本:backup_database.h
#!/bin/bash
set -euo pipefail
# ========== 配置区 ==========
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
WORK_DIR="/tmp/db_backup_$$"
FINAL_ARCHIVE="${WORK_DIR}/${BACKUP_DATE}.database.tar.gz"
SPLIT_SIZE="36m"
MAIL_TO="support@mdeve.com"
MAIL_SUBJECT_PREFIX="网站数据备份-${BACKUP_DATE}"
MYSQL_HOST="127.0.0.1"
MYSQL_USER="backup"
MYSQL_CNF="/etc/my.cnf.d/backup.conf"
DATABASES=(wordpress weather_ical proftpd phpmyadmin platform_public platform_logs smsfilter)
# 日志配置
LOG_DIR="/var/log/db_backup"
LOG_FILE="${LOG_DIR}/$(date +%Y%m%d).log"
# ===========================
# 初始化日志目录
if [ ! -d "$LOG_DIR" ]; then
mkdir -p "$LOG_DIR"
fi
# 日志函数:同时输出到终端和日志文件
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}
# 错误处理函数
error_exit() {
log "ERROR" "$1"
exit "${2:-1}"
}
# 开始执行
log "INFO" "========== 备份任务开始 =========="
log "INFO" "备份标识: $BACKUP_DATE"
log "INFO" "日志文件: $LOG_FILE"
# 检查依赖命令
for cmd in mysqldump tar split mail; do
if ! command -v "$cmd" &>/dev/null; then
error_exit "未找到命令 $cmd,请先安装。"
fi
done
log "INFO" "依赖命令检查通过"
# 检查 MySQL 配置文件
if [ ! -f "$MYSQL_CNF" ]; then
error_exit "MySQL配置文件 $MYSQL_CNF 不存在,请创建。"
fi
PERM=$(stat -c "%a" "$MYSQL_CNF" 2>/dev/null || stat -f "%Lp" "$MYSQL_CNF" 2>/dev/null)
if [ "$PERM" != "600" ]; then
log "WARN" "配置文件 $MYSQL_CNF 权限为 $PERM,建议设为600"
fi
# 创建临时工作目录
cleanup() {
log "INFO" "清理临时目录: $WORK_DIR"
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
mkdir -p "$WORK_DIR/sql_dumps"
log "INFO" "临时工作目录: $WORK_DIR"
# 复制脚本自身到工作目录作为附件
SCRIPT_ATTACH="${WORK_DIR}/backup_script.sh"
if [ -f "$0" ]; then
cp "$0" "$SCRIPT_ATTACH"
chmod 644 "$SCRIPT_ATTACH"
log "INFO" "已准备脚本附件: $SCRIPT_ATTACH"
else
log "WARN" "无法获取脚本自身路径,跳过脚本附件"
SCRIPT_ATTACH=""
fi
# 导出所有数据库
log "INFO" "开始导出数据库..."
for db in "${DATABASES[@]}"; do
log "INFO" " 正在导出: $db"
if mysqldump --defaults-extra-file="$MYSQL_CNF" \
-h "$MYSQL_HOST" -u "$MYSQL_USER" \
"$db" > "$WORK_DIR/sql_dumps/${db}.sql" 2>>"$LOG_FILE"; then
log "INFO" " ✓ $db 导出成功"
else
error_exit " ✗ $db 导出失败"
fi
done
# 打包所有 SQL 文件
log "INFO" "打包所有SQL文件..."
if tar czf "$FINAL_ARCHIVE" -C "$WORK_DIR/sql_dumps" . 2>>"$LOG_FILE"; then
log "INFO" "✓ 打包成功: $FINAL_ARCHIVE"
else
error_exit "✗ tar打包失败"
fi
# 分卷压缩
log "INFO" "分卷压缩(每卷 $SPLIT_SIZE)..."
cd "$WORK_DIR"
if split -b "$SPLIT_SIZE" "$FINAL_ARCHIVE" "${BACKUP_DATE}.database.tar.gz.part-" 2>>"$LOG_FILE"; then
log "INFO" "✓ 分卷压缩完成"
else
error_exit "✗ 分卷压缩失败"
fi
# 获取分卷文件列表
mapfile -t PARTS < <(ls "${BACKUP_DATE}.database.tar.gz.part-"* 2>/dev/null)
if [ ${#PARTS[@]} -eq 0 ]; then
error_exit "没有生成任何分卷文件"
fi
TOTAL_PARTS=${#PARTS[@]}
log "INFO" "共生成 $TOTAL_PARTS 个分卷"
# 准备日志附件文件(将完整日志复制到工作目录作为附件)
LOG_ATTACH="${WORK_DIR}/execution_log.txt"
if [ -f "$LOG_FILE" ]; then
cp "$LOG_FILE" "$LOG_ATTACH"
log "INFO" "已准备日志附件: $LOG_ATTACH"
else
echo "日志文件不存在" > "$LOG_ATTACH"
log "WARN" "日志文件不存在,已创建空附件"
fi
# 逐个分卷发送邮件(每封邮件附带相同的日志附件和脚本附件)
log "INFO" "开始发送邮件(共 $TOTAL_PARTS 封)..."
for i in "${!PARTS[@]}"; do
PART_FILE="${PARTS[$i]}"
PART_NUM=$((i+1))
MAIL_SUBJECT="${MAIL_SUBJECT_PREFIX} - 分卷 ${PART_NUM}/${TOTAL_PARTS}"
log "INFO" "发送分卷 ${PART_NUM}/${TOTAL_PARTS}: $(basename "$PART_FILE")"
# 构建附件参数(分卷文件 + 日志附件 + 脚本附件)
ATTACH_ARGS=(-a "$PART_FILE" -a "$LOG_ATTACH")
if [ -n "$SCRIPT_ATTACH" ] && [ -f "$SCRIPT_ATTACH" ]; then
ATTACH_ARGS+=(-a "$SCRIPT_ATTACH")
fi
# 简化的邮件正文
MAIL_BODY=$(cat <<EOF
网站数据库备份分卷 ${PART_NUM}/${TOTAL_PARTS}。
备份标识:${BACKUP_DATE}
分卷大小:每卷 ${SPLIT_SIZE}
当前附件:$(basename "$PART_FILE")
总卷数:${TOTAL_PARTS}
合并方法(将所有分卷下载到同一目录后执行):
cat ${BACKUP_DATE}.database.tar.gz.part-* > ${BACKUP_DATE}.database.tar.gz
tar xzvf ${BACKUP_DATE}.database.tar.gz
本邮件仅包含第 ${PART_NUM} 个分卷,请确保收到所有 ${TOTAL_PARTS} 个分卷后再进行合并。
附带附件:
- execution_log.txt:完整的执行日志
- backup_script.sh:本次使用的备份脚本
详细的执行日志请查看附件:execution_log.txt
EOF
)
# 发送邮件:分卷文件 + 日志附件 + 脚本附件(增加 -v 参数)
if echo "$MAIL_BODY" | mail -v -s "$MAIL_SUBJECT" "${ATTACH_ARGS[@]}" -- "$MAIL_TO" >>"$LOG_FILE" 2>&1; then
log "INFO" " ✓ 分卷 ${PART_NUM}/${TOTAL_PARTS} 发送成功(已附带日志和脚本附件)"
else
error_exit " ✗ 分卷 ${PART_NUM}/${TOTAL_PARTS} 发送失败"
fi
# 避免发送过快被限流
sleep 2
done
log "INFO" "所有 ${TOTAL_PARTS} 个分卷已通过 ${TOTAL_PARTS} 封邮件发送完成"
log "INFO" "========== 备份任务结束 =========="
# 删除本次执行生成的临时日志文件(正常结束时)
rm -f "$LOG_FILE"
exit 0
:bi...
文章评论