MySQL → mysqldump → 壓縮加密 → Mutt (SMTP) → Gmail (附件) → 保留 30 天標籤自動歸檔
↑ ↑
Crontab 02:00 每日 GPG 對稱加密 (AES256) 保護敏感資料
# RHEL/CentOS/Rocky/Alma
dnf install -y mysql mysql-server mutt gnupg2 zstd cronie
# Ubuntu/Debian
apt update && apt install -y mysql-client mutt gnupg zstd cron
# 通用:啟用並啟動 cron
systemctl enable --now crond # 或 cron
abcd efgh ijkl mnop,去除空格)set from = "mysql-backup@example.com"
set realname = "MySQL Automated Backup"
set smtp_url = "smtps://mysql-backup@example.com:abcdEFGHijklMNOP@smtp.gmail.com:465/"
set smtp_authenticators = "login"
set ssl_starttls = yes
set ssl_force_tls = yes
set certificate_file = "/etc/ssl/certs/ca-bundle.crt" # 系統 CA 憑證
set charset = "utf-8"
set send_charset = "utf-8"
set content_type = "text/plain"
set editor = "vim"
# GPG 加密預設收件者 (可被命令列覆蓋)
set pgp_default_key = ""
set crypt_autosign = no
set crypt_autoencrypt = no
set crypt_replyencrypt = no
set crypt_replysign = no
set crypt_verify_sig = yes
#!/bin/bash
# ==========================================
# MySQL 自動備份腳本 v2.1
# 功能:多庫備份 → 壓縮 → GPG 加密 → 郵件發送 → 本地保留
# 依賴:mysqldump, zstd, gpg, mutt, mailx (備用)
# ==========================================
set -euo pipefail
# ----- 配置區 (建議改為讀取 /etc/mysql/backup.conf) -----
DB_USER="backup_user"
DB_PASS="Str0ngP@ssw0rd!" # 生產環境請用 ~/.my.cnf 或密碼管理器
DB_HOST="127.0.0.1"
DB_PORT="3306"
DATABASES=("app_prod" "app_logs" "app_users") # 空陣列 = --all-databases
BACKUP_DIR="/backup/mysql/daily"
RETENTION_DAYS=7
GPG_RECIPIENT="dba-team@example.com" # GPG 公鑰對應的郵箱/Key ID
MAIL_TO="dba-team@example.com,oncall@example.com"
MAIL_SUBJECT="[MySQL Backup] $(hostname) - $(date +%F) - ${#DATABASES[@]} databases"
MAIL_BODY="附件為 $(date +%F) MySQL 每日備份,共 ${#DATABASES[@]} 個資料庫。
請定期驗證還原可用性。
--
自動備份系統 $(hostname)"
# ----- 內部變數 -----
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
HOSTNAME=$(hostname)
DUMP_FILE="${BACKUP_DIR}/mysql_${HOSTNAME}_${TIMESTAMP}.sql"
COMPRESSED_FILE="${DUMP_FILE}.zst"
ENCRYPTED_FILE="${COMPRESSED_FILE}.gpg"
LOG_FILE="${BACKUP_DIR}/backup_${TIMESTAMP}.log"
# ----- 函數定義 -----
log() { echo "[$(date +'%F %T')] $*" | tee -a "$LOG_FILE"; }
die() { log "ERROR: $*"; exit 1; }
# ----- 主流程 -----
mkdir -p "$BACKUP_DIR"
log "=== 開始備份 $HOSTNAME ==="
# 1. 執行 mysqldump
log "導出資料庫: ${DATABASES[*]:-ALL}"
if [ ${#DATABASES[@]} -eq 0 ]; then
mysqldump --defaults-extra-file=<(echo -e "[client]\nuser=$DB_USER\npassword=$DB_PASS\nhost=$DB_HOST\nport=$DB_PORT") \
--single-transaction --routines --triggers --events --hex-blob --quick \
--all-databases > "$DUMP_FILE" 2>>"$LOG_FILE" || die "mysqldump 失敗"
else
mysqldump --defaults-extra-file=<(echo -e "[client]\nuser=$DB_USER\npassword=$DB_PASS\nhost=$DB_HOST\nport=$DB_PORT") \
--single-transaction --routines --triggers --events --hex-blob --quick \
--databases "${DATABASES[@]}" > "$DUMP_FILE" 2>>"$LOG_FILE" || die "mysqldump 失敗"
fi
# 2. 壓縮 (zstd 極速高壓縮比)
log "壓縮中..."
zstd -T0 -19 -q "$DUMP_FILE" -o "$COMPRESSED_FILE" || die "壓縮失敗"
rm -f "$DUMP_FILE"
DUMP_SIZE=$(du -h "$COMPRESSED_FILE" | cut -f1)
log "壓縮完成: $DUMP_SIZE"
# 3. GPG 對稱加密 (AES256,無需公鑰分發,適合自動化)
log "加密中..."
gpg --batch --yes --cipher-algo AES256 --compress-algo 2 --symmetric \
--passphrase "$GPG_PASSPHRASE" \
--output "$ENCRYPTED_FILE" "$COMPRESSED_FILE" || die "加密失敗"
rm -f "$COMPRESSED_FILE"
ENC_SIZE=$(du -h "$ENCRYPTED_FILE" | cut -f1)
log "加密完成: $ENC_SIZE"
# 4. 發送郵件 (Mutt)
log "發送郵件至 $MAIL_TO..."
echo "$MAIL_BODY" | mutt -s "$MAIL_SUBJECT" -a "$ENCRYPTED_FILE" -- "$MAIL_TO" 2>>"$LOG_FILE" || {
log "Mutt 發送失敗,嘗試 mailx 備用..."
echo "$MAIL_BODY" | mailx -s "$MAIL_SUBJECT" -a "$ENCRYPTED_FILE" -r "mysql-backup@example.com" "$MAIL_TO" 2>>"$LOG_FILE" || die "郵件發送完全失敗"
}
log "郵件發送成功"
# 5. 本地保留 (最近 N 天)
log "清理 $RETENTION_DAYS 天前本地備份..."
find "$BACKUP_DIR" -name "mysql_${HOSTNAME}_*.gpg" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "backup_*.log" -mtime +$RETENTION_DAYS -delete
log "=== 備份完成 ==="
exit 0
# 腳本中使用環境變數傳遞密碼 (系統服務/環境變數注入)
export GPG_PASSPHRASE="SuperLongRandomPassphrase123!@#"
# 或讀取檔案 (權限 600)
GPG_PASSPHRASE=$(cat /etc/mysql/gpg_passphrase.txt)
# 1. 產生金鑰對 (離線安全機器)
gpg --full-generate-key
# 選擇 RSA 4096, 不設過期, 用戶名 "MySQL Backup", 郵箱 "backup@example.com"
# 2. 匯出公鑰分發至備份伺服器
gpg --armor --export "MySQL Backup" > mysql_backup_pubkey.asc
# 備份伺服器匯入
gpg --import mysql_backup_pubkey.asc
gpg --list-keys # 確認 Key ID
# 3. 腳本修改為非對稱加密
gpg --batch --yes --trust-model always --encrypt \
--recipient "MySQL Backup" \
--output "$ENCRYPTED_FILE" "$COMPRESSED_FILE"
# 4. 解密 (DBA 機器需有私鑰)
gpg --decrypt --output backup.sql.zst backup.sql.zst.gpg
# 編輯 root crontab
crontab -e
# 每日 02:00 執行 (避開業務高峰)
0 2 * * * /usr/local/bin/mysql_auto_backup.sh >> /var/log/mysql_backup_cron.log 2>&1
# 每週日 03:00 執行完整還原演練 (測試環境)
0 3 * * 0 /usr/local/bin/mysql_restore_drill.sh >> /var/log/mysql_restore_drill.log 2>&1
/var/log/mysql_backup_cron.log
/var/log/mysql_restore_drill.log
/backup/mysql/daily/backup_*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 640 root root
sharedscripts
postrotate
systemctl reload crond > /dev/null 2>&1 || true
endscript
}
Gmail 單附件限制 25MB,超過需分割或改用雲端硬碟:
# 分割加密檔案 (每片 20MB)
split -b 20M -d "$ENCRYPTED_FILE" "${ENCRYPTED_FILE}.part."
# 發送多封郵件 (Mutt 不直接支援多附件分割,需腳本迴圈)
for part in "${ENCRYPTED_FILE}.part."*; do
echo "$MAIL_BODY (Part $(basename $part))" | mutt -s "$MAIL_SUBJECT (Part $(basename $part))" -a "$part" -- "$MAIL_TO"
sleep 5 # 避免觸發 Gmail 發送頻率限制
done
# 或使用 rclone 上傳至 Google Drive (無大小限制、版本控制、共用連結)
rclone copy "$ENCRYPTED_FILE" gdrive:mysql_backups/$(hostname)/ --progress
bash -n /usr/local/bin/mysql_auto_backup.sh 語法檢查/usr/local/bin/mysql_auto_backup.sh 手動執行一次,觀察完整流程/var/log/mysql_backup_cron.log 無錯誤