切換選單
切換偏好設定選單
切換個人選單
尚未登入
若您做出任何編輯,會公開您的 IP 位址。

Meta:備份方式

出自H.-H.'s Wiki

由於託管 H.-H.'s Wiki虛擬主機 方案,其主機服務供應商 RackNerd 並未開啟 Softaculous 所提供的自動備份功能,因此採用的備份方式是透過 cPanel 控制面板設定 Cron Jobs 排程服務,並定期執行腳本任務。

備份腳本

具體的備份步驟,是將原始文件與資料庫傾印文件,進行打包壓縮後,透過 WebDAV 協定上傳至 InfiniCLOUD 所提供網路空間,並檢查指定目錄中的備份數量,清理老舊版本的備份。

腳本範例如下:

#!/bin/bash
# ==================================================
# Name        : backup_wiki.sh
# Description : 備份維基實例,壓縮後上傳至 WebDAV
# Author      : H.-H. PENG (Hsins)
# Contact     : <[email protected]>
# Version     : 0.9
# ==================================================

# 腳本常數設定
# Configuration
readonly WEBDAV_URL="WEBDAV_URL"
readonly WEBDAV_USERNAME="WEBDAV_USERNAME"
readonly WEBDAV_PASSWORD="WEBDAV_PASSWORD"

readonly MYSQL_DATABASE="MYSQL_DATABASE"
readonly MYSQL_USERNAME="MYSQL_USERNAME"
readonly MYSQL_PASSWORD="MYSQL_PASSWORD"

readonly SOURCE_DIR="/home/hhpeng/domains/wiki.hhpeng.org"
readonly REMOTE_DIR="_backups/wiki.hhpeng.org"
readonly EXCLUDE_DIRS=("node_modules" "cache" ".git")

readonly BACKUP_RETENTION=4

# 錯誤處理
# Error Handling
set -e
set -o pipefail

# 輔助函數
# Helper Functions
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') $1"
}

create_backup() {
    TIMESTAMP=$(date +"%Y%m%d%H%M%S")
    ARCHIVE_NAME="backup_$TIMESTAMP.tar.gz"
    
    # 生成排除清單
    EXCLUDE_FILE=$(mktemp)
    for dir in "${EXCLUDE_DIRS[@]}"; do
        echo "$LOCAL_DIR/$dir" >> "$EXCLUDE_FILE"
    done
    
    # 備份資料庫
    DB_DUMP_FILE="db_dump.sql"
    mysqldump --user=$MYSQL_USERNAME --password=$MYSQL_PASSWORD --databases $MYSQL_DATABASE > $DB_DUMP_FILE
    
    # 壓縮備份
    log "備份壓縮中..."
    tar --exclude-from="$EXCLUDE_FILE" -czf "$ARCHIVE_NAME" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" -C "$(pwd)" "$DB_DUMP_FILE"
    rm --force "$EXCLUDE_FILE" "$DB_DUMP_FILE"
    log "備份完成"
}

upload_new_backup() {
    log "上傳檔案中..."
    curl -u "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" -T "$ARCHIVE_NAME" "$WEBDAV_URL/$REMOTE_DIR/$ARCHIVE_NAME" 1>/dev/null 2>/dev/null
    
    if [ $? -eq 0 ]; then
        log "上傳成功"
        rm -f "$ARCHIVE_NAME"  # 移除本地備份
    else
        log "上傳失敗"
        exit 1
    fi
}

clean_old_backups() {
    # 獲取檔案清單,解析檔案名稱與修改時間
    RESPONSE=$(curl -s -u "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" -X PROPFIND --data '<?xml version="1.0"?><d:propfind xmlns:d="DAV:"><d:prop><d:getlastmodified/></d:prop></d:propfind>' -H "Depth: 1" "$WEBDAV_URL/$REMOTE_DIR/")
    FILES_WITH_DATES=$(echo "$RESPONSE" | awk '
        /<D:href>/ {
            gsub(/.*<D:href>/, ""); gsub(/<\/D:href>.*/, "");
            file = $0;
        }
        /<lp1:getlastmodified>/ {
            gsub(/.*<lp1:getlastmodified>/, ""); gsub(/<\/lp1:getlastmodified>.*/, "");
            print $0, file;
        }
    ' | grep "backup_.*\.tar\.gz" | sort)
    
    # 取得備份檔案總數
    TOTAL_FILES=$(echo "$FILES_WITH_DATES" | wc -l)
    
    if [ "$TOTAL_FILES" -gt "$BACKUP_RETENTION" ]; then
        log "發現 $TOTAL_FILES 份備份,將刪除 $(($TOTAL_FILES - $BACKUP_RETENTION)) 份最舊的備份。"
        DELETE_COUNT=$((TOTAL_FILES - BACKUP_RETENTION))
        FILES_TO_DELETE=$(echo "$FILES_WITH_DATES" | head -n "$DELETE_COUNT" | awk '{print $7}')

        for file in $FILES_TO_DELETE; do
            log "刪除備份: $file"
            FULL_URL=$(echo "$WEBDAV_URL/$file" | sed 's@dav//dav@dav@')
            curl -u "$WEBDAV_USERNAME:$WEBDAV_PASSWORD" -X DELETE "$FULL_URL"
        done
    else
        log "備份數量在 $BACKUP_RETENTION 份內,無須刪除備份。"
    fi
}

# 主要流程
# Main Processes
create_backup
upload_new_backup
clean_old_backups