Настройка репліцирування Master-Slave для веб-додатків
Реплікація вирішує дві задачі: відказостійкість (failover) та масштабування читання. Розглядаємо PostgreSQL streaming replication — стандартний та надійний підхід.
Архітектура
App сервери
│
├── запис ──▶ Primary (master)
│ │
│ WAL stream
│ │
└── читання ──▶ Replica 1 (hot standby)
Replica 2 (hot standby)
Hot standby — реплика приймає SELECT-запити. Cold standby — тільки для failover, без читання.
Настройка Primary
postgresql.conf на основному сервері:
# Replication
wal_level = replica # мінімум для streaming replication
max_wal_senders = 5 # максимум одночасних репліцірунків з'єднань
wal_keep_size = 1GB # скільки WAL тримати на диску для відстаючої реплік
max_replication_slots = 5 # фізичні слоти реплікації
# Performance
synchronous_commit = on # on = синхронний запис WAL, off = швидше, ризик втратити 1 транзакцію
pg_hba.conf на primary — дозволяємо підключення репліцірунка:
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 10.0.1.0/24 scram-sha-256
Створюємо користувача для реплікації:
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'strong_password';
Настройка Replica (PostgreSQL 12+)
На реплікі запускаємо pg_basebackup для початкової синхронізації:
# На сервері реплік, PostgreSQL зупинений
pg_basebackup \
-h 10.0.1.10 \ # IP primary
-U replicator \
-D /var/lib/postgresql/14/main \
-P \ # прогрес
-Xs \ # включити WAL stream
-R # створити standby.signal та записати primary_conninfo
Прапор -R створює standby.signal та додає в postgresql.auto.conf:
primary_conninfo = 'host=10.0.1.10 port=5432 user=replicator password=strong_password application_name=replica1'
primary_slot_name = 'replica1_slot'
postgresql.conf на реплікі:
hot_standby = on # приймати SELECT на реплікі
hot_standby_feedback = on # реплік повідомляє primary про свої транзакції (запобігає vacuum race)
max_standby_streaming_delay = 30s
Слоти реплікації
Слот гарантує, що primary не видалить WAL-сегменти, поки реплік їх не отримає:
-- На primary
SELECT pg_create_physical_replication_slot('replica1_slot');
-- Перевірка статусу
SELECT slot_name, active, restart_lsn, confirmed_flush_lsn,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag_bytes
FROM pg_replication_slots;
Небезпека слотів: якщо реплік надовго відключиться — primary буде накопичувати WAL-файли. Контролюємо:
# postgresql.conf на primary
max_slot_wal_keep_size = 10GB # PostgreSQL 13+: максимум WAL для слота
Моніторинг репліцірунків лага
-- На primary: статус усіх реплік
SELECT
application_name,
client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS replay_lag_bytes,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;
-- На реплікі: поточний лаг
SELECT
now() - pg_last_xact_replay_timestamp() AS replication_lag_seconds,
pg_is_in_recovery() AS is_replica;
Настройка додатку
На рівні додатку маршрутизуємо запити: запис → primary, читання → replica.
Для Node.js + Sequelize:
const sequelize = new Sequelize({
dialect: 'postgres',
replication: {
read: [
{ host: '10.0.1.11', username: 'app', password: process.env.DB_PASS, database: 'mydb' },
{ host: '10.0.1.12', username: 'app', password: process.env.DB_PASS, database: 'mydb' },
],
write: {
host: '10.0.1.10', username: 'app', password: process.env.DB_PASS, database: 'mydb',
},
},
pool: { max: 10, idle: 10000 },
});
Для Python (SQLAlchemy) — через custom routing або бібліотеку sqlalchemy-rwconn.
PgBouncer як прокси
Рекомендується ставити PgBouncer перед кожним сервером БД:
App → PgBouncer (primary:6432) → PostgreSQL Primary :5432
App → PgBouncer (replica:6432) → PostgreSQL Replica :5432
PgBouncer зменшує кількість реальних з'єднань до PostgreSQL — важливо при сотнях воркерів додатку.
Автоматичний failover
Patroni — стандарт для автоматичного failover PostgreSQL в production:
# /etc/patroni/config.yml (фрагмент)
scope: postgres-cluster
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: 10.0.1.10:8008
etcd:
hosts: 10.0.1.20:2379,10.0.1.21:2379,10.0.1.22:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576 # 1MB
pg_hba:
- host replication replicator 10.0.1.0/24 scram-sha-256
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.0.1.10:5432
data_dir: /var/lib/postgresql/14/main
parameters:
max_connections: 200
wal_level: replica
max_wal_senders: 5
Patroni використовує etcd (або Consul, ZooKeeper) як distributed lock. При отказі primary — автоматично промоутує реплік з найменшим лагом.
Графіки
Ручна настройка streaming replication (primary + 1–2 реплік, без failover): 1 день. Установка та настройка Patroni з etcd та HAProxy для автоматичного failover: 2–3 дні.







