Automated Backups ด้วย Rsync — SSH Key, Snapshot Strategy และ Cron

Rsync เป็นเครื่องมือ Backup ที่มีประสิทธิภาพสูงสำหรับการ Sync ไฟล์แบบ Incremental ทำให้ Backup ครั้งที่สองเป็นต้นไปเร็วมากเพราะส่งเฉพาะส่วนที่เปลี่ยนแปลง การตั้งค่า Automated Backup ด้วย Rsync และ Cron ช่วยให้มีข้อมูลสำรองอย่างสม่ำเสมอโดยไม่ต้องทำเองทุกวัน

บทความนี้เน้นการออกแบบและตั้งค่า Automated Backup ด้วย Rsync อย่างครบวงจร ตั้งแต่การตั้งค่า SSH Key สำหรับ Passwordless Authentication, รูปแบบ Backup ที่ใช้งานจริง, การตั้งค่า Retention Policy, การแจ้งเตือนเมื่อ Backup ล้มเหลว จนถึงการ Restore

ตั้งค่า SSH Key สำหรับ Passwordless Rsync

Automated Backup ต้องการการ Authentication โดยไม่มี Interactive Password Prompt SSH Key เป็นวิธีที่ปลอดภัยที่สุด

# บน Server ที่จะ Run Rsync (Backup Source/Client)
# สร้าง SSH Key สำหรับ Backup (ไม่มี Passphrase)
ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N "" -C "backup@$(hostname)"

# Copy Public Key ไปยัง Backup Server
ssh-copy-id -i ~/.ssh/backup_key.pub user@backup-server

# ทดสอบว่า Login ได้โดยไม่ต้องใส่ Password
ssh -i ~/.ssh/backup_key user@backup-server "echo OK"

# สร้าง SSH Config เพื่อใช้ Key อัตโนมัติ
cat >> ~/.ssh/config <<'EOF'
Host backup-server
    HostName 192.168.1.100
    User backupuser
    IdentityFile ~/.ssh/backup_key
    StrictHostKeyChecking no
    ConnectTimeout 30
EOF
# จำกัดสิทธิ์ Backup Key บน Backup Server (ป้องกัน Compromise)
# แก้ไข ~/.ssh/authorized_keys บน Backup Server
# เพิ่ม Prefix ก่อน Key เพื่อจำกัดว่าทำได้แค่ rsync

# ~/.ssh/authorized_keys บน Backup Server:
# command="rsync --server --daemon .",no-pty,no-port-forwarding ssh-ed25519 AAAA... backup@web-server

Rsync Backup Script พื้นฐาน

#!/bin/bash
# /usr/local/bin/rsync-backup.sh
# Rsync Backup Script พร้อม Logging และ Error Handling

# Configuration
SOURCE_DIR="/var/www/html"
BACKUP_SERVER="backup-server"
BACKUP_PATH="/backup/web-prod"
SSH_KEY="/root/.ssh/backup_key"
LOG_FILE="/var/log/rsync-backup.log"
ALERT_EMAIL="[email protected]"
HOSTNAME=$(hostname)

# Timestamp
DATE=$(date '+%Y-%m-%d %H:%M:%S')

log() {
    echo "[${DATE}] $1" | tee -a "$LOG_FILE"
}

log "=== Rsync Backup Started ==="

# รัน Rsync
rsync -avz --delete \
  --exclude="*.log" \
  --exclude="cache/" \
  --exclude="tmp/" \
  -e "ssh -i ${SSH_KEY} -o BatchMode=yes" \
  "${SOURCE_DIR}/" \
  "${BACKUP_SERVER}:${BACKUP_PATH}/"

EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
    log "Backup SUCCEEDED"
elif [ $EXIT_CODE -eq 24 ]; then
    # Exit code 24 = ไฟล์หายระหว่าง Transfer (ปกติ)
    log "Backup SUCCEEDED (some files vanished during transfer)"
else
    log "Backup FAILED with exit code: $EXIT_CODE"
    echo "Rsync backup failed on ${HOSTNAME} (exit code: $EXIT_CODE)" \
        | mail -s "[Alert] Backup Failed on ${HOSTNAME}" "$ALERT_EMAIL"
    exit 1
fi

log "=== Rsync Backup Completed ==="

Automated Backup ด้วย Snapshot Strategy

Snapshot Strategy ใช้ Hard Link เพื่อสร้าง Backup หลายจุดในเวลาต่างกัน โดยประหยัด Disk Space เพราะไฟล์ที่ไม่เปลี่ยนแปลงจะ Share กันผ่าน Hard Link

#!/bin/bash
# /usr/local/bin/snapshot-backup.sh
# Snapshot-style Backup ด้วย rsync + hard links

BACKUP_ROOT="/backup/snapshots/web"
SOURCE="/var/www/html"
SSH_KEY="/root/.ssh/backup_key"
BACKUP_HOST="backup-server"
MAX_SNAPSHOTS=30   # เก็บไว้ 30 Snapshot

DATE=$(date +%Y%m%d_%H%M%S)
LATEST="${BACKUP_ROOT}/latest"
DEST="${BACKUP_ROOT}/${DATE}"

# สร้าง Directory ปลายทาง
ssh -i "$SSH_KEY" "$BACKUP_HOST" "mkdir -p '${DEST}'"

# ตรวจสอบว่า latest Snapshot มีอยู่
if ssh -i "$SSH_KEY" "$BACKUP_HOST" "test -L '${LATEST}'"; then
    LINK_DEST_ARG="--link-dest=${LATEST}"
else
    LINK_DEST_ARG=""
fi

# Rsync พร้อม Hard Link
rsync -avz --delete \
  $LINK_DEST_ARG \
  --exclude="*.log" \
  --exclude="cache/" \
  -e "ssh -i ${SSH_KEY}" \
  "${SOURCE}/" \
  "${BACKUP_HOST}:${DEST}/"

# อัพเดต symlink
ssh -i "$SSH_KEY" "$BACKUP_HOST" "rm -f '${LATEST}' && ln -s '${DEST}' '${LATEST}'"

# ลบ Snapshot เก่าสุดถ้าเกิน MAX_SNAPSHOTS
SNAPSHOT_COUNT=$(ssh -i "$SSH_KEY" "$BACKUP_HOST" "ls -d ${BACKUP_ROOT}/20* 2>/dev/null | wc -l")
if [ "$SNAPSHOT_COUNT" -gt "$MAX_SNAPSHOTS" ]; then
    OLDEST=$(ssh -i "$SSH_KEY" "$BACKUP_HOST" "ls -d ${BACKUP_ROOT}/20* | sort | head -1")
    ssh -i "$SSH_KEY" "$BACKUP_HOST" "rm -rf '${OLDEST}'"
    echo "Removed old snapshot: $OLDEST"
fi

echo "Snapshot created: ${BACKUP_HOST}:${DEST}"

ตั้งค่า Cron Job สำหรับ Automated Backup

# เปิด Crontab สำหรับ root
sudo crontab -e

# ตัวอย่าง Cron Schedule:
# Backup ทุกวัน เวลา 02:00
0 2 * * * /usr/local/bin/rsync-backup.sh

# Snapshot ทุกวัน เวลา 01:00
0 1 * * * /usr/local/bin/snapshot-backup.sh

# Backup Database ทุกชั่วโมง
0 * * * * /usr/local/bin/db-backup.sh

# ทดสอบ Cron Expression ด้วย crontab.guru
# หรือดู Log: grep CRON /var/log/syslog

# ตั้งค่า Log Output ของ Cron
MAILTO="[email protected]"
0 2 * * * /usr/local/bin/rsync-backup.sh >> /var/log/backup-cron.log 2>&1

Backup Database พร้อม Rotation

#!/bin/bash
# /usr/local/bin/db-backup.sh
# Backup MySQL/MariaDB พร้อม Retention Policy

BACKUP_DIR="/backup/db"
BACKUP_HOST="backup-server"
SSH_KEY="/root/.ssh/backup_key"
REMOTE_DIR="/backup/remote-db"
RETENTION_LOCAL=7    # เก็บ Local 7 วัน
RETENTION_REMOTE=30  # เก็บ Remote 30 วัน

DATE=$(date +%Y%m%d_%H%M%S)
DUMP_FILE="${BACKUP_DIR}/all_db_${DATE}.sql.gz"

mkdir -p "$BACKUP_DIR"

# Dump ทุก Database
mysqldump \
  --defaults-extra-file=/etc/mysql/backup.cnf \
  --all-databases \
  --single-transaction \
  --quick | gzip > "$DUMP_FILE"

# ตรวจสอบว่า Dump สำเร็จ
if [ ! -s "$DUMP_FILE" ]; then
    echo "Database backup failed!" | mail -s "[Alert] DB Backup Failed" [email protected]
    exit 1
fi

# Sync ไปยัง Remote Backup Server
rsync -az \
  -e "ssh -i ${SSH_KEY}" \
  "$DUMP_FILE" \
  "${BACKUP_HOST}:${REMOTE_DIR}/"

# ลบ Local Backup เก่า
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +"$RETENTION_LOCAL" -delete

# ลบ Remote Backup เก่า
ssh -i "$SSH_KEY" "$BACKUP_HOST" \
  "find ${REMOTE_DIR} -name '*.sql.gz' -mtime +${RETENTION_REMOTE} -delete"

echo "Database backup: $DUMP_FILE"

ตรวจสอบและ Monitor Backup

#!/bin/bash
# /usr/local/bin/check-backup-age.sh
# ตรวจสอบว่า Backup ล่าสุดไม่เก่าเกิน N ชั่วโมง

BACKUP_HOST="backup-server"
SSH_KEY="/root/.ssh/backup_key"
BACKUP_PATH="/backup/web-prod"
MAX_AGE_HOURS=26   # Alert ถ้า Backup เก่ากว่า 26 ชั่วโมง
ALERT_EMAIL="[email protected]"
HOSTNAME=$(hostname)

# หาไฟล์ล่าสุดใน Backup Directory
LATEST_FILE=$(ssh -i "$SSH_KEY" "$BACKUP_HOST" \
  "find ${BACKUP_PATH} -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | awk '{print \$2}'")

if [ -z "$LATEST_FILE" ]; then
    echo "No backup files found!" \
        | mail -s "[Alert] No Backup Found on ${HOSTNAME}" "$ALERT_EMAIL"
    exit 1
fi

# ตรวจสอบ Age
FILE_AGE_HOURS=$(ssh -i "$SSH_KEY" "$BACKUP_HOST" \
  "echo \$(( ( \$(date +%s) - \$(stat -c %Y '${LATEST_FILE}') ) / 3600 ))")

if [ "$FILE_AGE_HOURS" -gt "$MAX_AGE_HOURS" ]; then
    echo "Last backup is ${FILE_AGE_HOURS} hours old (threshold: ${MAX_AGE_HOURS}h)" \
        | mail -s "[Alert] Backup Stale on ${HOSTNAME}" "$ALERT_EMAIL"
    exit 1
fi

echo "Backup OK: ${FILE_AGE_HOURS} hours old"

Restore จาก Rsync Backup

# Restore ทั้งหมด (Overwrite ปลายทาง)
rsync -avz \
  -e "ssh -i /root/.ssh/backup_key" \
  backup-server:/backup/web-prod/ \
  /var/www/html/

# Restore จาก Snapshot เฉพาะวันที่
rsync -avz \
  -e "ssh -i /root/.ssh/backup_key" \
  backup-server:/backup/snapshots/web/20260416_020000/ \
  /var/www/html/

# Restore เฉพาะบาง Directory
rsync -avz \
  -e "ssh -i /root/.ssh/backup_key" \
  backup-server:/backup/web-prod/wp-content/ \
  /var/www/html/wp-content/

# Restore ไปยัง Directory ชั่วคราว (ทดสอบก่อน)
rsync -avz \
  -e "ssh -i /root/.ssh/backup_key" \
  backup-server:/backup/web-prod/ \
  /tmp/restore-test/

# ดูความแตกต่างระหว่าง Backup กับ Production
rsync -avzn \
  -e "ssh -i /root/.ssh/backup_key" \
  backup-server:/backup/web-prod/ \
  /var/www/html/

สรุป

การทำ Automated Backup ด้วย Rsync ต้องการ 3 ส่วนหลัก ได้แก่ SSH Key สำหรับ Passwordless Authentication, Backup Script ที่ตรวจสอบ Exit Code และแจ้งเตือนเมื่อล้มเหลว และ Cron Job ที่รันสม่ำเสมอ Snapshot Strategy ด้วย --link-dest ช่วยประหยัด Disk Space โดยเก็บหลาย Snapshot พร้อมกัน Retention Policy ควรมีทั้ง Local (7 วัน) และ Remote (30 วัน) และต้องทดสอบ Restore เป็นประจำเพื่อให้มั่นใจว่า Backup ใช้งานได้จริง

แนะนำบริการ DE

การตั้งค่า Automated Backup ด้วย Rsync บน Server ต้องการสิทธิ์ Root และการจัดการ SSH Key Cloud VPS ของ DE ให้ Root Access เต็มรูปแบบ สามารถตั้งค่า Cron Jobs, SSH Keys, และ Rsync Scripts ได้อย่างอิสระ เหมาะสำหรับการสร้างระบบ Backup ที่ออกแบบเองได้ตามความต้องการ

หากต้องการระบบ Backup ที่ตั้งค่าพร้อมใช้โดยไม่ต้องเขียน Script เอง Cloud Hosting ของ DE มีระบบ Backup อัตโนมัติรายวันพร้อม Restore ผ่าน Control Panel สะดวกสำหรับผู้ที่ไม่ต้องการจัดการ Backup เอง