PostgreSQL Streaming Replication — primary i standby
Opublikowano: 10 kwietnia 2026 · Kategoria: VPS i serwery
Backup jest niezbędny, ale nie wystarczy. Gdy serwer PostgreSQL ulega awarii, przywrócenie bazy z pg_dump może zająć godziny. Streaming Replication utrzymuje w trybie gotowości jeden lub kilka serwerów standby, które mają aktualną kopię bazy i mogą w ciągu sekund przejąć rolę primary. Dodatkowo standby może obsługiwać zapytania read-only, odciążając primary. Ten artykuł pokazuje jak skonfigurować replikację fizyczną PostgreSQL 15/16 na dwóch serwerach, uruchomić hot standby i przeprowadzić kontrolowany failover.
Konfiguracja primary — postgresql.conf i pg_hba.conf
# Na PRIMARY: /etc/postgresql/16/main/postgresql.conf # Wlacz archiwizacje WAL wal_level = replica # replica lub logical max_wal_senders = 5 # liczba rownoleglych polaczen replikacji wal_keep_size = 1GB # trzymaj WAL do synchronizacji (PostgreSQL 13+) # Lub uzywaj replication slots zamiast wal_keep_size # Hot standby — pozwala replike obsługiwac SELECTy hot_standby = on # Dla synchronicznej replikacji (opcjonalnie — wolniejsza, ale brak utraty danych) # synchronous_standby_names = 'standby1' # synchronous_commit = remote_apply # Archiwum WAL (opcjonalne, dla point-in-time recovery) archive_mode = on archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f' # Restart PostgreSQL po zmianach sudo systemctl restart postgresql # ---- # Na PRIMARY: /etc/postgresql/16/main/pg_hba.conf # Dodaj linię zezwalająca standy na relikację: # TYPE DATABASE USER ADDRESS METHOD replication replicator 192.168.1.11/32 scram-sha-256 # (192.168.1.11 = IP serwera standby) # Utwórz uzytkownika replikacji sudo -u postgres psql -c " CREATE USER replicator WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'strong_repl_pass'; " # Przeladuj konfiguracje pg_hba bez restartu sudo -u postgres psql -c "SELECT pg_reload_conf();"
Replication slot — gwarancja zachowania WAL
# Na PRIMARY: stworzenie physical replication slot
sudo -u postgres psql -c "
SELECT pg_create_physical_replication_slot('standby1_slot');
"
# Sprawdz dostepne sloty
sudo -u postgres psql -c "SELECT * FROM pg_replication_slots;"
# slot_name | active | wal_status | safe_wal_size
# standby1_slot| f | reserved | ...
# Monitorowanie uzytych slotow (pilnuj zeby wal_status != "lost")
sudo -u postgres psql -c "
SELECT slot_name, active, restart_lsn,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots;
"
# UWAGA: jezeli standby jest offline i slot nie jest uzywany,
# primary bedzie trzymal WAL w nieskonczonosc i moze zapelnic dysk!
# Usun slot jezeli standby jest trwale offline:
# SELECT pg_drop_replication_slot('standby1_slot'); Konfiguracja standby — pg_basebackup i recovery
# Na STANDBY: instalacja PostgreSQL (ta sama wersja co primary) sudo apt install postgresql-16 -y sudo systemctl stop postgresql # Usuniecie domyslnych danych (zastapione przez pg_basebackup) sudo rm -rf /var/lib/postgresql/16/main/* # Klonowanie primary przez pg_basebackup # -h = host primary, -U = uzytkownik replikacji, -P = progress, -v = verbose # -R = automatycznie tworzy standby.signal i primary_conninfo # -S = uzyj replication slot sudo -u postgres pg_basebackup \ -h 192.168.1.10 \ -U replicator \ -D /var/lib/postgresql/16/main \ -P -v -R \ -S standby1_slot \ --checkpoint=fast # pg_basebackup automatycznie stworzyl: # - standby.signal (plik sygnalizujacy standby mode) # - postgresql.auto.conf z primary_conninfo # Sprawdz wygenerowana konfiguracje cat /var/lib/postgresql/16/main/postgresql.auto.conf # primary_conninfo = 'host=192.168.1.10 port=5432 user=replicator password=... sslmode=prefer' # primary_slot_name = 'standby1_slot' # Uruchom standby sudo systemctl start postgresql # Sprawdz czy standby sie polaczyl (na standby) sudo -u postgres psql -c "SELECT pg_is_in_recovery();" -- powinno zwrocic 't' # Na PRIMARY: sprawdz polaczone repliki sudo -u postgres psql -c "SELECT * FROM pg_stat_replication;"
Monitoring opóźnienia replikacji
# Na PRIMARY: szczegolowy status replikacji
sudo -u postgres psql -c "
SELECT
application_name,
client_addr,
state,
write_lag,
flush_lag,
replay_lag,
sync_state
FROM pg_stat_replication;
"
# Na STANDBY: ile czasu za primary
sudo -u postgres psql -c "
SELECT
now() - pg_last_xact_replay_timestamp() AS replication_delay,
pg_is_in_recovery() AS is_standby,
pg_last_wal_replay_lsn() AS last_replayed_lsn;
"
# Skrypt monitoringu (mozna uzywac z Nagios/Zabbix)
#!/bin/bash
LAG=$(sudo -u postgres psql -t -c \
"SELECT EXTRACT(EPOCH FROM (now() - pg_last_xact_replay_timestamp()))::int" 2>/dev/null)
THRESHOLD=30 # sekundy
if [ "$LAG" -gt "$THRESHOLD" ]; then
echo "CRITICAL: Replication lag = ${LAG}s (threshold: ${THRESHOLD}s)"
exit 2
fi
echo "OK: Replication lag = ${LAG}s"
exit 0 Failover — ręczny i automatyczny z Patroni
# --- RECZNIE FAILOVER (PostgreSQL 12+) --- # Na STANDBY: promocja do primary sudo -u postgres psql -c "SELECT pg_promote();" # lub: sudo -u postgres touch /tmp/promote_trigger # (jezeli skonfigurowano promote_trigger_file w postgresql.conf) # Sprawdz ze standby jest teraz primary sudo -u postgres psql -c "SELECT pg_is_in_recovery();" # Powinno zwrocic 'f' (false = jest primary) # Stary primary (jezeli wracamy po naprawie) — zresetuj jako standby # OPCJA 1: pg_rewind (szybkie, wymaga ze old_primary ma wal_log_hints = on) sudo -u postgres pg_rewind \ --target-pgdata=/var/lib/postgresql/16/main \ --source-server='host=192.168.1.11 user=replicator password=strong_repl_pass dbname=postgres' # OPCJA 2: pg_basebackup od nowa (wolniejsze, zawsze dziala) sudo -u postgres pg_basebackup -h 192.168.1.11 -U replicator -D /tmp/pgdata_new -P -R # --- PATRONI — automatyczny failover z DCS --- # Patroni uzywa etcd lub Consul do leader election # Instalacja: sudo pip3 install patroni[etcd] # Konfiguracja: /etc/patroni/patroni.yml # Po failerze Patroni automatycznie: # 1. Wykrywa niedostepnosc primary (health check co 10s) # 2. Przeprowadza elekcje lidera przez etcd # 3. Promuje najlepszy standby # 4. Informuje przez REST API o zmianie # Sprawdz status Patroni patronictl -c /etc/patroni/patroni.yml list # + Cluster: postgres-prod (123456789) --------+ # | Member | Host | Role | State | # | pg-node1 | 192.168.1.10 | Replica | running | # | pg-node2 | 192.168.1.11 | Leader | running |