PostgreSQL streaming replication — konfiguracja HA
Opublikowano: 9 kwietnia 2026 · Kategoria: Bazy danych / VPS
Jedna instancja PostgreSQL to single point of failure — gdy serwer padnie, aplikacja przestaje działać. Streaming replication pozwala utrzymać hot standby (ciepłą rezerwę): replika na bieżąco otrzymuje zmiany z primary i w razie awarii może przejąć ruch w ciągu sekund. Oto jak skonfigurować replikację od podstaw.
Architektura streaming replication
PostgreSQL streaming replication działa przez protokół WAL (Write Ahead Log): primary zapisuje każdą zmianę do WAL przed jej zastosowaniem na dysku. Replika podłącza się do primary przez specjalne połączenie replikacji i strumieniuje (streaming) kolejne rekordy WAL:
- Primary — przyjmuje zapisy i odczyty. Wysyła WAL do replik w czasie rzeczywistym.
- Standby / Replica — tylko odczyty (read-only). Stosuje WAL od primary. Może obsługiwać zapytania raportowe, backupy, analizy.
- Synchronous standby — opcjonalnie: primary czeka na potwierdzenie od repliki przed odpowiedzią klientowi. Zero utraty danych, ale większe opóźnienia zapisu.
- Asynchronous standby — domyślny tryb. Primary nie czeka na replikę — minimalne opóźnienia, ryzyko utraty ostatnich transakcji przy failoverze.
Krok 1 — Konfiguracja primary
Edytuj postgresql.conf na serwerze primary (zazwyczaj
/etc/postgresql/16/main/postgresql.conf lub
/var/lib/postgresql/data/postgresql.conf):
# postgresql.conf na PRIMARY # Wymagane: włącz WAL dla replikacji wal_level = replica # Minimalne: replica. Dla logical: logical # Maksymalna liczba jednoczesnych połączeń replikacji max_wal_senders = 10 # 1 na każdą replikę + zapas # Ile WAL zachować na primary (dla repliki która jest chwilowo offline) wal_keep_size = 1GB # PostgreSQL 13+; starsze: wal_keep_segments = 64 # Opcjonalnie: synchroniczna replikacja (zero utraty danych) # synchronous_standby_names = 'replica1' # synchronous_commit = on # Archiwizacja WAL (dla Point-in-Time Recovery) archive_mode = on archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f'
Następnie utwórz użytkownika replikacji w pg_hba.conf:
# Utwórz użytkownika replikacji psql -U postgres -c "CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'haslo_replikacji';" # pg_hba.conf — dodaj wpis dla repliki (IP: 10.0.0.11) # Plik: /etc/postgresql/16/main/pg_hba.conf # TYPE DATABASE USER ADDRESS METHOD host replication replicator 10.0.0.11/32 scram-sha-256 # Przeładuj konfigurację sudo systemctl reload postgresql
Krok 2 — pg_basebackup na replice
Na serwerze repliki wykonaj bazowe kopiowanie danych z primary przez
pg_basebackup. To jednorazowa operacja inicjalizacyjna:
# Na serwerze REPLIKI — zatrzymaj PostgreSQL sudo systemctl stop postgresql # Usuń istniejące dane (UWAGA: niszczy dane lokalne) sudo rm -rf /var/lib/postgresql/16/main/* # Skopiuj dane z primary (IP: 10.0.0.10) sudo -u postgres pg_basebackup \ -h 10.0.0.10 \ -U replicator \ -D /var/lib/postgresql/16/main \ -P \ --wal-method=stream \ --checkpoint=fast # -P = pokaż postęp # --wal-method=stream = strumieniuj WAL podczas backupu # --checkpoint=fast = szybki checkpoint na primary przed startem
Krok 3 — konfiguracja standby
Po pg_basebackup skonfiguruj replikę. W PostgreSQL 12+ wystarczą dwa pliki:
# 1. Utwórz plik standby.signal (pusty plik — sygnalizuje tryb standby)
sudo -u postgres touch /var/lib/postgresql/16/main/standby.signal
# 2. Dodaj primary_conninfo do postgresql.auto.conf
sudo -u postgres tee -a /var/lib/postgresql/16/main/postgresql.auto.conf <<EOF
primary_conninfo = 'host=10.0.0.10 port=5432 user=replicator password=haslo_replikacji application_name=replica1'
primary_slot_name = 'replica1_slot'
EOF
# 3. Opcjonalnie: utwórz replication slot na primary (zabezpiecza WAL)
# psql -U postgres -h 10.0.0.10 -c "SELECT pg_create_physical_replication_slot('replica1_slot');"
# 4. Uruchom PostgreSQL na replice
sudo systemctl start postgresql Weryfikacja replikacji
# Na PRIMARY — sprawdź podłączone repliki psql -U postgres -c "SELECT client_addr, state, sent_lsn, replay_lsn, (sent_lsn - replay_lsn) AS lag_bytes FROM pg_stat_replication;" # Wynik: state=streaming, lag_bytes bliskie 0 = OK # Na REPLICE — sprawdź czy jest w trybie recovery psql -U postgres -c "SELECT pg_is_in_recovery();" # Wynik: t (true) = replika # Opóźnienie czasowe na replice psql -U postgres -c "SELECT now() - pg_last_xact_replay_timestamp() AS delay;"
Parametry konfiguracyjne — tabela
| Parametr | Wartość | Opis |
|---|---|---|
wal_level | replica | Minimalny poziom WAL dla replikacji fizycznej |
max_wal_senders | 10 | Maks. liczba jednoczesnych połączeń replikacji |
wal_keep_size | 1GB | Ile WAL zachować gdy replika jest offline (PG13+) |
hot_standby | on (domyślnie) | Zezwala na odczyty na replice podczas recovery |
recovery_min_apply_delay | 0 | Opóźnienie aplikowania WAL — zabezpieczenie przed DROP TABLE |
Patroni — automatyczny failover
Ręczna replikacja streaming nie ma automatycznego failoveru — gdy primary padnie, administrator musi ręcznie promować replikę. Patroni to open-source HA manager (Zalando) który automatyzuje ten proces:
- Używa etcd / Consul / ZooKeeper jako distributed consensus store — dokładnie jeden węzeł jest leaderem (primary) w każdym momencie.
- Gdy primary nie odpowiada, Patroni przeprowadza wybory i promuje najlepszą replikę, rekonfiguruje pozostałe jako repliki nowego primary.
-
Udostępnia REST API (
/health,/leader,/replicas) — HAProxy lub Nginx mogą używać go do health check i routingu ruchu. - Obsługuje planned switchover bez utraty danych: promuje replikę gdy primary jest zdrowy (np. na czas maintenence).
pgBouncer — connection pooling
PostgreSQL jest kosztowny przy tworzeniu połączeń — każde połączenie to osobny proces OS. pgBouncer rozwiązuje ten problem jako lekki connection pooler:
# pgbouncer.ini — podstawowa konfiguracja [databases] mydb = host=127.0.0.1 port=5432 dbname=mydb [pgbouncer] listen_addr = 127.0.0.1 listen_port = 5432 # pgBouncer słucha na 5432, PostgreSQL przeniesiony na 5433 auth_type = scram-sha-256 auth_file = /etc/pgbouncer/userlist.txt pool_mode = transaction # transaction/session/statement max_client_conn = 1000 # Maks. połączeń od aplikacji default_pool_size = 20 # Maks. połączeń do PostgreSQL
Tryb transaction to najefektywniejszy tryb poolingu: jedno połączenie do PostgreSQL
obsługuje wiele klientów aplikacji, bo każda transakcja może trafić do innego połączenia. Ograniczenie:
nie obsługuje SET, LISTEN, prepared statements (bez modyfikacji
aplikacji).