Full System Backup Strategy คือการวางแผนระบบ Backup ที่ครอบคลุมทุกส่วนของ Server ทั้ง Operating System, Configuration Files, Application Data, และ Database ให้สามารถ Restore กลับมาได้อย่างรวดเร็วในกรณีที่เกิด System Failure, Ransomware, หรือข้อมูลเสียหาย บน Cloud VPS ที่ไม่มี Physical Access การมีกลยุทธ์ Backup ที่ดีเป็นเรื่องจำเป็นอย่างยิ่ง
บทความนี้อธิบาย Full System Backup Strategy แบบครบวงจรสำหรับ Cloud VPS ตั้งแต่การแบ่งประเภทข้อมูลที่ต้อง Backup, เครื่องมือที่เหมาะสมสำหรับแต่ละประเภท, การออกแบบ Retention Policy แบบ 3-2-1 Backup Rule, การตรวจสอบความสมบูรณ์ของ Backup และการทดสอบ Restore จริง
ประเภทข้อมูลที่ต้อง Backup บน Cloud VPS
ก่อนออกแบบกลยุทธ์ Backup ต้องรู้ว่าข้อมูลอะไรอยู่ที่ไหน และมีความสำคัญแค่ไหน
# สำรวจข้อมูลบน Server
# 1. Application Files
ls /var/www/ # Web files
ls /opt/ # Installed applications
# 2. Configuration Files (สำคัญมาก — ใช้เวลานานถ้าต้อง config ใหม่)
ls /etc/ # System configs
ls /etc/nginx/ # Nginx config
ls /etc/mysql/ # MySQL config
ls /etc/ssl/ # SSL Certificates
# 3. Database (ข้อมูลที่เปลี่ยนแปลงบ่อยที่สุด)
mysql -e "SHOW DATABASES;"
# 4. User Home Directories
ls /home/
ls /root/
# 5. Log Files (ปกติไม่ต้อง backup แต่เก็บบางส่วน)
ls /var/log/
# ดูขนาดแต่ละ Directory
du -sh /var/www /etc /home /opt /var/log 2>/dev/null | sort -hr
3-2-1 Backup Rule — มาตรฐานกลยุทธ์ Backup
3-2-1 Backup Rule คือหลักการที่ถือว่าเป็นมาตรฐานอุตสาหกรรมสำหรับการป้องกันข้อมูล ประกอบด้วย: เก็บ Backup ไว้ 3 ชุด บน 2 Media ที่แตกต่างกัน โดยมี 1 ชุดอยู่นอกสถานที่ (Off-site) บน Cloud VPS ทำได้โดยเก็บไว้ใน Local Disk + Backup Server อีก Region + Object Storage
# 3-2-1 Backup บน Cloud VPS:
# Copy 1: Local Snapshot (บน VPS เดียวกัน)
# - เร็วที่สุดในการ Restore
# - ป้องกัน: การลบไฟล์ผิด, Application bug
# Copy 2: Remote Backup Server (ต่าง Region)
# - ป้องกัน: Data Center ไฟไหม้, Network outage
# - ใช้ rsync ส่งผ่าน SSH
# Copy 3: Object Storage (S3, Backblaze B2, Wasabi)
# - ป้องกัน: Ransomware (เพราะ object storage มี versioning)
# - ต้นทุนต่ำสุด
# - ใช้ rclone sync
# ตัวอย่าง rclone config สำหรับ Backblaze B2
rclone config
# ตอบ: n (new remote), ตั้งชื่อ, เลือก type 10 (Backblaze B2)
# กรอก accountId และ applicationKey
# ทดสอบ rclone
rclone ls b2:my-backup-bucket
Backup Configuration Files ด้วย etckeeper
etckeeper เป็นเครื่องมือที่ Track การเปลี่ยนแปลงใน /etc ด้วย Git ทำให้เห็นประวัติทุก Config Change และ Rollback ได้ทันที
# ติดตั้ง etckeeper
sudo apt install etckeeper # Ubuntu/Debian
sudo dnf install etckeeper # RHEL/Rocky
# เริ่มต้น etckeeper
sudo etckeeper init
sudo etckeeper commit "Initial /etc commit"
# etckeeper จะ auto-commit ทุกครั้งที่ติดตั้ง package
# ดู History
sudo git -C /etc log --oneline -10
# ดูการเปลี่ยนแปลงล่าสุด
sudo git -C /etc diff HEAD~1
# Rollback ไฟล์ config เดี่ยว
sudo git -C /etc checkout HEAD~1 -- nginx/nginx.conf
# Push ไปยัง Remote Git Repository (เป็น Backup ด้วย)
sudo git -C /etc remote add origin [email protected]:myorg/server-etc.git
sudo git -C /etc push -u origin master
Full Backup Script — ครอบคลุมทุกส่วน
#!/bin/bash
# /usr/local/bin/full-backup.sh
# Full System Backup: Config + App Files + Database
BACKUP_BASE="/backup/local"
REMOTE_SERVER="backup-server"
REMOTE_PATH="/backup/remote/$(hostname)"
SSH_KEY="/root/.ssh/backup_key"
DATE=$(date +%Y%m%d_%H%M%S)
LOG="/var/log/full-backup.log"
ALERT_EMAIL="[email protected]"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
fail() { log "ERROR: $*"; echo "$*" | mail -s "[Alert] Backup Failed: $(hostname)" "$ALERT_EMAIL"; exit 1; }
log "=== Full Backup Started ==="
mkdir -p "$BACKUP_BASE"
# 1. Backup /etc (Configuration Files)
log "Backing up /etc..."
tar -czf "${BACKUP_BASE}/etc_${DATE}.tar.gz" \
--exclude="/etc/mtab" \
/etc/ 2>/dev/null || fail "Failed to backup /etc"
log " /etc backup: OK ($(du -sh "${BACKUP_BASE}/etc_${DATE}.tar.gz" | cut -f1))"
# 2. Backup Web Files
log "Backing up /var/www..."
rsync -az --delete \
--exclude="*.log" --exclude="cache/" \
-e "ssh -i ${SSH_KEY}" \
/var/www/ "${REMOTE_SERVER}:${REMOTE_PATH}/www/" \
|| fail "Failed to backup /var/www"
log " /var/www backup: OK"
# 3. Backup Databases
log "Backing up databases..."
DB_DUMP="${BACKUP_BASE}/databases_${DATE}.sql.gz"
mysqldump \
--defaults-extra-file=/etc/mysql/backup.cnf \
--all-databases --single-transaction --quick \
| gzip > "$DB_DUMP" \
|| fail "mysqldump failed"
[ -s "$DB_DUMP" ] || fail "Database dump is empty"
log " Database backup: OK ($(du -sh "$DB_DUMP" | cut -f1))"
# 4. Sync Local Backups ไปยัง Remote
log "Syncing to remote server..."
rsync -az \
-e "ssh -i ${SSH_KEY}" \
"${BACKUP_BASE}/" \
"${REMOTE_SERVER}:${REMOTE_PATH}/archives/" \
|| log "WARNING: Remote sync failed (non-fatal)"
# 5. ลบ Local Backup เก่ากว่า 7 วัน
find "$BACKUP_BASE" -name "*.tar.gz" -o -name "*.sql.gz" \
| xargs -I{} find {} -mtime +7 -delete 2>/dev/null
log "=== Full Backup Completed ==="
Backup ไปยัง Object Storage ด้วย rclone
#!/bin/bash
# /usr/local/bin/offsite-backup.sh
# Upload Backup ไปยัง Object Storage (Off-site)
BACKUP_DIR="/backup/local"
RCLONE_DEST="b2:my-backup-bucket/$(hostname)"
LOG="/var/log/offsite-backup.log"
RETENTION_DAYS=30
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
log "=== Off-site Backup Started ==="
# Sync ไปยัง Object Storage
rclone sync "$BACKUP_DIR" "$RCLONE_DEST" \
--transfers=4 \
--checkers=8 \
--log-file="$LOG" \
--log-level=INFO
if [ $? -eq 0 ]; then
log "Off-site sync: OK"
else
log "WARNING: Off-site sync had errors (check log)"
fi
# ลบไฟล์เก่าใน Object Storage
rclone delete "$RCLONE_DEST" \
--min-age "${RETENTION_DAYS}d" \
--log-file="$LOG"
log "=== Off-site Backup Completed ==="
Backup Verification — ตรวจสอบความสมบูรณ์
#!/bin/bash
# /usr/local/bin/verify-backup.sh
# ตรวจสอบว่า Backup ไฟล์สมบูรณ์และใช้งานได้
BACKUP_DIR="/backup/local"
LOG="/var/log/backup-verify.log"
ALERT_EMAIL="[email protected]"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
alert() { echo "$*" | mail -s "[Alert] Backup Verify Failed: $(hostname)" "$ALERT_EMAIL"; }
log "=== Backup Verification Started ==="
ERRORS=0
# 1. ตรวจสอบ tar.gz ว่า archive ไม่เสีย
for f in "${BACKUP_DIR}"/*.tar.gz; do
[ -f "$f" ] || continue
if tar -tzf "$f" > /dev/null 2>&1; then
log " OK: $(basename $f)"
else
log " CORRUPT: $(basename $f)"
alert "Corrupt backup archive: $f"
ERRORS=$((ERRORS + 1))
fi
done
# 2. ตรวจสอบ .sql.gz ว่า gzip ไม่เสีย
for f in "${BACKUP_DIR}"/*.sql.gz; do
[ -f "$f" ] || continue
if gzip -t "$f" 2>/dev/null; then
log " OK: $(basename $f)"
else
log " CORRUPT: $(basename $f)"
alert "Corrupt database backup: $f"
ERRORS=$((ERRORS + 1))
fi
done
# 3. ตรวจสอบ Age — ต้องมี Backup ไม่เก่ากว่า 26 ชั่วโมง
touch -d "26 hours ago" /tmp/backup-age-check
if [ -z "$(find "$BACKUP_DIR" -name "*.gz" -newer /tmp/backup-age-check 2>/dev/null)" ]; then
log " WARNING: No recent backup (older than 26h)"
alert "No backup files found newer than 26 hours"
ERRORS=$((ERRORS + 1))
fi
log "Verification done. Errors: $ERRORS"
[ $ERRORS -eq 0 ] && log "=== All backups verified OK ===" || log "=== Verification FAILED ==="
Restore Runbook — ขั้นตอน Restore จริง
# === RESTORE RUNBOOK ===
# ใช้เมื่อต้อง Restore หลัง Disaster
# Step 1: เข้าถึง Backup (ถ้า VPS พังทั้งหมด ต้องสร้าง VPS ใหม่ก่อน)
scp -i ~/.ssh/backup_key backup-server:/backup/remote/web-prod/archives/etc_20260416.tar.gz /tmp/
# Step 2: Restore /etc
cd /
sudo tar -xzf /tmp/etc_20260416.tar.gz --strip-components=1 -C /etc/
# หรือ restore เฉพาะไฟล์:
sudo tar -xzf /tmp/etc_20260416.tar.gz etc/nginx/nginx.conf -O | sudo tee /etc/nginx/nginx.conf
# Step 3: Restore Web Files
rsync -avz \
-e "ssh -i /root/.ssh/backup_key" \
backup-server:/backup/remote/web-prod/www/ \
/var/www/
# Step 4: Restore Database
gunzip -c /tmp/databases_20260416.sql.gz | mysql \
--defaults-extra-file=/etc/mysql/restore.cnf
# Step 5: Restore เฉพาะ Database เดี่ยว
# ดึง SQL ของ database เดียวจากไฟล์ dump รวม
gunzip -c /tmp/databases_20260416.sql.gz \
| awk '/^-- Current Database: `mydb`/,/^-- Current Database: `/' \
| mysql mydb
# Step 6: ตรวจสอบหลัง Restore
systemctl restart nginx php8.1-fpm mysql
curl -o /dev/null -s -w "%{http_code}" http://localhost/
mysql -e "SHOW DATABASES;"
Cron Schedule — Full Backup Strategy
# /etc/cron.d/full-backup — Full Backup Cron Schedule
# ตั้งค่าแบบ Tiered: Database บ่อยกว่า Config
MAILTO="[email protected]"
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Full backup ทุกคืน เวลา 02:00
0 2 * * * root /usr/local/bin/full-backup.sh >> /var/log/full-backup-cron.log 2>&1
# Off-site sync หลัง full backup เสร็จ
30 3 * * * root /usr/local/bin/offsite-backup.sh >> /var/log/offsite-backup-cron.log 2>&1
# ตรวจสอบ Backup ทุกเช้า เวลา 07:00
0 7 * * * root /usr/local/bin/verify-backup.sh >> /var/log/backup-verify-cron.log 2>&1
# Backup /etc เฉพาะ (เร็ว) ทุก 6 ชั่วโมง
0 */6 * * * root tar -czf /backup/local/etc_$(date +\%Y\%m\%d_\%H\%M).tar.gz /etc/ 2>/dev/null
Test Restore Drill — ทดสอบ Restore เป็นประจำ
#!/bin/bash
# /usr/local/bin/restore-drill.sh
# ทดสอบ Restore ใน Sandbox เพื่อยืนยันว่า Backup ใช้งานได้จริง
DRILL_DIR="/tmp/restore-drill-$(date +%Y%m%d)"
BACKUP_DIR="/backup/local"
LOG="/var/log/restore-drill.log"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
log "=== Restore Drill Started ==="
mkdir -p "$DRILL_DIR"
# ดึง Backup ล่าสุด
LATEST_ETC=$(ls -t "${BACKUP_DIR}"/etc_*.tar.gz 2>/dev/null | head -1)
LATEST_DB=$(ls -t "${BACKUP_DIR}"/databases_*.sql.gz 2>/dev/null | head -1)
ERRORS=0
# ทดสอบ Restore /etc ใน Sandbox
if [ -n "$LATEST_ETC" ]; then
mkdir -p "${DRILL_DIR}/etc"
if tar -xzf "$LATEST_ETC" -C "${DRILL_DIR}/etc" 2>/dev/null; then
if [ -f "${DRILL_DIR}/etc/etc/nginx/nginx.conf" ]; then
log " /etc restore: OK (nginx.conf found)"
else
log " WARNING: nginx.conf not in backup"
fi
else
log " FAILED: /etc restore"
ERRORS=$((ERRORS + 1))
fi
fi
# ทดสอบ Database Restore ใน Docker Sandbox
if [ -n "$LATEST_DB" ] && command -v docker &>/dev/null; then
log " Testing DB restore in Docker sandbox..."
docker run --rm -d --name drill-mysql \
-e MYSQL_ROOT_PASSWORD=drillpass \
-p 13306:3306 mysql:8.0 > /dev/null 2>&1
sleep 15
if gunzip -c "$LATEST_DB" | docker exec -i drill-mysql \
mysql -uroot -pdrillpass --force 2>/dev/null; then
DB_COUNT=$(docker exec drill-mysql mysql -uroot -pdrillpass \
-e "SHOW DATABASES;" 2>/dev/null | grep -v "Database\|information_schema\|mysql\|performance\|sys" | wc -l)
log " DB restore: OK ($DB_COUNT user databases restored)"
else
log " WARNING: DB restore had errors"
fi
docker stop drill-mysql > /dev/null 2>&1
fi
rm -rf "$DRILL_DIR"
log "Restore Drill completed. Errors: $ERRORS"
สรุป
Full System Backup Strategy บน Cloud VPS ต้องครอบคลุม 3 ประเภทข้อมูล ได้แก่ Configuration Files (/etc), Application Files (/var/www), และ Database ซึ่งแต่ละประเภทมีความถี่และเครื่องมือที่เหมาะสมต่างกัน การใช้ 3-2-1 Backup Rule โดยเก็บ Backup ไว้ใน Local, Remote Server, และ Object Storage ช่วยป้องกันความเสียหายได้หลายระดับ etckeeper ช่วย Track การเปลี่ยนแปลง Config อย่างละเอียด ส่วน rclone ช่วย Sync Backup ไปยัง Cloud Storage สิ่งสำคัญที่สุดคือการทดสอบ Restore จริงด้วย Restore Drill เป็นประจำ เพราะ Backup ที่ไม่เคยทดสอบ Restore อาจไม่ใช้งานได้จริงเมื่อเกิดเหตุการณ์จริง
แนะนำบริการ DE
การสร้าง Full System Backup Strategy ต้องการ Root Access เพื่อติดตั้ง etckeeper, rclone, และตั้งค่า Cron Jobs Cloud VPS ของ DE ให้สิทธิ์ Root เต็มรูปแบบ รองรับการติดตั้งและตั้งค่าระบบ Backup แบบ 3-2-1 ได้อย่างอิสระ พร้อม Network เสถียรสำหรับ Sync ข้อมูลไปยัง Remote Server
สำหรับผู้ที่ต้องการระบบ Backup พร้อมใช้โดยไม่ต้องจัดการ Script เอง Cloud Hosting ของ DE มีระบบ Backup อัตโนมัติรายวันในตัว พร้อม Restore ผ่าน Control Panel ได้ทันทีโดยไม่ต้องเขียน Runbook

