Disaster Recovery Planning สำหรับ Database — คู่มือวางแผนกู้คืนฐานข้อมูล

ระบบฐานข้อมูลเป็นหัวใจสำคัญของแอปพลิเคชัน หากเกิดเหตุการณ์ไม่คาดคิดเช่น ฮาร์ดแวร์เสียหาย ข้อมูลถูกลบโดยไม่ตั้งใจ หรือระบบล่มจาก Ransomware การมีแผน Disaster Recovery (DR) ที่ดีจะช่วยให้กู้คืนข้อมูลได้อย่างรวดเร็วและลดความเสียหายให้น้อยที่สุด

บทความนี้จะอธิบายหลักการวางแผน Disaster Recovery สำหรับฐานข้อมูล ตั้งแต่การกำหนด RTO/RPO การออกแบบกลยุทธ์สำรองข้อมูล การทำ Replication ไปจนถึงการทดสอบแผนกู้คืนอย่างเป็นระบบ พร้อมตัวอย่างคำสั่งสำหรับ MySQL, PostgreSQL และ MongoDB

ทำความเข้าใจ RTO และ RPO

ก่อนวางแผน DR สิ่งแรกที่ต้องกำหนดคือค่า RTO (Recovery Time Objective) และ RPO (Recovery Point Objective) ซึ่งเป็นตัวชี้วัดสำคัญที่กำหนดขอบเขตของแผนกู้คืน

RTO (Recovery Time Objective) คือระยะเวลาสูงสุดที่ระบบสามารถหยุดทำงานได้ตั้งแต่เกิดเหตุจนกลับมาใช้งานได้ปกติ ตัวอย่างเช่น RTO 1 ชั่วโมง หมายความว่าต้องกู้คืนระบบได้ภายใน 1 ชั่วโมงหลังเกิดเหตุ

RPO (Recovery Point Objective) คือปริมาณข้อมูลสูงสุดที่ยอมสูญเสียได้ วัดจากจุดสำรองข้อมูลล่าสุด ตัวอย่างเช่น RPO 15 นาที หมายความว่าข้อมูลที่สูญหายต้องไม่เกิน 15 นาทีก่อนเกิดเหตุ

ระดับความสำคัญRTORPOกลยุทธ์ที่เหมาะสม
สูงมาก (Mission Critical)< 5 นาที0 (Zero Data Loss)Synchronous Replication + Auto Failover
สูง (Business Critical)< 1 ชั่วโมง< 15 นาทีAsynchronous Replication + Point-in-Time Recovery
ปานกลาง< 4 ชั่วโมง< 1 ชั่วโมงIncremental Backup + Standby Server
ต่ำ< 24 ชั่วโมง< 24 ชั่วโมงDaily Full Backup

ประเภทของ Disaster ที่ต้องเตรียมรับมือ

การวางแผน DR ที่ดีต้องครอบคลุมเหตุการณ์หลายรูปแบบ แต่ละประเภทมีวิธีป้องกันและกู้คืนที่แตกต่างกัน

Hardware Failure — ดิสก์เสียหาย, RAM เสีย, เซิร์ฟเวอร์ล่ม เป็นเหตุการณ์ที่พบบ่อยที่สุด แก้ไขด้วยการทำ RAID, Replication ไปยังเซิร์ฟเวอร์สำรอง และ Automated Failover

Human Error — ลบข้อมูลผิด, DROP TABLE โดยไม่ตั้งใจ, UPDATE ไม่มี WHERE clause เป็นสาเหตุอันดับต้น ๆ ของการสูญเสียข้อมูล แก้ไขด้วย Point-in-Time Recovery (PITR) และ Delayed Replication

Software Bug / Corruption — ข้อมูลเสียหายจาก Bug ในแอปพลิเคชัน หรือ Database Engine มีปัญหา แก้ไขด้วย Backup ที่ตรวจสอบได้และ Logical Backup เพิ่มเติมจาก Physical Backup

Security Incident — Ransomware เข้ารหัสข้อมูล, SQL Injection ทำลายข้อมูล แก้ไขด้วย Offsite Backup ที่แยกสิทธิ์ออกจากระบบหลัก (Air-gapped Backup)

Datacenter Disaster — ไฟไหม้, น้ำท่วม, ไฟดับยาว แก้ไขด้วย Cross-region Replication และ Offsite Backup Storage

กลยุทธ์ Backup สำหรับ Disaster Recovery

Backup เป็นรากฐานของทุกแผน DR ควรใช้หลายวิธีร่วมกันเพื่อให้ครอบคลุมทุกสถานการณ์

3-2-1 Backup Rule

หลักการพื้นฐานที่ทุกองค์กรควรปฏิบัติตาม คือกฎ 3-2-1 ซึ่งกำหนดว่า ต้องมีข้อมูลอย่างน้อย 3 สำเนา เก็บบน 2 สื่อที่แตกต่างกัน (เช่น ดิสก์ในเครื่องและ Object Storage) และ 1 สำเนาต้องเก็บนอกสถานที่ (Offsite) หรือคนละ Region

Full Backup — MySQL

สำหรับ MySQL การใช้ mysqldump เป็นวิธี Logical Backup ที่ง่ายที่สุด เหมาะกับฐานข้อมูลขนาดไม่เกิน 50 GB

# Full backup ทุกฐานข้อมูล
mysqldump --all-databases --single-transaction \
  --routines --triggers --events \
  --master-data=2 \
  -u root -p | gzip > /backup/mysql_full_$(date +%Y%m%d_%H%M%S).sql.gz

# Backup เฉพาะฐานข้อมูลที่ต้องการ
mysqldump --single-transaction --routines --triggers \
  -u root -p mydb > /backup/mydb_$(date +%Y%m%d).sql

# ตรวจสอบ backup file
gunzip -c /backup/mysql_full_*.sql.gz | tail -5
# ต้องเห็น "-- Dump completed" แสดงว่าสำเร็จ

สำหรับฐานข้อมูลขนาดใหญ่ ควรใช้ Percona XtraBackup ซึ่งเป็น Physical Backup ที่ไม่ต้อง lock table

# Physical backup ด้วย Percona XtraBackup
xtrabackup --backup --target-dir=/backup/full_$(date +%Y%m%d)

# Prepare backup (ทำให้พร้อม restore)
xtrabackup --prepare --target-dir=/backup/full_20260407

# Restore — ต้องหยุด MySQL ก่อน
systemctl stop mysql
rm -rf /var/lib/mysql/*
xtrabackup --copy-back --target-dir=/backup/full_20260407
chown -R mysql:mysql /var/lib/mysql
systemctl start mysql

Full Backup — PostgreSQL

PostgreSQL มี pg_dump สำหรับ Logical Backup และ pg_basebackup สำหรับ Physical Backup ที่รองรับ Point-in-Time Recovery

# Logical backup
pg_dump -U postgres -Fc mydb > /backup/mydb_$(date +%Y%m%d).dump

# Logical backup ทุกฐานข้อมูล
pg_dumpall -U postgres | gzip > /backup/pg_all_$(date +%Y%m%d).sql.gz

# Physical backup สำหรับ PITR
pg_basebackup -U replicator -D /backup/base_$(date +%Y%m%d) \
  -Ft -z -Xs -P

# ตรวจสอบ backup
pg_restore --list /backup/mydb_20260407.dump | head -20

Full Backup — MongoDB

MongoDB ใช้ mongodump สำหรับ Logical Backup หรือ Filesystem Snapshot สำหรับ Physical Backup ที่เร็วกว่า

# Logical backup
mongodump --uri="mongodb://admin:pass@localhost:27017" \
  --gzip --out=/backup/mongo_$(date +%Y%m%d)

# Backup เฉพาะ collection
mongodump --db=myapp --collection=users \
  --gzip --out=/backup/users_$(date +%Y%m%d)

# Restore
mongorestore --gzip /backup/mongo_20260407/

# สำหรับ Replica Set — backup จาก Secondary เพื่อไม่กระทบ Primary
mongodump --host=secondary1:27017 --readPreference=secondary \
  --gzip --oplog --out=/backup/mongo_oplog_$(date +%Y%m%d)

Point-in-Time Recovery (PITR)

PITR ช่วยให้กู้คืนข้อมูลไปยังจุดเวลาใด ๆ ก็ได้ ไม่จำกัดเฉพาะจุดที่ทำ Backup เท่านั้น เหมาะอย่างยิ่งสำหรับกรณี Human Error ที่ต้องกู้คืนข้อมูลก่อนเวลาที่เกิดความผิดพลาด

PITR สำหรับ MySQL

MySQL ใช้ Binary Log (binlog) สำหรับ PITR โดยต้องเปิด binlog ไว้ล่วงหน้า

# ตรวจสอบว่า binlog เปิดอยู่
SHOW VARIABLES LIKE 'log_bin';

# เปิด binlog ใน my.cnf
[mysqld]
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
expire_logs_days = 14
server_id = 1

# ดู binlog ปัจจุบัน
SHOW MASTER STATUS;
SHOW BINARY LOGS;

# Restore: 1) restore full backup ก่อน แล้ว 2) replay binlog ถึงจุดที่ต้องการ
mysql -u root -p < /backup/mydb_full.sql

# replay binlog ถึงเวลาก่อนเกิดเหตุ (เช่น ก่อน DROP TABLE)
mysqlbinlog --stop-datetime="2026-04-07 14:30:00" \
  /var/log/mysql/mysql-bin.000042 | mysql -u root -p

# หรือระบุตาม position
mysqlbinlog --stop-position=12345 \
  /var/log/mysql/mysql-bin.000042 | mysql -u root -p

PITR สำหรับ PostgreSQL

PostgreSQL ใช้ WAL (Write-Ahead Log) สำหรับ PITR ซึ่งมีความยืดหยุ่นสูงมาก

# เปิด WAL archiving ใน postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'cp %p /backup/wal_archive/%f'

# Restore ด้วย PITR — สร้างไฟล์ recovery.signal
# (PostgreSQL 12+)

# แก้ไข postgresql.conf สำหรับ recovery
restore_command = 'cp /backup/wal_archive/%f %p'
recovery_target_time = '2026-04-07 14:30:00'
recovery_target_action = 'promote'

# ขั้นตอน PITR:
# 1. หยุด PostgreSQL
systemctl stop postgresql

# 2. ย้าย data directory เดิมออก
mv /var/lib/postgresql/16/main /var/lib/postgresql/16/main_broken

# 3. Restore base backup
tar -xzf /backup/base_20260407/base.tar.gz -C /var/lib/postgresql/16/main

# 4. สร้าง recovery.signal
touch /var/lib/postgresql/16/main/recovery.signal

# 5. Start PostgreSQL — จะ replay WAL ถึงเวลาที่กำหนด
systemctl start postgresql

Replication สำหรับ High Availability

การทำ Replication เป็นอีกชั้นของ DR ที่ช่วยให้ระบบทำงานต่อได้ทันทีเมื่อเซิร์ฟเวอร์หลักล่ม โดยไม่ต้องรอ Restore จาก Backup

Synchronous vs Asynchronous Replication

คุณสมบัติSynchronousAsynchronous
Data LossZero Data Lossอาจสูญเสียข้อมูลบางส่วน
Performanceช้ากว่า (รอ Replica ยืนยัน)เร็วกว่า (ไม่ต้องรอ)
เหมาะกับMission Critical, การเงินส่วนใหญ่ของระบบทั่วไป
RPO0วินาที-นาที

Delayed Replication — ป้องกัน Human Error

Delayed Replication คือการตั้งให้ Replica ช้ากว่า Primary ตามเวลาที่กำหนด เช่น 1 ชั่วโมง เพื่อที่หากเกิดการลบข้อมูลผิดพลาด ข้อมูลบน Replica จะยังไม่ถูกลบตาม

# MySQL — ตั้ง Delayed Replication 1 ชั่วโมง (3600 วินาที)
STOP REPLICA;
CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600;
START REPLICA;

# ตรวจสอบ delay
SHOW REPLICA STATUS\G
# ดูค่า SQL_Delay = 3600, SQL_Remaining_Delay

# กรณีต้องกู้คืน — หยุด Replica แล้วปล่อยให้ replay ถึงจุดที่ต้องการ
STOP REPLICA;
START REPLICA UNTIL MASTER_LOG_FILE='mysql-bin.000042', MASTER_LOG_POS=12345;

Automated Backup Script

การตั้ง Cron Job สำหรับ Backup อัตโนมัติช่วยลดความเสี่ยงจากการลืม Backup ด้วยตนเอง ตัวอย่าง Script ที่ครอบคลุมทั้ง Full Backup, Incremental Backup และการส่งไปเก็บ Offsite

#!/bin/bash
# db_backup.sh — Automated Database Backup Script

# ตัวแปร
BACKUP_DIR="/backup/mysql"
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
S3_BUCKET="s3://mycompany-db-backup"
LOG_FILE="/var/log/db_backup.log"

# สร้าง directory
mkdir -p "$BACKUP_DIR"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# Full Backup
log "Starting full backup..."
mysqldump --all-databases --single-transaction \
  --routines --triggers --events \
  --master-data=2 \
  -u backup_user -p"${MYSQL_BACKUP_PASS}" \
  2>> "$LOG_FILE" | gzip > "$BACKUP_DIR/full_${DATE}.sql.gz"

if [ $? -eq 0 ]; then
    log "Backup completed: full_${DATE}.sql.gz"
    
    # ตรวจสอบขนาดไฟล์ (ต้องไม่เป็น 0)
    FILE_SIZE=$(stat -f%z "$BACKUP_DIR/full_${DATE}.sql.gz" 2>/dev/null || stat -c%s "$BACKUP_DIR/full_${DATE}.sql.gz")
    if [ "$FILE_SIZE" -lt 1000 ]; then
        log "WARNING: Backup file too small ($FILE_SIZE bytes) — possible failure"
        exit 1
    fi
    
    # ส่งไป Offsite (S3)
    aws s3 cp "$BACKUP_DIR/full_${DATE}.sql.gz" "$S3_BUCKET/mysql/" \
      --storage-class STANDARD_IA
    log "Uploaded to S3: $S3_BUCKET/mysql/full_${DATE}.sql.gz"
    
    # ลบ backup เก่า
    find "$BACKUP_DIR" -name "full_*.sql.gz" -mtime +$RETENTION_DAYS -delete
    log "Cleaned up backups older than $RETENTION_DAYS days"
else
    log "ERROR: Backup failed!"
    # ส่ง alert
    curl -X POST "$SLACK_WEBHOOK" \
      -H 'Content-type: application/json' \
      -d "{\"text\": \"🔴 MySQL backup failed on $(hostname) at $(date)\"}"
    exit 1
fi

ตั้ง Cron Job ให้ทำงานอัตโนมัติ

# แก้ไข crontab
crontab -e

# Full backup ทุกวัน ตี 2
0 2 * * * /opt/scripts/db_backup.sh >> /var/log/db_backup.log 2>&1

# Incremental backup ทุก 4 ชั่วโมง (สำหรับ XtraBackup)
0 */4 * * * /opt/scripts/db_incremental.sh >> /var/log/db_backup.log 2>&1

# ตรวจสอบ backup ทุกสัปดาห์ (อาทิตย์ ตี 6)
0 6 * * 0 /opt/scripts/db_backup_verify.sh >> /var/log/db_backup.log 2>&1

Failover Strategy

Failover คือกระบวนการเปลี่ยนไปใช้เซิร์ฟเวอร์สำรองเมื่อเซิร์ฟเวอร์หลักล่ม แบ่งได้เป็น 2 แบบ คือ Manual Failover ที่ต้องมีคนสั่ง และ Automatic Failover ที่ระบบจัดการเอง

Automatic Failover ด้วย Orchestrator (MySQL)

Orchestrator เป็นเครื่องมือยอดนิยมสำหรับจัดการ MySQL Topology และ Automatic Failover

# orchestrator.conf.json — ตัวอย่างค่าหลัก
{
  "MySQLTopologyUser": "orchestrator",
  "MySQLTopologyPassword": "orch_pass",
  "RecoverMasterClusterFilters": ["*"],
  "PromotionIgnoreHostnameFilters": [],
  "DetectClusterAliasQuery": "SELECT @@hostname",
  "RecoveryPeriodBlockSeconds": 3600,
  "PostMasterFailoverProcesses": [
    "/opt/scripts/update_dns.sh {failureCluster} {successorHost}"
  ]
}

# ตรวจสอบ topology
orchestrator-client -c topology -alias mycluster

# Manual failover (ถ้าจำเป็น)
orchestrator-client -c graceful-master-takeover \
  -alias mycluster \
  -d new_master:3306

Automatic Failover ด้วย Patroni (PostgreSQL)

Patroni ร่วมกับ etcd เป็นโซลูชัน HA ที่ได้รับความนิยมสูงสำหรับ PostgreSQL มีระบบ Leader Election และ Automatic Failover ในตัว

# ตรวจสอบสถานะ cluster
patronictl -c /etc/patroni/patroni.yml list

# ผลลัพธ์ตัวอย่าง:
# +--------+----------+---------+---------+----+-----------+
# | Member | Host     | Role    | State   | TL | Lag in MB |
# +--------+----------+---------+---------+----+-----------+
# | node1  | 10.0.1.1 | Leader  | running |  3 |           |
# | node2  | 10.0.1.2 | Replica | running |  3 |         0 |
# | node3  | 10.0.1.3 | Replica | running |  3 |         0 |
# +--------+----------+---------+---------+----+-----------+

# Manual switchover (planned)
patronictl -c /etc/patroni/patroni.yml switchover --leader node1 --candidate node2

# ดู history failover
patronictl -c /etc/patroni/patroni.yml history

การทดสอบแผน Disaster Recovery

แผน DR ที่ไม่ได้ทดสอบก็ไม่ต่างจากไม่มีแผน การทดสอบอย่างสม่ำเสมอช่วยให้มั่นใจว่าเมื่อเกิดเหตุจริง ทุกอย่างจะทำงานได้ตามที่คาดหวัง

Backup Verification

ตรวจสอบว่าไฟล์ Backup สามารถ Restore ได้จริง ไม่ใช่แค่มีไฟล์อยู่

#!/bin/bash
# verify_backup.sh — ทดสอบ restore backup บนเซิร์ฟเวอร์ทดสอบ

BACKUP_FILE=$1
TEST_DB="verify_$(date +%s)"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }

# สร้างฐานข้อมูลทดสอบ
log "Creating test database: $TEST_DB"
mysql -u root -p -e "CREATE DATABASE $TEST_DB;"

# Restore ลงฐานข้อมูลทดสอบ
log "Restoring backup..."
gunzip -c "$BACKUP_FILE" | mysql -u root -p "$TEST_DB"

if [ $? -eq 0 ]; then
    # ตรวจสอบจำนวน table
    TABLE_COUNT=$(mysql -u root -p -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$TEST_DB';")
    log "Restore success — $TABLE_COUNT tables found"
    
    # ตรวจสอบ row count ของ table สำคัญ
    mysql -u root -p -e "SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema='$TEST_DB' ORDER BY table_rows DESC LIMIT 10;"
    
    # เปรียบเทียบกับ production
    PROD_TABLES=$(mysql -u root -p -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='production_db';")
    if [ "$TABLE_COUNT" -eq "$PROD_TABLES" ]; then
        log "✅ Table count matches production"
    else
        log "⚠️ Table count mismatch: backup=$TABLE_COUNT, production=$PROD_TABLES"
    fi
else
    log "❌ Restore failed!"
fi

# ลบฐานข้อมูลทดสอบ
mysql -u root -p -e "DROP DATABASE $TEST_DB;"
log "Test database cleaned up"

Failover Drill

ทดสอบ Failover เป็นประจำเพื่อให้มั่นใจว่าระบบ Automatic Failover ทำงานได้จริง และทีมงานรู้ขั้นตอนที่ต้องทำ

# ขั้นตอนทดสอบ Failover Drill

# 1. บันทึกสถานะก่อนทดสอบ
mysql -u root -p -e "SELECT @@hostname, @@server_id, NOW();" > /tmp/pre_failover.log

# 2. จำลอง Primary ล่ม (ใน maintenance window)
# วิธี A: หยุด MySQL บน Primary
systemctl stop mysql

# วิธี B: block network ด้วย iptables (ทดสอบ network partition)
iptables -A INPUT -p tcp --dport 3306 -j DROP

# 3. สังเกตว่า Failover เกิดขึ้นภายในกี่วินาที
# ดูจาก Orchestrator UI หรือ Patroni log

# 4. ตรวจสอบว่า Application เชื่อมต่อ Primary ใหม่ได้
mysql -h db-vip -u app_user -p -e "SELECT @@hostname;"

# 5. คืนค่าเซิร์ฟเวอร์เดิม (rejoin เป็น Replica)
# MySQL
systemctl start mysql
# Orchestrator จะ rejoin อัตโนมัติ หรือสั่ง manual:
# CHANGE REPLICATION SOURCE TO SOURCE_HOST='new_primary', ...;
# START REPLICA;

DR Drill Checklist

ทุกครั้งที่ทดสอบ DR ควรบันทึกผลลัพธ์ตาม Checklist นี้เพื่อปรับปรุงแผนให้ดีขึ้น

  • Backup ล่าสุดอายุไม่เกิน RPO ที่กำหนดหรือไม่
  • Restore สำเร็จภายในเวลา RTO ที่กำหนดหรือไม่
  • ข้อมูลหลัง Restore ครบถ้วนและถูกต้องหรือไม่ (ตรวจ row count, checksum)
  • Failover เกิดขึ้นภายในเวลาที่คาดหวังหรือไม่
  • Application เชื่อมต่อเซิร์ฟเวอร์ใหม่ได้โดยอัตโนมัติหรือไม่
  • ทีมงานรู้ขั้นตอนที่ต้องทำและติดต่อใครเมื่อเกิดเหตุจริง
  • มีจุดไหนที่ต้องปรับปรุงในแผน DR หรือไม่

Offsite Backup และ Cross-Region Strategy

การเก็บ Backup ไว้ในเซิร์ฟเวอร์เดียวกับฐานข้อมูลไม่เพียงพอ หาก Datacenter ล่มทั้งหมด Backup ก็จะหายไปด้วย ควรมีกลยุทธ์ Offsite ที่ชัดเจน

# ส่ง Backup ไป Object Storage (S3-compatible)
aws s3 sync /backup/mysql/ s3://mycompany-backup/mysql/ \
  --storage-class STANDARD_IA \
  --exclude "*.tmp"

# ตั้ง Lifecycle Policy ให้ย้ายไป Glacier หลัง 90 วัน
# aws s3api put-bucket-lifecycle-configuration ...

# ส่ง Backup ไปเซิร์ฟเวอร์อีก Region ด้วย rsync
rsync -avz --progress /backup/mysql/ \
  [email protected]:/backup/mysql/

# เข้ารหัส Backup ก่อนส่ง Offsite
gpg --symmetric --cipher-algo AES256 \
  -o /backup/encrypted/full_20260407.sql.gz.gpg \
  /backup/mysql/full_20260407.sql.gz

# ถอดรหัส
gpg -d /backup/encrypted/full_20260407.sql.gz.gpg > /backup/restored.sql.gz

Monitoring และ Alerting สำหรับ DR

การ Monitor ระบบ Backup และ Replication อย่างต่อเนื่องช่วยให้ตรวจจับปัญหาได้ก่อนที่จะกลายเป็น Disaster

#!/bin/bash
# dr_health_check.sh — ตรวจสอบสุขภาพ DR

ALERT_WEBHOOK="$SLACK_WEBHOOK"
ERRORS=0

# 1. ตรวจสอบ Backup ล่าสุด (ต้องไม่เก่าเกิน 25 ชั่วโมง)
LATEST_BACKUP=$(find /backup/mysql -name "full_*.sql.gz" -mmin -1500 | head -1)
if [ -z "$LATEST_BACKUP" ]; then
    echo "🔴 No recent backup found (>25 hours old)"
    ERRORS=$((ERRORS + 1))
fi

# 2. ตรวจสอบ Replication Lag
REPLICA_LAG=$(mysql -u monitor -p -h replica1 -N -e "SHOW REPLICA STATUS\G" | grep "Seconds_Behind_Source" | awk '{print $2}')
if [ "$REPLICA_LAG" -gt 300 ]; then
    echo "🔴 Replication lag: ${REPLICA_LAG}s (>5 min)"
    ERRORS=$((ERRORS + 1))
fi

# 3. ตรวจสอบ Replication Status
REPLICA_IO=$(mysql -u monitor -p -h replica1 -N -e "SHOW REPLICA STATUS\G" | grep "Replica_IO_Running" | awk '{print $2}')
REPLICA_SQL=$(mysql -u monitor -p -h replica1 -N -e "SHOW REPLICA STATUS\G" | grep "Replica_SQL_Running" | awk '{print $2}')
if [ "$REPLICA_IO" != "Yes" ] || [ "$REPLICA_SQL" != "Yes" ]; then
    echo "🔴 Replication broken: IO=$REPLICA_IO SQL=$REPLICA_SQL"
    ERRORS=$((ERRORS + 1))
fi

# 4. ตรวจสอบ Disk Space
DISK_USAGE=$(df /backup | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 85 ]; then
    echo "🟡 Backup disk usage: ${DISK_USAGE}%"
fi

# 5. ส่ง Alert ถ้ามีปัญหา
if [ $ERRORS -gt 0 ]; then
    curl -X POST "$ALERT_WEBHOOK" \
      -H 'Content-type: application/json' \
      -d "{\"text\": \"🔴 DR Health Check: $ERRORS issues found on $(hostname)\"}"
fi

DR Runbook Template

ทุกองค์กรควรมี Runbook ที่เขียนขั้นตอนไว้อย่างชัดเจน เพื่อให้ทุกคนในทีมสามารถดำเนินการกู้คืนได้แม้ในสถานการณ์กดดัน ตัวอย่างโครงสร้าง Runbook สำหรับ DR

  • Scenario: ระบุสถานการณ์ที่เกิดขึ้น เช่น Primary Database Down, Data Corruption, Datacenter Outage
  • Impact: ผลกระทบต่อระบบ เช่น แอปพลิเคชันใช้งานไม่ได้ ข้อมูลสูญหาย
  • Detection: ระบบตรวจจับอย่างไร เช่น Monitoring Alert, Application Error
  • Response Team: ใครรับผิดชอบ เช่น DBA on-call, DevOps Lead
  • Step-by-step Procedure: ขั้นตอนกู้คืนแบบละเอียดพร้อมคำสั่ง
  • Verification: วิธีตรวจสอบว่ากู้คืนสำเร็จ
  • Post-incident: สิ่งที่ต้องทำหลังกู้คืนเสร็จ เช่น Post-mortem, ปรับปรุงแผน

ข้อควรระวังในการวางแผน DR

  • ทดสอบ Restore เป็นประจำ — Backup ที่ไม่เคยทดสอบ Restore อาจไม่สามารถใช้งานได้จริง ควรทดสอบอย่างน้อยเดือนละครั้ง
  • แยกสิทธิ์ Backup จากระบบหลัก — Ransomware มักโจมตี Backup ที่อยู่ในเครือข่ายเดียวกัน ใช้ Credential แยกและ Immutable Storage
  • อย่าพึ่ง Replication อย่างเดียว — Replication ไม่ใช่ Backup ถ้า DROP TABLE บน Primary ข้อมูลบน Replica ก็หายไปเช่นกัน ต้องมี Backup ควบคู่เสมอ
  • บันทึกค่า Config ของฐานข้อมูล — เก็บ my.cnf, postgresql.conf, mongod.conf ไว้ใน Version Control เพื่อให้สร้างเซิร์ฟเวอร์ใหม่ได้อย่างรวดเร็ว
  • คำนวณ Storage ให้เพียงพอ — ถ้า Backup วันละ 10 GB เก็บ 30 วัน ต้องมีพื้นที่อย่างน้อย 300 GB บวกกันชนสำหรับ Incremental
  • ซ้อมกับทีมจริง — DR Drill ต้องให้ทุกคนที่เกี่ยวข้องเข้าร่วม ไม่ใช่แค่ DBA เพราะเมื่อเกิดเหตุจริง ทุกคนต้องรู้บทบาทของตัวเอง

สรุป

การวางแผน Disaster Recovery สำหรับฐานข้อมูลไม่ใช่สิ่งที่ทำครั้งเดียวแล้วเสร็จ แต่เป็นกระบวนการที่ต้องปรับปรุงอย่างต่อเนื่อง จุดเริ่มต้นคือการกำหนด RTO/RPO ให้สอดคล้องกับความต้องการทางธุรกิจ จากนั้นออกแบบกลยุทธ์ Backup ตามกฎ 3-2-1 เสริมด้วย Replication สำหรับ High Availability และ PITR สำหรับกรณี Human Error

สิ่งสำคัญที่สุดคือการทดสอบแผนอย่างสม่ำเสมอ Backup ที่ไม่เคย Restore มีค่าเท่ากับไม่มี Backup และ Failover ที่ไม่เคยซ้อมอาจไม่ทำงานเมื่อต้องการจริง ควรทดสอบ DR Drill อย่างน้อยไตรมาสละครั้ง และบันทึกผลลัพธ์เพื่อนำไปปรับปรุงแผนให้ดียิ่งขึ้น

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

หากคุณกำลังวางแผน Disaster Recovery สำหรับฐานข้อมูล การมีเซิร์ฟเวอร์ที่เสถียรและควบคุมได้เต็มที่เป็นสิ่งจำเป็น Cloud VPS ของ DE รองรับการติดตั้ง Replication, Backup Script, และ Monitoring ได้อย่างอิสระ พร้อม root access เต็มรูปแบบสำหรับจัดการ DR ตามที่ต้องการ

สำหรับผู้ที่ต้องการโฮสต์เว็บไซต์ควบคู่กับฐานข้อมูลโดยไม่ต้องจัดการเซิร์ฟเวอร์เอง Cloud Hosting ของ DE มีระบบ Backup อัตโนมัติในตัว ช่วยให้คุณมั่นใจว่าข้อมูลเว็บไซต์และฐานข้อมูลได้รับการปกป้องอยู่เสมอ