Workshop: Database High Availability Setup

High Availability (HA) คือการออกแบบระบบฐานข้อมูลให้ทำงานได้อย่างต่อเนื่อง แม้จะมีเซิร์ฟเวอร์บางตัวล่มหรือเกิดปัญหา ระบบ HA ช่วยลด Downtime ป้องกันข้อมูลสูญหาย และทำให้แอพพลิเคชันพร้อมให้บริการตลอดเวลา

Workshop นี้จะพาคุณตั้งค่า High Availability สำหรับฐานข้อมูลยอดนิยม ได้แก่ MySQL Replication, PostgreSQL Streaming Replication และ MongoDB Replica Set บน Cloud VPS พร้อมแนวทางปฏิบัติสำหรับ Production

สิ่งที่ต้องเตรียม

  • Cloud VPS อย่างน้อย 2 เครื่อง (Primary + Replica) แต่ละเครื่องใช้ Ubuntu 22.04 LTS
  • เครื่องสามารถสื่อสารกันผ่าน Private Network ได้
  • Root access บนทุกเครื่อง
  • RAM อย่างน้อย 2 GB ต่อเครื่อง

ส่วนที่ 1 — MySQL Replication

ตั้งค่า Primary Server

# บน Primary Server (IP: 10.0.0.1)
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

# เพิ่ม/แก้ไข settings:
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_do_db = myapp_db
max_binlog_size = 100M
expire_logs_days = 7
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

# Restart MySQL
sudo systemctl restart mysql
# สร้าง Replication User
mysql -u root -p

CREATE USER 'repl_user'@'10.0.0.%' IDENTIFIED BY 'ReplPassword123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'10.0.0.%';
FLUSH PRIVILEGES;

# ดู Binary Log Position
SHOW MASTER STATUS;
# จด File และ Position ไว้ เช่น:
# mysql-bin.000001 | 154

ตั้งค่า Replica Server

# บน Replica Server (IP: 10.0.0.2)
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
server-id = 2
relay_log = /var/log/mysql/mysql-relay-bin.log
read_only = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW

# Restart MySQL
sudo systemctl restart mysql
# ตั้งค่า Replication
mysql -u root -p

CHANGE MASTER TO
  MASTER_HOST='10.0.0.1',
  MASTER_USER='repl_user',
  MASTER_PASSWORD='ReplPassword123!',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=154;

START SLAVE;

# ตรวจสอบสถานะ
SHOW SLAVE STATUS\G
# ต้องเห็น:
# Slave_IO_Running: Yes
# Slave_SQL_Running: Yes
# Seconds_Behind_Master: 0

ทดสอบ MySQL Replication

# บน Primary — สร้างข้อมูลทดสอบ
mysql -u root -p myapp_db -e "
CREATE TABLE test_repl (
  id INT AUTO_INCREMENT PRIMARY KEY,
  msg VARCHAR(100),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO test_repl (msg) VALUES ('Hello from Primary');
"

# บน Replica — ตรวจสอบข้อมูล
mysql -u root -p myapp_db -e "SELECT * FROM test_repl;"
# ควรเห็นข้อมูลที่สร้างจาก Primary

ส่วนที่ 2 — PostgreSQL Streaming Replication

ตั้งค่า Primary Server

# บน Primary Server (IP: 10.0.0.1)
# สร้าง replication user
sudo -u postgres psql -c "
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'ReplPassword123!';
"

# แก้ไข postgresql.conf
sudo nano /etc/postgresql/16/main/postgresql.conf

listen_addresses = '*'
wal_level = replica
max_wal_senders = 5
wal_keep_size = 1GB
hot_standby = on
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/16/archive/%f'

# สร้างโฟลเดอร์ archive
sudo mkdir -p /var/lib/postgresql/16/archive
sudo chown postgres:postgres /var/lib/postgresql/16/archive

# แก้ไข pg_hba.conf — เพิ่มบรรทัด
sudo nano /etc/postgresql/16/main/pg_hba.conf

host    replication    replicator    10.0.0.0/24    md5

# Restart PostgreSQL
sudo systemctl restart postgresql

ตั้งค่า Standby Server

# บน Standby Server (IP: 10.0.0.2)
# หยุด PostgreSQL
sudo systemctl stop postgresql

# ลบข้อมูลเดิม
sudo rm -rf /var/lib/postgresql/16/main/*

# ทำ Base Backup จาก Primary
sudo -u postgres pg_basebackup \
  -h 10.0.0.1 \
  -U replicator \
  -D /var/lib/postgresql/16/main \
  -Fp -Xs -P -R

# คำสั่ง -R จะสร้างไฟล์ standby.signal
# และเพิ่ม connection info ใน postgresql.auto.conf

# ตรวจสอบว่ามีไฟล์ standby.signal
ls -la /var/lib/postgresql/16/main/standby.signal

# เริ่มต้น PostgreSQL
sudo systemctl start postgresql

ตรวจสอบ PostgreSQL Replication

# บน Primary — ตรวจสอบ replication status
sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
# ควรเห็น client_addr = 10.0.0.2

# ดู replication lag
sudo -u postgres psql -c "
SELECT
  client_addr,
  state,
  sent_lsn,
  write_lsn,
  flush_lsn,
  replay_lsn,
  pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes
FROM pg_stat_replication;
"

# บน Standby — ตรวจสอบว่าเป็น recovery mode
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"
# ควรได้ t (true)

# ทดสอบ — สร้างข้อมูลบน Primary
sudo -u postgres psql -d myapp_db -c "
CREATE TABLE ha_test (id SERIAL, msg TEXT);
INSERT INTO ha_test (msg) VALUES ('Replication works!');
"

# ตรวจสอบบน Standby
sudo -u postgres psql -d myapp_db -c "SELECT * FROM ha_test;"

ส่วนที่ 3 — MongoDB Replica Set

# ตั้งค่าบนทุกเครื่อง (3 เครื่อง)
sudo nano /etc/mongod.conf

# เพิ่ม replication section:
replication:
  replSetName: "rs0"

net:
  bindIp: 0.0.0.0
  port: 27017

security:
  keyFile: /etc/mongodb-keyfile

# สร้าง keyfile สำหรับ authentication
openssl rand -base64 756 > /tmp/mongodb-keyfile
sudo cp /tmp/mongodb-keyfile /etc/mongodb-keyfile
sudo chmod 400 /etc/mongodb-keyfile
sudo chown mongodb:mongodb /etc/mongodb-keyfile

# Restart MongoDB บนทุกเครื่อง
sudo systemctl restart mongod
# เชื่อมต่อเข้า Primary แล้ว initiate Replica Set
mongosh

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "10.0.0.1:27017", priority: 2 },
    { _id: 1, host: "10.0.0.2:27017", priority: 1 },
    { _id: 2, host: "10.0.0.3:27017", priority: 1 }
  ]
});

# ตรวจสอบสถานะ
rs.status()

# ตรวจสอบ config
rs.conf()

# ทดสอบเขียนข้อมูล
use myapp_db
db.ha_test.insertOne({ msg: "Replica Set works!", ts: new Date() })

# เชื่อมต่อ Secondary แล้วอ่านข้อมูล
db.getMongo().setReadPref("secondary")
db.ha_test.find()

Automatic Failover

Failover คือกระบวนการเปลี่ยน Replica ให้เป็น Primary โดยอัตโนมัติเมื่อ Primary ล่ม แต่ละระบบมีวิธีจัดการ Failover ต่างกัน:

MySQL — ไม่มี Automatic Failover ในตัว ต้องใช้เครื่องมือเพิ่มเติมเช่น MySQL Router, Orchestrator หรือ ProxySQL

PostgreSQL — ใช้ Patroni หรือ repmgr สำหรับจัดการ Failover อัตโนมัติ

MongoDB — มี Automatic Failover ในตัว เมื่อ Primary ล่ม สมาชิกที่เหลือจะเลือก Primary ใหม่อัตโนมัติ

ตั้งค่า Health Check Script

#!/bin/bash
# health_check.sh — ตรวจสอบสถานะ Replication

check_mysql() {
  STATUS=$(mysql -u root -p'YourPass' -e "SHOW SLAVE STATUS\G" 2>/dev/null)
  IO_RUNNING=$(echo "$STATUS" | grep "Slave_IO_Running" | awk '{print $2}')
  SQL_RUNNING=$(echo "$STATUS" | grep "Slave_SQL_Running" | awk '{print $2}')
  LAG=$(echo "$STATUS" | grep "Seconds_Behind_Master" | awk '{print $2}')

  if [ "$IO_RUNNING" = "Yes" ] && [ "$SQL_RUNNING" = "Yes" ]; then
    echo "MySQL Replication: OK (Lag: ${LAG}s)"
  else
    echo "MySQL Replication: FAILED"
    exit 1
  fi
}

check_postgresql() {
  IS_STANDBY=$(sudo -u postgres psql -t -c "SELECT pg_is_in_recovery();")
  if echo "$IS_STANDBY" | grep -q "t"; then
    LAG=$(sudo -u postgres psql -t -c "
      SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int;
    ")
    echo "PostgreSQL Standby: OK (Lag: ${LAG}s)"
  else
    echo "PostgreSQL: Running as Primary"
  fi
}

check_mongodb() {
  STATUS=$(mongosh --quiet --eval "rs.status().myState")
  case $STATUS in
    1) echo "MongoDB: PRIMARY" ;;
    2) echo "MongoDB: SECONDARY" ;;
    *) echo "MongoDB: UNKNOWN ($STATUS)"; exit 1 ;;
  esac
}

echo "=== Replication Health Check ==="
echo "Date: $(date)"
check_mysql 2>/dev/null || echo "MySQL: Not configured"
check_postgresql 2>/dev/null || echo "PostgreSQL: Not configured"
check_mongodb 2>/dev/null || echo "MongoDB: Not configured"

Monitoring Replication

# ติดตั้ง cron สำหรับตรวจสอบอัตโนมัติ
chmod +x /opt/health_check.sh

# เพิ่มใน crontab
crontab -e

# ตรวจสอบทุก 5 นาที
*/5 * * * * /opt/health_check.sh >> /var/log/repl_health.log 2>&1

# Alert เมื่อ Lag เกินกำหนด (ตัวอย่าง script)
cat > /opt/repl_alert.sh << 'EOF'
#!/bin/bash
MAX_LAG=60

# MySQL
LAG=$(mysql -u root -p'YourPass' -e "SHOW SLAVE STATUS\G" 2>/dev/null | grep "Seconds_Behind_Master" | awk '{print $2}')
if [ -n "$LAG" ] && [ "$LAG" -gt "$MAX_LAG" ]; then
  echo "ALERT: MySQL replication lag is ${LAG}s" | mail -s "Replication Alert" [email protected]
fi

# PostgreSQL
PG_LAG=$(sudo -u postgres psql -t -c "SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int;" 2>/dev/null)
if [ -n "$PG_LAG" ] && [ "$PG_LAG" -gt "$MAX_LAG" ]; then
  echo "ALERT: PostgreSQL replication lag is ${PG_LAG}s" | mail -s "Replication Alert" [email protected]
fi
EOF

chmod +x /opt/repl_alert.sh

Best Practices สำหรับ HA

Network — ใช้ Private Network สำหรับ Replication เพื่อลด Latency และเพิ่มความปลอดภัย ไม่ควรเปิด Replication ผ่าน Public Internet

Backup — HA ไม่ใช่ Backup ต้องมี Backup แยกต่างหากเสมอเพราะถ้าลบข้อมูลบน Primary ข้อมูลบน Replica จะถูกลบตาม

Testing — ทดสอบ Failover เป็นประจำ อย่างน้อยเดือนละครั้ง เพื่อให้มั่นใจว่าระบบทำงานได้จริงเมื่อเกิดเหตุฉุกเฉิน

Monitoring — ต้อง Monitor Replication Lag ตลอดเวลา ถ้า Lag สูงเกินไปอาจหมายถึง Replica รับโหลดไม่ไหวหรือมีปัญหา Network

Read/Write Splitting — กระจายการอ่านไปยัง Replica เพื่อลดโหลดบน Primary แต่ต้องระวังว่าข้อมูลบน Replica อาจล่าช้ากว่า Primary เล็กน้อย

สรุป

Workshop นี้ครอบคลุมการตั้งค่า High Availability สำหรับฐานข้อมูล 3 ระบบหลัก ได้แก่ MySQL Master-Slave Replication, PostgreSQL Streaming Replication และ MongoDB Replica Set แต่ละระบบมีจุดเด่นและข้อจำกัดต่างกัน การเลือกใช้ขึ้นอยู่กับความต้องการของแอพพลิเคชันและทีม

สิ่งสำคัญที่สุดคือ HA ต้องได้รับการทดสอบ ติดตาม และบำรุงรักษาอย่างสม่ำเสมอ ระบบที่ตั้งค่าแล้วไม่เคยทดสอบ Failover อาจไม่ทำงานเมื่อต้องการจริง

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

การตั้งค่าระบบ HA ต้องการเซิร์ฟเวอร์อย่างน้อย 2 เครื่องที่สื่อสารกันผ่าน Private Network พร้อม root access เพื่อตั้งค่าทุกอย่าง Cloud VPS ของ DE รองรับการใช้งานหลายเครื่องพร้อมกัน สามารถเลือก Spec ให้เหมาะกับ Primary และ Replica แยกกันได้ พร้อม SSD Storage ที่ช่วยให้ Replication ทำงานได้รวดเร็ว

สำหรับเว็บไซต์ที่ต้องการความเสถียรสูงแต่ไม่ต้องการดูแลเซิร์ฟเวอร์เอง Cloud Hosting ของ DE มีระบบ Redundancy ในตัวที่ช่วยลด Downtime โดยไม่ต้องตั้งค่า Replication เอง