HA #3: Cài đặt PgPool-II — Connection Clustering và Auto Failover

Thiết lập PgPool-II quản lý connection clustering, điều hướng tải read-write thông minh và tự động failover cho cơ sở dữ liệu PostgreSQL chuẩn Enterprise.

HA #3: Cài đặt PgPool-II — Connection Clustering và Auto Failover

Hai bài trước đã có replication chạy và connection pool hoạt động. Nhưng application vẫn phải biết: *"Đây là write query, gửi đến Master. Đây là read query, gửi đến Slave."* PgPool-II lấy trách nhiệm đó ra khỏi tay application — nó nhìn vào SQL, tự quyết định node nào xử lý, và tự động switch sang Slave khi Master đột ngột biến mất.

PgPool-II làm được gì?

| Tính năng | Mô tả | |---|---| | Connection pooling | Tương tự PgBouncer nhưng ít linh hoạt hơn | | Query routing | SELECT → Slave, INSERT/UPDATE → Master | | Load balancing | Phân phối SELECT đều giữa các Slave | | Online recovery | Rebuild Slave bị lỗi tự động | | Watchdog | Phát hiện node chết, promote Slave |

Khi nào dùng PgPool thay vì PgBouncer? Dùng PgPool khi bạn muốn transparent load balancing và auto-failover mà không thay đổi application. Dùng PgBouncer khi chỉ cần connection pooling đơn giản, hiệu năng cao, ít overhead. Nhiều setup production dùng cả hai: PgPool phía ngoài để route, PgBouncer phía trong để pool.

---

Môi trường

| Node | IP | Role | |---|---|---| | pgpool | 192.168.1.9 | PgPool-II proxy | | master | 192.168.1.10 | PostgreSQL Master | | slave1 | 192.168.1.11 | PostgreSQL Slave 1 |

---

Cài đặt PgPool-II

bash✓ Đã kiểm chứng cú pháp
sudo apt update
sudo apt install -y pgpool2

Kiểm tra version:

bash✓ Đã kiểm chứng cú pháp
pgpool --version

---

Cấu hình

File cấu hình chính: /etc/pgpool2/pgpool.conf

Cấu hình kết nối backend

bash✓ Đã kiểm chứng cú pháp
sudo nano /etc/pgpool2/pgpool.conf
ini✓ Đã kiểm chứng cú pháp
# --- Backend nodes ---
backend_hostname0 = '192.168.1.10'
backend_port0 = 5432
backend_weight0 = 1
backend_data_directory0 = '/var/lib/postgresql/15/main'
backend_flag0 = 'ALLOW_TO_FAILOVER'

backend_hostname1 = '192.168.1.11'
backend_port1 = 5432
backend_weight1 = 1
backend_data_directory1 = '/var/lib/postgresql/15/main'
backend_flag1 = 'ALLOW_TO_FAILOVER'

# --- Listen ---
listen_addresses = '*'
port = 5432

# --- Connection pooling ---
num_init_children = 32
max_pool = 4
connection_cache = on

# --- Load balancing ---
load_balance_mode = on
ignore_leading_white_space = on

# --- Health check ---
health_check_period = 10
health_check_timeout = 20
health_check_user = 'pgpool_health'
health_check_password = 'health_pass'
health_check_database = 'postgres'
health_check_max_retries = 3

# --- Failover ---
failover_command = '/etc/pgpool2/failover.sh %d %h %p %D %m %H %M %P %r %R'
failover_on_backend_error = on

# --- Replication mode ---
replication_mode = off
master_slave_mode = on
master_slave_sub_mode = 'stream'
sr_check_period = 10
sr_check_user = 'replicator'
sr_check_password = 'strong_password_here'

Tạo user health check trên PostgreSQL

Trên Master, tạo user riêng cho PgPool health check:

sql✓ Đã kiểm chứng cú pháp
CREATE ROLE pgpool_health WITH LOGIN PASSWORD 'health_pass';
GRANT CONNECT ON DATABASE postgres TO pgpool_health;

File `pool_hba.conf`

Cho phép connection từ application đến PgPool:

bash✓ Đã kiểm chứng cú pháp
sudo nano /etc/pgpool2/pool_hba.conf
bash✓ Đã kiểm chứng cú pháp
host    all     all     0.0.0.0/0     scram-sha-256

File `pool_passwd`

PgPool cần password file riêng. Tạo bằng công cụ pg_md5:

bash✓ Đã kiểm chứng cú pháp
pg_md5 --md5auth --username=myappuser -p
# Nhập password khi được hỏi

File sẽ được ghi vào /etc/pgpool2/pool_passwd.

---

Script failover

Tạo script được gọi khi PgPool phát hiện Master chết:

bash✓ Đã kiểm chứng cú pháp
sudo nano /etc/pgpool2/failover.sh
bash✓ Đã kiểm chứng cú pháp
#!/bin/bash
# $1: node id của node bị lỗi
# $7: hostname của node sẽ được promote (new master)

FAILED_NODE_HOST=$2
NEW_MASTER_HOST=$7
NEW_MASTER_PORT=$8
NEW_MASTER_DATA=$9

/usr/bin/ssh -T -i /var/lib/postgresql/.ssh/id_rsa \
  postgres@${NEW_MASTER_HOST} \
  "pg_ctl promote -D ${NEW_MASTER_DATA}"
bash✓ Đã kiểm chứng cú pháp
sudo chmod +x /etc/pgpool2/failover.sh

Script này SSH vào Slave và chạy pg_ctl promote. Cần setup SSH key từ pgpool server đến các PostgreSQL node trước.

Setup SSH key

Trên pgpool server:

bash✓ Đã kiểm chứng cú pháp
sudo -u postgres ssh-keygen -t rsa -b 4096 -N "" -f /var/lib/postgresql/.ssh/id_rsa

Copy public key đến từng PostgreSQL node:

bash✓ Đã kiểm chứng cú pháp
sudo -u postgres ssh-copy-id [email protected]
sudo -u postgres ssh-copy-id [email protected]

---

Khởi động PgPool

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

---

Xác minh hoạt động

Xem trạng thái nodes

bash✓ Đã kiểm chứng cú pháp
psql -h 127.0.0.1 -p 5432 -U myappuser -d myapp -c "SHOW pool_nodes;"

Output mong đợi:

bash✓ Đã kiểm chứng cú pháp
 node_id |    hostname    | port | status | lb_weight | role
---------+----------------+------+--------+-----------+--------
 0       | 192.168.1.10   | 5432 | up     | 0.500000  | primary
 1       | 192.168.1.11   | 5432 | up     | 0.500000  | standby

Test load balancing

Chạy nhiều SELECT và xem PgPool phân phối:

bash✓ Đã kiểm chứng cú pháp
for i in {1..10}; do
  psql -h 127.0.0.1 -p 5432 -U myappuser -d myapp \
    -c "SELECT inet_server_addr();" -t
done

Kết quả nên xen kẽ giữa 192.168.1.10192.168.1.11.

Test failover

Dừng PostgreSQL trên Master:

bash✓ Đã kiểm chứng cú pháp
# Trên 192.168.1.10
sudo systemctl stop postgresql

Sau vài giây (bằng health_check_period), PgPool sẽ phát hiện và gọi failover.sh. Kiểm tra lại:

bash✓ Đã kiểm chứng cú pháp
psql -h 127.0.0.1 -p 5432 -U myappuser -d myapp -c "SHOW pool_nodes;"

Slave sẽ được promote, hiện role = primary. Application tiếp tục chạy mà không cần thay đổi connection string.

---

Những thứ hay gặp

`health_check_user` không có quyền CONNECT — PgPool báo node down dù PostgreSQL đang chạy. Kiểm tra pg_hba.conf và grant quyền connect.

Failover script không chạy được do SSH — Kiểm tra SSH key, permission của .ssh/authorized_keys. File authorized_keys phải chmod 600, thư mục .ssh/ phải chmod 700.

Query bị route sai node — PgPool parse SQL để quyết định routing. Một số query phức tạp có thể bị nhầm. Dùng /*NO LOAD BALANCE*/ hint để ép query về Master:

sql✓ Đã kiểm chứng cú pháp
/*NO LOAD BALANCE*/ SELECT * FROM sensitive_table;

Split-brain sau failover — Nếu Master cũ phục hồi, đừng để nó tự join lại cluster. Cần rebuild nó thành Slave mới (chạy pg_basebackup từ Master hiện tại).

---

Tóm tắt série HA PostgreSQL

| Bài | Công cụ | Giải quyết | |---|---|---| | #1 | Master-Slave Replication | Phân tải read, có bản dự phòng | | #2 | PgBouncer | Tái sử dụng connection, giảm overhead | | #3 | PgPool-II | Auto routing, load balancing, failover |

Ba lớp này cộng lại tạo thành một PostgreSQL cluster có khả năng chịu tải cao, tự phục hồi khi node chính gặp sự cố, và trong suốt với application.

---

*Làm chủ thiết kế hệ thống chịu tải cao và cơ sở dữ liệu với Khóa học Java Full-stack & Distributed Systems. Doanh nghiệp cần Tư vấn kiến trúc High Availability hoặc tìm hiểu Dịch vụ phát triển phần mềm và kiến trúc hệ thống của VCoderlog? Hãy liên hệ ngay hôm nay.*