PostgreSQL e' un potente DBMS relazionale Open Source noto per la robustezza e la ricchezza di funzionalita' anche sull'alta affidabilita'. In questo documento descriveremo la configurazione della replica con una versione 12 o successiva di PostgreSQL, perche' e' cambiata in modo significativo rispetto alle precedenti versioni.
Questo documento non e' introduttivo ed e' consigliato ad un pubblico informaticamente adulto...
Un documento introduttivo su PostgreSQL
e' Introduzione a PostgreSQL,
un documento piu' completo e' Qualcosa in piu' su PostgreSQL
la replica e la sua configurazione nelle precedenti versioni e'
descritta in
Replica in PostgreSQL.
Infine alcuni importanti elementi sulla continuita' operativa per le basi dati sono riportati in
Architetture di Database per l'HA.
In questo breve documento una base dati PostgreSQL attiva su un nodo Primary
viene configurata per essere replicata su uno o piu' nodi secondari in Standby.
Vengono analizzate sia le configurazioni per
fornire l'alta affidabilita' (HA: High Availability)
con server posti nella stessa rete locale ed RPO e RTO molto bassi;
sia configurazioni addatte a garantire la continuita' operativa in caso di
disastro (DR: Disaster Recovery) con server posti su reti differenti a
distanza geografica. Naturalmente le configurazioni per DR hanno un RTO piu' elevato.
Le configurazioni presentate sono relative a PostgreSQL in versione 12.0 o successiva,
poiche' sono cambiate rispetto alle versioni precedenti.
Se siete interessati ad una versione precedente di PostgreSQL leggete
Replica in PostgreSQL.
I server utilizzati per la replica debbono avere la stessa versione di PostgreSQL e, possibilimente,
la stessa versione di sistema operativo. E' anche fortemente consigliato che utilizzino
gli stessi path, stessi utenti/gruppi di sistema, stesse minor version, ...
Un breve accenno all'architettura di PostgreSQL puo' essere utile...
Il processo
postmaster e' il processo principale che si occupa della gestione delle
connessioni (e' in LISTEN sulla porta socket 5432)
ed e' il "padre" di tutti i processi, sia di sistema (eg. wal writer)
sia quelli relativi alle connessione utente.
Tutti i processi girano come utente postgres ed eseguono un attach
al segmento di shared memory su cui vengono mantenuti buffer e lock.
La gestione delle transazioni in PostgreSQL avviene con la tecnica del MVCC (Multiversion Concurrency Control) e la consistenza dei dati su disco e' assicurata con il logging (Write-Ahead Logging). L'isolation level di default e' Read Committed. Ogni transazione al commit effettua una scrittura sul WAL (Write-Ahead Log): in questo modo e' certo che le attivita' confermate alla transazione sono state scritte su disco. La scrittura sul WAL e' sequenziale e molto veloce. La scrittura effettiva sui file delle tabelle e degli indici viene effettuata successivamente ottimizzando le scritture sul disco. I WAL possono essere archiviati e questo consente l'effettuazione di backup a caldo e il PITR (Point In Time Recovery).
Il Clustered Database e' la struttura del file system che contiene i dati. All'interno vengono mantenuti i file di configurazione ($PGDATA/*.conf), i log delle transazioni (nella directory $PGDATA/pg_wal) e le strutture interne della base dati che contengono tabelle ed indici (suddivise per database nella directory $PGDATA/base). Il blocco ha dimensione 8KB mentre i wal log hanno come dimensione 16MB.
La replica di una base dati PostgreSQL e' basata sulla ricezione e l'applicazione dei WAL sui sistemi secondari o Standby. Nelle prime versioni di PostgreSQL era presente la sola modalita' di log shipping ma le versioni piu' recenti implementano la streaming replication che ha una latenza molto bassa.
Dal punto di vista architetturale la replica della versione 12 non ha differenze significative rispetto alle versioni precedenti pero' la configurazione e' completamente diversa. Non e' piu' presente il file recovery.conf ma va utilizzato il file standby.signal per indicare che il database e' in stato di standby e tutte le configurazioni sono contenute nel file postgresql.conf come vedremo in dettaglio nel seguito.
Il sistema Primary o Master e' quello che ospita la base dati in condizioni di normale operativita'.
Oltre alla normale configurazione che si effettua con il tuning dei parametri per ottenere prestazioni ottimali con le applicazioni ospitate, vanno impostati parametri specifici per la replica.
In pratica con la versione 12 e successive non e' quasi mai necessaria una configurazione specifica ed il riavvio dell'istanza Primary perche' i valori di default dei parametri sono gia' adatti alla replica.
La configurazione di default eseguita pg_basebackup e' gia' sufficiente: gia fatto!
Con l'opzione -R del pg_basebackup vengono creati anche tutti i file di supporto per la replica.
In particolare viene creato il file standby.signal che indica che il
database e' in stato di standby e nel file postgresql.auto.conf
viene inserito il parametro primary_conninfo gia' correttamente configurato.
Se si vuole utilizzare il nodo slave solo per l'HA (nessun accesso in lettura)
basta rinominare il file standby.signal in recovery.signal.
Puo' essere impostato il parametro promote_trigger_file
nel file postgresql.conf
[NdA sostituisce il parametro trigger_file del file recovery.conf].
Ora sono disponibili ed utilizzabili due istanze PostgreSQL allineate tra loro (la seconda in sola lettura).
Sono possibili diverse configurazioni ulteriori... nel seguito ne vedremo alcune.
La configurazione in cascading replication e' adatta per repliche geografiche multiple.
In una configurazione di DR un requisito e' la distanza e la separazione tra
l'ambiente di produzione ed i server in Standby. Saranno quindi molto piu' alte
le latenze di rete ed l'RPO ottenibile. La configurazione deve essere ottimizzata
per ridurre il traffico di rete.
Un server di Standby non deve necessariamente connettersi direttamente al Primary
ma puo' raccogliere le modifiche da un altro Standby formando cosi' una catena.
Il server piu' vicino al Primary viene chiamato di Upstream mentre quello piu'
lontano viene chiamato Downstream.
Questa configurazione consente di diminuire le connessioni dirette verso il Primary
e di ottimizzare il traffico di rete se piu' server si trovano in una rete diversa
da quella del Primary (situazione tipica per un sito di DR).
Nella figura di esempio si utilizza la replica in cascata per mantenere allineati
due CED in una tipica configurazione di Disaster Recovery.
Il traffico di rete e' ottimizzato poiche' e' attiva una sola replica in geografico
tra il Primary e l'Upstream Secondary2 mentre la replica verso il Downstream Secondary3
avviene su rete locale.
La configurazione sincrona minimizza le perdite di dati
ma ha un impatto significativo sulle performances.
La replica su PostgreSQL per default e' asincrona.
E' possibile configurare la replica sincrona (2-safe replication)
impostando i parametri synchronous_standby_names e synchronous_commit.
Nella configurazione sincrona le transazioni non vengono chiuse fino a che
non siano state trasferite ad almeno uno standby.
La configurazione si effettua con il parametro
synchronous_commit impostandolo a remote_write.
La replica sincrona puo' indurre rallentamenti alle transazioni
[NdA la replica sincrona rallenta sempre tutte le transazioni poiche' richiede un round trip di conferma,
solo in alcuni casi il rallentamento non e' sensibile]:
pertanto e' da utilizzare solo in casi specifici
quando la latenza e' minimale e sono disponibili piu' Slave.
Anche se i valori di default dei parametri di configurazione relativi alla replica presenti nelle versioni 12+ sono gia' adatti per l'utilizzo della streaming replication e' possibile effettuare un tuning specifico quando si rende necessario. Nel seguito vediamo i parametri piu' utili.
Se la replica viene interrotta per qualche ragione poi PostgreSQL
recupera automaticamente, purche' siano ancora disponibili i WAL da applicare.
I WAL mantenuti sul Master sono definiti dal parametro wal_keep_segments
il cui valore di default e'... 0 ovvero nessun WAL in piu' rispetto a quelli
strettamente necessari.
Se si utilizza la replica e' opportuno impostare il parametro in modo da mantenere
un numero adeguato di WAL segments che hanno una dimensione tipica di 16MB.
Attenzione: dalla versione 13+ il parametro da utilizzare e'
wal_keep_size. Il default e' 0, il mio suggerimento e' quello di configurare questo parametro:
una dimensione consigliabile e' 1GB.
Per mantenere la disponibilita' dei WAL necessari alle repliche
e' anche possibile utilizzare l'archiving o i replication slot...
ma la descrizione di queste alternative supera le possibilita'
di questa paginetta introduttiva [NdA KISS: keep it simple!
Ma se volete utilizzare i replication slot con il parametro
primary_slot_name sul secondario
allora non dimenticate di impostare il parametro max_slot_wal_keep_size sul primario
disponibile dalla 13+].
Sullo Slave quando l’applicazione di un WAL va in conflitto con una query quest'ultima viene abortita.
Questo puo' avvenire con query di lunga durata (eg. pg_dump) eseguite su tabelle che vengono
modificate e sui viene applicato il vacuum sul primario.
E' un comportamento voluto perche' la precedenza viene data alla sicurezza dei dati
e non alle query eseguite sullo standby.
Questo fenomeno puo’ essere ridotto agendo sui parametri
max_standby_archive_delay e max_standby_streaming_delay che concedono piu’ tempo allo Slave
per terminare le query.
Altro parametro utilizzabile e’
l'hot_standby_feedback che notifica la presenza di query sullo Slave
ma influenza il primary perche' puo' impedire il vacuuming in alcune condizioni di carico.
Infine e’ possibile interrompere l’applicazione dei WAL con pg_wal_replay_pause()
e riprenderlo al termine delle query pesanti.
Quando il numero di standby server e' elevato puo' essere necessario aumentare il parametro max_wal_senders.
Con connessioni geografiche puo' essere utile aumentare rispetto al valore di default (60 secondi) i parametri wal_sender_timeout sul primary e wal_receiver_timeout sul secondary.
Come gia' riportato la replica sincrona va utilizzata in casi specifici perche' rallenta, anche in modo significativo, il commit delle transazioni. Se gli slave hanno la stessa priorita' e' consigliabile utilizzare la modalita' ANY con un numero inferiore al totale per minimizzare le attese (eg. synchronous_standby_names=ANY 1 (s1, s2, s3) conferma il commit dopo aver ricevuto la conferma da solo uno dei tre slave configurati). Anche se la configurazione generale prevede la replica sincrona e' possibile derogare a questa con il parametro synchronous_commit che puo' assumere i valori: remote_apply, on, remote_write, local, off. Il significato delle opzioni e' abbastanza logico: se synchronous_standby_names non e' impostato le due modalita' sono on ed off ed indicano l'attesa o meno del flush del WAL su disco; altrimenti le possibilita' ulteriori sono attendere: l'applicazione della transazione sugli slave, il flush del WAL su disco sugli slave, la scrittura del WAL su disco sugli slave, il flush su disco locale, oppure nessuna attesa. Il parametro synchronous_commit ha come context il livello user, quindi puo' essere configurato all'occorrenza.
I parametri riportati sono utili sia con la streaming replication, descritta in questa pagina, che con la logical replication. L'unica avvertenza ulteriore con la logical replication e' che il numero di sottoscrizioni e' tipicamente piu' elevato del numero di secondari e quindi e' opportuno aumentare adeguatamente il parametro max_wal_senders. Inoltre va impostato il parametro wal_level = logical: il default del parametro e' "replica" e non e' sufficiente per la replica logica, la modifica richiede un riavvio.
La documentazione della configurazione e delle operative da utilizzare
per una corretta gestione degli ambienti ha un'importanza forse superiore
alla parte tecnica di implementazione!
Aspetto importante nella continuita' operativa e'
che vi sono eventi che non possono essere gestiti da automatismi
ma richiedono interventi manuali ed autorizzazioni:
anche questi processi debbono essere descritti.
Altrimenti il rischio e' quello di avere una macchina perfetta...
ma senza il pilota!
L'operazione piu' critica dal punto di vista del RTO (Recovery Time Objective) e'
quando lo Standby deve sostituire il Primary che e' andato in fault.
L'operazione e' semplice, veloce ed eseguibile in modi diversi:
Il Failover di PostgreSQL tipicamente richiede un tempo molto limitato (RTO < pochi secondi):
e' sicuramente maggiore il tempo che occorre per prendere la decisione giusta.
Con il failover vengono recuperate tutte le transazioni ricevute dallo Standby,
tuttavia non vi e' certezza che lo Standby abbia ricevuto tutte le transazioni eseguite sul Primary
(RPO > 0).
Per effetturare uno Switchover e' necessario terminare in modo pulito il primario,
attendere l'applicazione di tutte le transazioni raccolte dal secondario
(generalmente immediata ma possono esserci eccezioni) ed infine effettuare il promote.
Con questa operativa non si ha perdita di trasazioni e si evita il resilvering del vecchio
Primary se deve essere nuovamente messo in linea.
Oltre a verificare il corretto funzionamento dei sistemi, per il momento dato per scontato, e' necessario controllare il corretto allineamento delle repliche.
Con la configurazione in Hot Standby la verifica funzionale e' semplice: basta collegarsi e lanciare una query!
Per controllare lo stato delle repliche e' possibile utilizzare la seguente query sul Primary che restituisce una riga per ogni processo WAL Sender e quindi per ogni Standby connesso:
select client_addr, state, sync_state, txid_current_snapshot(), sent_lsn, write_lsn, flush_lsn, replay_lsn, backend_start from pg_stat_replication;
Sono informazioni analoghe rispetto a queste due query da lanciare rispettivamente sul Master e sullo Slave:
select pg_current_wal_lsn();
select pg_last_wal_receive_lsn();
Una metrica fondamentale per la replica e' il lag ovvero il ritardo nell'applicazione delle modifiche sullo slave. Un modo per valutarlo e':
select case when pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn() then 0 else extract(epoch from now() - pg_last_xact_replay_timestamp())::INTEGER end as LAG_seconds;
Sul primary molto utili sono le informazioni delle colonne *_lag sempre della vista pg_stat_replication
anche se a volte =null [NdA dipende se la replica e' sincrona o asincrona e se sono occorse transazioni di recente].
Altra query utile sul secondary e': select * from pg_stat_wal_receiver;
Informazioni banali, ma spesso utili, si ottengono com:
select now(), inet_server_addr()||':'||inet_server_port() as pg_host, pg_is_in_recovery();
Un report completo sulla configurazione di PostgreSQL, con una sezione dedicata alla replica, si ottiene con questo script.
Anche a livello di sistema operativo si possono eseguire alcuni controlli utili per la replica. In particolare e' possibile valutare la latenza (importante per valutare il lag delle repliche sincrone) e la banda:
L'ultimo consiglio sui monitoraggi... sapete qual'e' la causa piu' frequente di blocco delle repliche? Un file system full!
Il monitoraggio degli spazi e' quindi fondamentale per ogni istanza PostgreSQL ed in particolare per la Streaming Replication.
L'aggiornamento di una minor upgrade ad una configurazione in replica non presenta particolari problemi. L'unica avvertenza e' quella di applicare l'aggiornamento a tutti gli standby e quindi per ultimo al database primario. In realta' nella maggior parte dei casi non ci sono problemi anche utilizzando un diverso ordine di applicazione delle patch ma questo e' il modo piu' sicuro perche' la retrocompatibilita' e' sempre garantita.
In caso di major upgrade invece e' opportuno ricostruire le repliche. La presenza di uno standby e' comunque utile perche' si dispone di piu' database e risulta molto semplice un eventuale rollback alla versione precedente in caso di problemi.
Le funzionalita' della replication in PostgreSQL hanno avuto un'importante evoluzione nel tempo. Ecco le tappe principali:
Titolo: Replica in PostgreSQL 12+
Livello: Esperto
Data:
31 Ottobre 2019 🎃 Halloween
Versione: 1.0.5 - 31 Ottobre 2024 🎃 Halloween
Autore: mail [AT] meo.bogliolo.name