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