Database Clustering คือการนำเซิร์ฟเวอร์ฐานข้อมูลหลายเครื่องมาทำงานร่วมกันเสมือนเป็นระบบเดียว เพื่อเพิ่มความสามารถในการรองรับ Load ที่สูงขึ้น เพิ่ม Availability ให้ระบบทำงานได้ต่อเนื่องแม้เซิร์ฟเวอร์บางเครื่องล่ม และกระจายภาระงานออกไปยังหลาย Node เพื่อลดคอขวดของระบบ
บทความนี้จะอธิบายแนวคิดและวิธีตั้งค่า Clustering สำหรับฐานข้อมูลยอดนิยมอย่าง MySQL (Group Replication), PostgreSQL (Patroni + etcd) และ MongoDB (Replica Set) พร้อมตัวอย่างการตั้งค่าจริงที่สามารถนำไปใช้งานบน Cloud VPS ได้ทันที
ทำไมต้อง Cluster ฐานข้อมูล
ฐานข้อมูลที่ทำงานบนเซิร์ฟเวอร์เครื่องเดียวมีข้อจำกัดหลายประการ เมื่อเซิร์ฟเวอร์ล่มทุกอย่างหยุดทำงาน เมื่อ Traffic เพิ่มขึ้นไม่สามารถกระจาย Load ได้ และเมื่อต้อง Maintenance ต้องหยุดให้บริการ การทำ Clustering แก้ปัญหาเหล่านี้ได้ทั้งหมด โดยระบบ Cluster ให้ประโยชน์หลัก 3 ด้าน คือ High Availability ที่ทำให้ระบบทำงานได้แม้ Node ใด Node หนึ่งล่ม, Horizontal Scaling ที่กระจาย Read Load ไปยังหลาย Node และ Zero-downtime Maintenance ที่สามารถอัพเกรดทีละ Node ได้โดยไม่ต้องหยุดระบบ
รูปแบบ Clustering ที่ควรรู้
ก่อนเริ่มตั้งค่า ควรเข้าใจรูปแบบหลัก ๆ ของการทำ Cluster ซึ่งมีหลายแบบขึ้นอยู่กับความต้องการ แบบแรกคือ Primary-Replica (Master-Slave) ที่มี Node หนึ่งรับ Write และ Node อื่น ๆ รับเฉพาะ Read เหมาะสำหรับระบบที่มี Read มากกว่า Write แบบที่สองคือ Multi-Primary (Multi-Master) ที่ทุก Node สามารถรับทั้ง Read และ Write ได้ เหมาะสำหรับระบบที่ต้องการ Write Scalability แบบที่สามคือ Shared-Nothing Clustering ที่แต่ละ Node เก็บข้อมูลคนละส่วน (Sharding) เหมาะสำหรับข้อมูลขนาดใหญ่มาก
MySQL Group Replication
MySQL Group Replication เป็นฟีเจอร์ที่มาพร้อมกับ MySQL 8.0 ขึ้นไป รองรับทั้งโหมด Single-Primary และ Multi-Primary สามารถตรวจจับ Node ที่ล่มและ Failover อัตโนมัติ รวมถึงรับประกัน Consistency ของข้อมูลด้วย Paxos Protocol
สถาปัตยกรรมที่ใช้
ตัวอย่างนี้จะตั้งค่า Cluster แบบ 3 Node โดยใช้ Single-Primary Mode ประกอบด้วย Node 1 (Primary) ที่ IP 10.0.0.1, Node 2 (Secondary) ที่ IP 10.0.0.2 และ Node 3 (Secondary) ที่ IP 10.0.0.3 ทุก Node ใช้ MySQL 8.0 บน Ubuntu
ตั้งค่า Node 1 (Primary)
# แก้ไข Configuration
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
# Network
server-id = 1
bind-address = 0.0.0.0
port = 3306
# Binary Log (จำเป็นสำหรับ Group Replication)
log_bin = mysql-bin
binlog_format = ROW
binlog_checksum = NONE
log_replica_updates = ON
gtid_mode = ON
enforce_gtid_consistency = ON
# Group Replication Settings
plugin_load_add = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot = OFF
group_replication_local_address = "10.0.0.1:33061"
group_replication_group_seeds = "10.0.0.1:33061,10.0.0.2:33061,10.0.0.3:33061"
group_replication_bootstrap_group = OFF
# InnoDB Settings
innodb_buffer_pool_size = 1G
innodb_flush_log_at_trx_commit = 1
# Replica Settings
replica_parallel_workers = 4
replica_preserve_commit_order = ON
# Restart MySQL
sudo systemctl restart mysql
# เข้า MySQL Console
sudo mysql
# สร้าง User สำหรับ Replication
CREATE USER 'repl_user'@'%' IDENTIFIED BY 'ReplSecurePass123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';
GRANT CONNECTION_ADMIN ON *.* TO 'repl_user'@'%';
GRANT BACKUP_ADMIN ON *.* TO 'repl_user'@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO 'repl_user'@'%';
FLUSH PRIVILEGES;
# ตั้งค่า Recovery Channel
CHANGE REPLICATION SOURCE TO SOURCE_USER='repl_user', SOURCE_PASSWORD='ReplSecurePass123!' FOR CHANNEL 'group_replication_recovery';
# Bootstrap Group (ทำเฉพาะ Node แรกเท่านั้น)
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;
# ตรวจสอบสถานะ
SELECT * FROM performance_schema.replication_group_members;
ตั้งค่า Node 2 และ Node 3
Configuration ของ Node 2 และ 3 เหมือน Node 1 เกือบทั้งหมด แต่ต้องเปลี่ยน server-id และ group_replication_local_address ให้ตรงกับ IP ของแต่ละ Node
# Node 2 — เปลี่ยนค่าเหล่านี้
server-id = 2
group_replication_local_address = "10.0.0.2:33061"
# Node 3 — เปลี่ยนค่าเหล่านี้
server-id = 3
group_replication_local_address = "10.0.0.3:33061"
# หลัง Restart MySQL บน Node 2 และ 3
# สร้าง User เหมือน Node 1
CREATE USER 'repl_user'@'%' IDENTIFIED BY 'ReplSecurePass123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'%';
GRANT CONNECTION_ADMIN ON *.* TO 'repl_user'@'%';
GRANT BACKUP_ADMIN ON *.* TO 'repl_user'@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO 'repl_user'@'%';
FLUSH PRIVILEGES;
# ตั้งค่า Recovery Channel
CHANGE REPLICATION SOURCE TO SOURCE_USER='repl_user', SOURCE_PASSWORD='ReplSecurePass123!' FOR CHANNEL 'group_replication_recovery';
# Join Group (ไม่ต้อง Bootstrap)
START GROUP_REPLICATION;
# ตรวจสอบว่า Node เข้า Group สำเร็จ
SELECT * FROM performance_schema.replication_group_members;
ทดสอบ Failover
# ตรวจสอบว่า Node ไหนเป็น Primary
SELECT MEMBER_HOST, MEMBER_ROLE
FROM performance_schema.replication_group_members;
# หยุด Primary Node เพื่อทดสอบ Failover
# (บน Node 1)
sudo systemctl stop mysql
# ตรวจสอบบน Node 2 หรือ 3 — ควรเห็น Primary ใหม่
SELECT MEMBER_HOST, MEMBER_ROLE
FROM performance_schema.replication_group_members;
# เริ่ม Node 1 กลับมาใหม่
sudo systemctl start mysql
sudo mysql -e "START GROUP_REPLICATION;"
SELECT * FROM performance_schema.replication_group_members;
PostgreSQL Clustering ด้วย Patroni + etcd
PostgreSQL ไม่มี Built-in Clustering เหมือน MySQL Group Replication แต่ใช้ Patroni ซึ่งเป็นเครื่องมือจัดการ High Availability ที่ได้รับความนิยมสูงสุดในวงการ โดย Patroni ทำหน้าที่ Monitor สถานะของทุก Node จัดการ Failover อัตโนมัติ และใช้ etcd เป็น Distributed Configuration Store สำหรับเก็บสถานะของกลุ่มเครื่อง
ติดตั้ง etcd บนทุก Node
# ติดตั้ง etcd
sudo apt install -y etcd
# แก้ไข Configuration (Node 1 ตัวอย่าง)
sudo nano /etc/default/etcd
ETCD_NAME="node1"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://10.0.0.1:2380"
ETCD_LISTEN_CLIENT_URLS="http://10.0.0.1:2379,http://127.0.0.1:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.0.0.1:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://10.0.0.1:2379"
ETCD_INITIAL_CLUSTER="node1=http://10.0.0.1:2380,node2=http://10.0.0.2:2380,node3=http://10.0.0.3:2380"
ETCD_INITIAL_CLUSTER_TOKEN="pg-cluster-token"
ETCD_INITIAL_CLUSTER_STATE="new"
# เริ่ม etcd Service
sudo systemctl restart etcd
sudo systemctl enable etcd
# ตรวจสอบ Cluster Health
etcdctl cluster-health
ติดตั้ง Patroni
# ติดตั้ง PostgreSQL และ Patroni
sudo apt install -y postgresql postgresql-client python3-pip
sudo pip3 install patroni[etcd] --break-system-packages
# หยุด PostgreSQL Default Service (Patroni จะจัดการเอง)
sudo systemctl stop postgresql
sudo systemctl disable postgresql
สร้าง Patroni Configuration
# สร้างไฟล์ Configuration สำหรับ Node 1
sudo nano /etc/patroni/patroni.yml
scope: pg-cluster
name: node1
restapi:
listen: 10.0.0.1:8008
connect_address: 10.0.0.1:8008
etcd:
hosts: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
parameters:
max_connections: 200
shared_buffers: 1GB
effective_cache_size: 3GB
work_mem: 16MB
wal_level: replica
max_wal_senders: 5
max_replication_slots: 5
hot_standby: "on"
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
- host replication repl_user 10.0.0.0/24 md5
- host all all 10.0.0.0/24 md5
- host all all 0.0.0.0/0 md5
users:
admin:
password: AdminPass123!
options:
- createrole
- createdb
repl_user:
password: ReplPass456!
options:
- replication
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.0.0.1:5432
data_dir: /var/lib/postgresql/14/main
bin_dir: /usr/lib/postgresql/14/bin
authentication:
replication:
username: repl_user
password: ReplPass456!
superuser:
username: postgres
password: PostgresPass789!
# สร้าง Systemd Service สำหรับ Patroni
sudo nano /etc/systemd/system/patroni.service
[Unit]
Description=Patroni PostgreSQL Cluster Manager
After=syslog.target network.target etcd.service
[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=process
TimeoutSec=30
Restart=no
[Install]
WantedBy=multi-user.target
# เริ่ม Patroni
sudo systemctl daemon-reload
sudo systemctl start patroni
sudo systemctl enable patroni
# ตรวจสอบสถานะ Cluster
patronictlctl -c /etc/patroni/patroni.yml list
สำหรับ Node 2 และ 3 ใช้ Configuration เดียวกันแต่เปลี่ยน name, restapi listen/connect_address และ postgresql connect_address ให้ตรงกับ IP ของแต่ละ Node เมื่อเริ่ม Patroni บน Node 2 และ 3 ระบบจะ Clone ข้อมูลจาก Primary มาอัตโนมัติ
MongoDB Replica Set
MongoDB รองรับ Clustering ผ่าน Replica Set ซึ่งเป็นกลุ่มของ mongod Instance ที่เก็บข้อมูลชุดเดียวกัน ประกอบด้วย Primary 1 ตัวและ Secondary หลายตัว โดย Primary รับ Write ทั้งหมดและ Secondary จะ Replicate ข้อมูลมาอัตโนมัติ
ตั้งค่า Replica Set
# แก้ไข Configuration บนทุก Node
sudo nano /etc/mongod.conf
# Node 1 Configuration
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
net:
port: 27017
bindIp: 0.0.0.0
replication:
replSetName: "rs0"
security:
keyFile: /etc/mongodb/keyfile
authorization: enabled
# สร้าง Key File สำหรับ Authentication ระหว่าง Node
sudo openssl rand -base64 756 > /tmp/keyfile
sudo mv /tmp/keyfile /etc/mongodb/keyfile
sudo chown mongodb:mongodb /etc/mongodb/keyfile
sudo chmod 400 /etc/mongodb/keyfile
# Copy keyfile ไปยัง Node 2 และ 3 (ต้องเป็นไฟล์เดียวกันทุก Node)
scp /etc/mongodb/keyfile [email protected]:/etc/mongodb/keyfile
scp /etc/mongodb/keyfile [email protected]:/etc/mongodb/keyfile
# Restart MongoDB บนทุก Node
sudo systemctl restart mongod
# เชื่อมต่อ Node 1 แล้วเริ่มต้น Replica Set
mongosh
# Initiate Replica Set
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()
# สร้าง Admin User
use admin
db.createUser({
user: "adminUser",
pwd: "AdminSecure123!",
roles: [{ role: "root", db: "admin" }]
})
ทดสอบ Replica Set
# เขียนข้อมูลบน Primary
mongosh -u adminUser -p 'AdminSecure123!' --authenticationDatabase admin
use testdb
db.test.insertOne({ name: "cluster test", timestamp: new Date() })
# อ่านข้อมูลบน Secondary (ต้องเปิด Read Preference)
# เชื่อมต่อ Node 2
mongosh --host 10.0.0.2 -u adminUser -p 'AdminSecure123!' --authenticationDatabase admin
db.getMongo().setReadPref("secondary")
use testdb
db.test.find()
# ทดสอบ Failover — หยุด Primary
# (บน Node 1)
sudo systemctl stop mongod
# ตรวจสอบบน Node 2 — ควรเห็น Primary ใหม่ถูกเลือก
mongosh --host 10.0.0.2 -u adminUser -p 'AdminSecure123!' --authenticationDatabase admin
rs.status()
Load Balancing สำหรับ Database Cluster
หลังจากตั้งค่า Cluster แล้ว ต้องมี Load Balancer เพื่อกระจาย Connection จากแอปพลิเคชันไปยัง Node ต่าง ๆ เครื่องมือที่นิยมใช้ ได้แก่ HAProxy สำหรับ TCP Load Balancing ที่รองรับ Health Check, ProxySQL สำหรับ MySQL โดยเฉพาะที่แยก Read/Write ได้อัตโนมัติ และ PgBouncer สำหรับ PostgreSQL Connection Pooling
ตัวอย่าง HAProxy สำหรับ MySQL Cluster
# ติดตั้ง HAProxy
sudo apt install -y haproxy
# แก้ไข Configuration
sudo nano /etc/haproxy/haproxy.cfg
global
log /dev/log local0
maxconn 4096
defaults
log global
mode tcp
timeout connect 5s
timeout client 30s
timeout server 30s
# MySQL Write (Primary only)
frontend mysql_write
bind *:3306
default_backend mysql_primary
backend mysql_primary
option tcp-check
server node1 10.0.0.1:3306 check port 3306
server node2 10.0.0.2:3306 check port 3306 backup
server node3 10.0.0.3:3306 check port 3306 backup
# MySQL Read (All nodes — Round Robin)
frontend mysql_read
bind *:3307
default_backend mysql_replicas
backend mysql_replicas
balance roundrobin
option tcp-check
server node1 10.0.0.1:3306 check port 3306
server node2 10.0.0.2:3306 check port 3306
server node3 10.0.0.3:3306 check port 3306
# Stats Page
listen stats
bind *:8080
mode http
stats enable
stats uri /stats
stats auth admin:HaproxyPass123!
# เริ่ม HAProxy
sudo systemctl restart haproxy
sudo systemctl enable haproxy
# ทดสอบเชื่อมต่อผ่าน HAProxy
mysql -h 127.0.0.1 -P 3306 -u appuser -p # Write (Primary)
mysql -h 127.0.0.1 -P 3307 -u appuser -p # Read (Round Robin)
Monitoring Cluster Health
การ Monitor สถานะของ Cluster เป็นสิ่งจำเป็นเพื่อตรวจจับปัญหาก่อนที่จะส่งผลกระทบต่อผู้ใช้งาน คำสั่งพื้นฐานสำหรับตรวจสอบแต่ละระบบมีดังนี้
# MySQL Group Replication — ตรวจสถานะ Members
SELECT MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
# ตรวจ Replication Lag
SELECT * FROM performance_schema.replication_group_member_stats\G
# PostgreSQL Patroni — ตรวจสถานะ Cluster
patronictlctl -c /etc/patroni/patroni.yml list
# ตรวจ Replication Lag
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
(sent_lsn - replay_lsn) AS replication_lag
FROM pg_stat_replication;
# MongoDB — ตรวจสถานะ Replica Set
rs.status()
rs.printReplicationInfo()
rs.printSecondaryReplicationInfo()
ข้อควรระวังในการตั้งค่า Cluster
การตั้งค่า Cluster มีหลายจุดที่ต้องระวัง ประการแรก จำนวน Node ควรเป็นเลขคี่ (3, 5, 7) เพื่อให้ระบบ Quorum ทำงานได้ถูกต้อง ถ้ามี Node เป็นเลขคู่อาจเกิด Split-brain ได้ ประการที่สอง Network Latency ระหว่าง Node ต้องต่ำ ควรอยู่ใน Data Center เดียวกันหรือใช้ Private Network เชื่อมต่อ ประการที่สาม Disk I/O ต้องเร็วเพียงพอ เพราะ Replication ต้องเขียนข้อมูลทั้ง Local และส่งไปยัง Node อื่น ควรใช้ SSD NVMe ประการที่สี่ Backup ยังคงจำเป็น แม้จะมี Replication แล้วก็ตาม เพราะ Replication ป้องกัน Hardware Failure ได้ แต่ไม่ป้องกัน Human Error เช่น การลบข้อมูลผิด
สรุป
การตั้งค่า Cluster สำหรับฐานข้อมูลเป็นขั้นตอนสำคัญสำหรับระบบ Production ที่ต้องการ High Availability และรองรับ Load สูง MySQL Group Replication เหมาะสำหรับผู้ที่ใช้ MySQL อยู่แล้วเพราะเป็น Built-in Feature ส่วน PostgreSQL ใช้ Patroni + etcd เป็นคู่ที่ได้รับการพิสูจน์แล้วว่าเสถียรและยืดหยุ่น และ MongoDB Replica Set ตั้งค่าได้ง่ายที่สุดเพราะเป็น Native Feature การเลือกรูปแบบ Clustering ขึ้นอยู่กับฐานข้อมูลที่ใช้ ความต้องการด้าน Consistency และงบประมาณ
แนะนำบริการ DE
การรัน Database Cluster ต้องการเซิร์ฟเวอร์อย่างน้อย 3 เครื่องที่มี Network Latency ต่ำระหว่างกัน และ Disk I/O ที่เร็วสำหรับ Replication Cloud VPS ของ DE รองรับ Private Network ระหว่าง VPS พร้อม SSD NVMe ที่มีความเร็วสูง เหมาะสำหรับการตั้งค่า Database Cluster ที่ต้องการประสิทธิภาพสูง
สำหรับโปรเจกต์ที่ไม่ต้องการจัดการเซิร์ฟเวอร์เอง Cloud Hosting ของ DE เป็นทางเลือกที่สะดวกพร้อม Managed Infrastructure และระบบ High Availability ให้ใช้งานได้ทันที

