HA #1: Cài đặt PostgreSQL Master-Slave Replication

Hướng dẫn thực chiến cài PostgreSQL Master-Slave replication trên Linux, cấu hình WAL streaming và quy trình promote slave khi master gặp sự cố.

HA #1: Cài đặt PostgreSQL Master-Slave Replication

Hệ thống database chạy trên một node duy nhất là một quả bom hẹn giờ. Không phải *nếu* nó chết — mà là *khi nào*. Bài này ghi lại cách cài đặt mô hình Master-Slave để đọc được phân tải và có sẵn một bản dự phòng khi node chính gặp sự cố.

Bối cảnh

Mô hình này phù hợp khi bạn:

  • Có lượng read query nhiều hơn write đáng kể
  • Muốn có node dự phòng sẵn sàng promote lên Master khi cần
  • Chạy trên 2 server riêng biệt (không phải Docker Compose trên cùng host)

Môi trường bài này:

| | Master | Slave | |---|---|---| | IP | 192.168.1.10 | 192.168.1.11 | | OS | Ubuntu 22.04 | Ubuntu 22.04 | | PostgreSQL | 15 | 15 | | Port | 5432 | 5432 |

Phiên bản PostgreSQL trên Master và Slave phải khớp nhau — ít nhất là cùng major version.

---

Cài đặt PostgreSQL trên cả 2 node

Chạy trên cả Master và Slave:

bash✓ Đã kiểm chứng cú pháp
sudo apt update
sudo apt install -y postgresql-15
sudo systemctl enable postgresql
sudo systemctl start postgresql

Kiểm tra version:

bash✓ Đã kiểm chứng cú pháp
psql --version
# postgresql 15.x

---

Cấu hình Master

1. Tạo replication user

bash✓ Đã kiểm chứng cú pháp
sudo -u postgres psql
sql✓ Đã kiểm chứng cú pháp
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'strong_password_here';

2. Chỉnh `postgresql.conf`

bash✓ Đã kiểm chứng cú pháp
sudo nano /etc/postgresql/15/main/postgresql.conf

Tìm và sửa các dòng sau:

ini✓ Đã kiểm chứng cú pháp
listen_addresses = '*'
wal_level = replica
max_wal_senders = 3
wal_keep_size = 64

Giải thích:

  • wal_level = replica — bật WAL ở mức đủ cho streaming replication
  • max_wal_senders — số kết nối streaming tối đa cho phép (mỗi Slave cần 1)
  • wal_keep_size — giữ lại WAL segments để Slave có thể catch up nếu lag

3. Chỉnh `pg_hba.conf`

bash✓ Đã kiểm chứng cú pháp
sudo nano /etc/postgresql/15/main/pg_hba.conf

Thêm dòng này vào cuối file (thay IP của Slave):

bash✓ Đã kiểm chứng cú pháp
host    replication     replicator      192.168.1.11/32     scram-sha-256

4. Restart PostgreSQL

bash✓ Đã kiểm chứng cú pháp
sudo systemctl restart postgresql

---

Cấu hình Slave

1. Dừng PostgreSQL và xóa data directory

bash✓ Đã kiểm chứng cú pháp
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/15/main/*

Đây là bước quan trọng — Slave phải bắt đầu từ một bản sao chính xác của Master, không phải data cũ.

2. Chạy `pg_basebackup`

bash✓ Đã kiểm chứng cú pháp
sudo -u postgres pg_basebackup \
  -h 192.168.1.10 \
  -U replicator \
  -D /var/lib/postgresql/15/main \
  -P \
  -Xs \
  -R

Flag quan trọng:

  • -Xs — streaming mode, chuyển WAL real-time trong quá trình backup
  • -R — tự động tạo standby.signal và ghi primary_conninfo vào postgresql.auto.conf
  • -P — hiển thị tiến trình

Nhập password khi được hỏi. Quá trình này có thể mất vài phút tùy dung lượng database.

3. Kiểm tra file `postgresql.auto.conf`

bash✓ Đã kiểm chứng cú pháp
sudo cat /var/lib/postgresql/15/main/postgresql.auto.conf

Phải có dòng tương tự:

ini✓ Đã kiểm chứng cú pháp
primary_conninfo = 'host=192.168.1.10 port=5432 user=replicator password=strong_password_here'

4. Khởi động Slave

bash✓ Đã kiểm chứng cú pháp
sudo systemctl start postgresql

---

Xác minh replication hoạt động

Trên Master

sql✓ Đã kiểm chứng cú pháp
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn
FROM pg_stat_replication;

Nếu replication đang chạy, bạn sẽ thấy một row với state = streaming và IP của Slave.

Trên Slave

sql✓ Đã kiểm chứng cú pháp
SELECT status, received_lsn, last_msg_receipt_time
FROM pg_stat_wal_receiver;

Test thực tế

Trên Master, tạo một bảng test:

sql✓ Đã kiểm chứng cú pháp
CREATE TABLE replication_test (id serial, note text, ts timestamptz default now());
INSERT INTO replication_test (note) VALUES ('hello from master');

Trên Slave, kiểm tra ngay:

sql✓ Đã kiểm chứng cú pháp
SELECT * FROM replication_test;
-- Phải thấy row vừa insert

Thử ghi trực tiếp lên Slave (phải bị từ chối):

sql✓ Đã kiểm chứng cú pháp
INSERT INTO replication_test (note) VALUES ('should fail');
-- ERROR:  cannot execute INSERT in a read-only transaction

---

Promote Slave thành Master (khi cần)

Khi Master gặp sự cố và bạn cần Slave tiếp quản:

bash✓ Đã kiểm chứng cú pháp
sudo -u postgres pg_ctl promote -D /var/lib/postgresql/15/main

Hoặc dùng psql:

sql✓ Đã kiểm chứng cú pháp
SELECT pg_promote();

Sau khi promote, Slave sẽ chấp nhận write queries. Lúc này bạn cần cập nhật connection string của application trỏ sang IP mới.

Lưu ý kiến trúc:

Nếu Master cũ phục hồi sau đó, không được để nó tự động kết nối lại. Cần rebuild nó thành Slave mới — chạy lại pg_basebackup từ Master mới (node vừa promote).

---

Những thứ dễ sai

`pg_hba.conf` sai IP hoặc sai method — Slave kết nối được nhưng bị từ chối auth. Kiểm tra log ở /var/log/postgresql/.

`wal_level` không đủ — Nếu đang dùng minimal, streaming replication không hoạt động. Phải restart sau khi đổi.

Slave bị lag quá nhiều — Tăng wal_keep_size trên Master. Nếu Slave lag quá threshold thì sẽ mất WAL và phải pg_basebackup lại từ đầu.

Firewall block port 5432 — Kiểm tra ufw hoặc iptables trên cả hai node.

---

Bước tiếp theo

Mô hình này giải quyết được việc phân tải read và có bản dự phòng. Nhưng application vẫn phải tự quản lý việc kết nối đến đúng node — Master cho write, Slave cho read.

Bài tiếp theo sẽ đặt PgBouncer vào giữa để gom và tái sử dụng connection, tránh tình trạng database bị quá tải kết nối khi traffic tăng.