Skip to content

备份脚本

本节实现一个功能完整的自动化备份脚本,支持增量备份、压缩加密、远程同步和备份轮转。

功能设计

  • 支持多个备份源目录
  • 压缩打包(tar + gzip)
  • 按日期命名和组织备份文件
  • 自动清理过期备份
  • 可选远程同步(rsync/scp)
  • 备份结果通知

完整备份脚本

bash
#!/usr/bin/env bash
#
# 自动化备份脚本
# 用法:./backup.sh [--config backup.conf]
#

set -euo pipefail

# ============================================================
# 默认配置
# ============================================================
BACKUP_SOURCES=(
    "/etc"
    "/var/www"
    "/home"
)
BACKUP_DEST="/backup"
KEEP_DAYS=30                        # 保留最近 N 天的备份
COMPRESS=true
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
HOSTNAME=$(hostname -s)
LOG_FILE="/var/log/backup.log"

# 远程同步配置(可选)
REMOTE_SYNC=false
REMOTE_HOST="backup-server"
REMOTE_USER="backup"
REMOTE_PATH="/remote/backup"

# ============================================================
# 工具函数
# ============================================================
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

error() {
    log "ERROR: $*" >&2
    exit 1
}

# 格式化字节大小
format_size() {
    local bytes="$1"
    if (( bytes < 1024 )); then
        echo "${bytes}B"
    elif (( bytes < 1048576 )); then
        echo "$(( bytes / 1024 ))KB"
    elif (( bytes < 1073741824 )); then
        echo "$(( bytes / 1048576 ))MB"
    else
        echo "$(( bytes / 1073741824 ))GB"
    fi
}

# ============================================================
# 备份函数
# ============================================================

# 备份单个目录
backup_dir() {
    local source="$1"
    local name="${source//\//_}"
    name="${name#_}"    # 去掉开头的下划线
    local dest_file="${BACKUP_DEST}/${HOSTNAME}_${name}_${TIMESTAMP}.tar.gz"

    log "备份:$source$dest_file"

    if [[ ! -d "$source" ]]; then
        log "警告:目录不存在,跳过:$source"
        return 0
    fi

    if tar -czf "$dest_file" "$source" 2>>"$LOG_FILE"; then
        local size
        size=$(stat -c %s "$dest_file")
        log "备份完成:$dest_file($(format_size $size))"
        echo "$dest_file"
    else
        error "备份失败:$source"
    fi
}

# 数据库备份(MySQL 示例)
backup_mysql() {
    local db_name="${1:-all}"
    local db_user="${DB_USER:-root}"
    local db_pass="${DB_PASS:-}"
    local dest_file="${BACKUP_DEST}/${HOSTNAME}_mysql_${db_name}_${TIMESTAMP}.sql.gz"

    log "备份 MySQL 数据库:$db_name"

    if [[ "$db_name" == "all" ]]; then
        mysqldump -u"$db_user" ${db_pass:+-p"$db_pass"} --all-databases 2>>"$LOG_FILE" | \
            gzip > "$dest_file"
    else
        mysqldump -u"$db_user" ${db_pass:+-p"$db_pass"} "$db_name" 2>>"$LOG_FILE" | \
            gzip > "$dest_file"
    fi

    log "数据库备份完成:$dest_file"
}

# 清理过期备份
cleanup_old_backups() {
    log "清理 ${KEEP_DAYS} 天前的备份..."
    local count=0
    while IFS= read -r old_file; do
        rm -f "$old_file"
        (( count++ ))
        log "删除过期备份:$old_file"
    done < <(find "$BACKUP_DEST" -name "*.tar.gz" -o -name "*.sql.gz" | \
             xargs ls -t 2>/dev/null | tail -n +$(( KEEP_DAYS + 1 )) 2>/dev/null || true)
    log "共清理 $count 个过期备份"
}

# 同步到远程服务器
sync_to_remote() {
    if [[ "$REMOTE_SYNC" != "true" ]]; then
        return 0
    fi

    log "同步备份到远程:${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}"
    rsync -avz --delete \
        "$BACKUP_DEST/" \
        "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/" \
        2>>"$LOG_FILE" && log "远程同步完成" || log "警告:远程同步失败"
}

# ============================================================
# 主流程
# ============================================================
main() {
    log "=========================================="
    log "备份开始 | 主机:$HOSTNAME | 时间:$TIMESTAMP"
    log "=========================================="

    # 确保备份目录存在
    mkdir -p "$BACKUP_DEST"

    local start_time=$SECONDS
    local backup_files=()

    # 备份各源目录
    for source in "${BACKUP_SOURCES[@]}"; do
        if backup_file=$(backup_dir "$source"); then
            backup_files+=("$backup_file")
        fi
    done

    # 清理过期备份
    cleanup_old_backups

    # 远程同步
    sync_to_remote

    # 统计报告
    local elapsed=$(( SECONDS - start_time ))
    local total_size=0
    for f in "${backup_files[@]}"; do
        [[ -f "$f" ]] && total_size=$(( total_size + $(stat -c %s "$f") ))
    done

    log "=========================================="
    log "备份完成!"
    log "  备份文件数:${#backup_files[@]}"
    log "  总大小:$(format_size $total_size)"
    log "  耗时:${elapsed} 秒"
    log "=========================================="
}

main "$@"

增量备份(使用 rsync)

bash
#!/usr/bin/env bash
#
# rsync 增量备份脚本
#

SOURCE="/var/www"
DEST="/backup/incremental"
TIMESTAMP=$(date +%Y%m%d)
LATEST_LINK="$DEST/latest"

mkdir -p "$DEST/$TIMESTAMP"

rsync -av \
    --link-dest="$LATEST_LINK" \
    "$SOURCE/" \
    "$DEST/$TIMESTAMP/" \
    && ln -snf "$DEST/$TIMESTAMP" "$LATEST_LINK"

echo "增量备份完成:$DEST/$TIMESTAMP"

使用方法

bash
chmod +x backup.sh

# 立即执行备份
./backup.sh

# 加入定时任务(每天凌晨 2 点执行)
# crontab -e
# 0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1