
L'obiettivo principale di Patroni è fornire alta disponibilità per PostgreSQL. Ma Patroni è solo un template, non uno strumento già pronto (come si dice nella documentazione). A prima vista, configurando Patroni in un laboratorio di test, si può vedere quanto sia uno strumento magnifico e come gestisca facilmente i nostri tentativi di compromettere il cluster. Tuttavia, nella pratica, nell'ambiente di produzione, tutto non sempre avviene così bene ed elegantemente come nel laboratorio di test.

Racconterò un po' di me. Ho iniziato come amministratore di sistema. Ho lavorato nello sviluppo web. Dal 2014 lavoro in Data Egret. L'azienda si occupa di consulenze nel settore di Postgres. E ci occupiamo proprio di Postgres, lavorando con Postgres ogni giorno, quindi abbiamo una varietà di esperienza legata all'operatività.
E alla fine del 2018 abbiamo iniziato a utilizzare lentamente Patroni. Abbiamo accumulato una certa esperienza. In qualche modo l'abbiamo diagnosticato, ottimizzato, siamo arrivati alle nostre migliori pratiche. E in questa relazione ne parlerò.
Oltre a Postgres, amo Linux. Mi piace esplorarlo e sperimentare, mi piace compilare i kernel. Amo la virtualizzazione, i container, Docker, Kubernetes. Tutto questo mi interessa, perché riflette le mie vecchie abitudini da amministratore. Mi piace occuparmi del monitoraggio. E mi piacciono le cose di Postgres legate all'amministrazione, cioè replica, backup. E nel mio tempo libero scrivo in Go. Non sono un ingegnere del software, scrivo solo per me stesso in Go. E questo mi porta gioia.

- Penso che molti di voi sappiano che in Postgres non c'è HA (Alta Disponibilità) di default. Per ottenere HA è necessario installare qualcosa, configurarlo, impegnarsi e ottenerlo.
- Ci sono diversi strumenti e Patroni è uno di essi, che risolve l'HA in modo piuttosto efficace e molto bene. Ma installando tutto questo in un laboratorio di test e avviandolo, possiamo vedere che funziona, possiamo riprodurre alcuni problemi, vedere come Patroni li gestisce. E vedremo che tutto funziona magnificamente.
- Ma nella pratica ci siamo imbattuti in vari problemi. E parlerò di questi problemi.
- Racconterò come li abbiamo diagnosticati, cosa abbiamo aggiustato – se questo ci ha aiutato o meno.

- Non racconterò come installare Patroni, perché è possibile cercare su internet, e si possono guardare i file di configurazione per capire come tutto questo viene avviato e configurato. Si può approfondire nelle architetture, trovando informazioni su questo in rete.
- Non parlerò delle esperienze altrui. Parlerò solo dei problemi che abbiamo incontrato noi.
- E non parlerò dei problemi al di fuori di Patroni e PostgreSQL. Se, ad esempio, ci sono problemi legati al bilanciamento, quando il nostro cluster si è rotto, non ne parlerò.

E una piccola premessa prima di cominciare la nostra presentazione.
Tutti questi problemi che abbiamo affrontato si sono presentati nei primi 6-7-8 mesi di utilizzo. Col tempo, siamo giunti ai nostri migliori criteri interni. E i problemi sono scomparsi. Pertanto, la presentazione è stata annunciata circa sei mesi fa, quando tutto questo era fresco nella mia mente e lo ricordavo perfettamente.
Durante la preparazione della presentazione, ho già rivenuto vecchi post mortem e guardato i log. E parte dei dettagli potrebbe essersi dimenticata, oppure alcuni dettagli potrebbero non essere stati esplorati a sufficienza durante l'analisi dei problemi, quindi in alcuni punti potrebbe sembrare che i problemi non siano stati trattati completamente, o che ci sia una mancanza di informazioni. Pertanto, vi chiedo di scusarmi per questo.

Che cos'è Patroni?
- È un modello per costruire HA. Così è scritto nella documentazione. E dal mio punto di vista, è un'ottima precisazione. Patroni non è una panacea che risolverà tutti i vostri problemi, ovvero è necessario fare uno sforzo affinché inizi a funzionare e a portare benefici.
- È un servizio agente che viene installato su ogni servizio con database, e che è una sorta di sistema di init per il vostro Postgres. Avvia Postgres, lo ferma, lo riavvia, modifica la configurazione e cambia la topologia del vostro cluster.
- Pertanto, per memorizzare lo stato del cluster, la sua attuale rappresentazione, come appare, è necessario un certo tipo di archiviazione. E da questo punto di vista, Patroni ha scelto di memorizzare lo stato in un sistema esterno. Si tratta di un sistema di archiviazione della configurazione distribuita. Possono essere Etcd, Consul, ZooKeeper, oppure Etcd di Kubernetes, quindi uno di questi opzioni.
- Una delle caratteristiche di Patroni è che l'auto-failover è incluso di default, basta configurarlo. Se confrontiamo con Repmgr, il failover viene fornito a parte. Con Repmgr otteniamo lo switchover, ma se vogliamo l'auto-failover, dobbiamo configurarlo separatamente. In Patroni l'auto-failover è già incluso di default.
- E ci sono molte altre cose. Ad esempio, la gestione delle configurazioni, il provisioning di nuove repliche, il backup, ecc. Ma questo è al di fuori della presentazione, non ne parlerò.

E un piccolo riassunto è che l'obiettivo principale di Patroni è di gestire bene e in modo affidabile l'auto-failover, affinché il cluster rimanga operativo e l'applicazione non noti cambiamenti nella topologia del cluster.

Ma quando iniziamo a utilizzare Patroni, il nostro sistema diventa un po' più complesso. Se prima avevamo Postgres, ora con Patroni abbiamo Patroni stesso, un DCS in cui è memorizzato lo stato. E tutto questo deve funzionare in qualche modo. Quindi, cosa può andare storto?
Può guastarsi:
- Può guastarsi Postgres. Può essere il master o una replica, uno dei due può smettere di funzionare.
- Può guastarsi lo stesso Patroni.
- Può guastarsi il DCS, dove è memorizzato lo stato.
- E può guastarsi la rete.
Tutti questi aspetti li tratterò nella presentazione.

Esaminerò i casi in ordine di complessità, non dal punto di vista del numero di componenti coinvolti, ma da quello delle sensazioni soggettive, ovvero se un caso è stato difficile o facile da analizzare.

E il primo caso è il più semplice. È il caso in cui abbiamo preso un cluster di database e abbiamo configurato il nostro archivio DCS su quel cluster. Questo è l'errore più comune. È un errore di architettura, ossia combinare diversi componenti nello stesso posto.
Quindi, si è verificato un failover, andiamo a vedere cosa è successo.

E qui ci interessa sapere quando è avvenuto il failover. Cioè, ci interessa questo momento in cui lo stato del cluster è cambiato.
Ma il failover non avviene sempre in un solo momento, cioè non occupa una singola unità di tempo, può prolungarsi. Può durare nel tempo.
Quindi ha un tempo di inizio e un tempo di fine, cioè è un evento prolungato. E dividiamo tutti gli eventi in tre intervalli: abbiamo il tempo prima del failover, durante il failover e dopo il failover. Cioè consideriamo tutti gli eventi in questa linea temporale.

E per prima cosa, quando si è verificato il failover, cerchiamo la causa di cosa è successo, cosa ha causato il failover.
Se guardiamo ai log, avremo i classici log di Patroni. In essi ci dice che il server è diventato master e il ruolo di master è passato a questo nodo. Qui è evidenziato.

Poi dobbiamo capire perché si è verificato il failover, cioè quali eventi si sono verificati che hanno costretto il ruolo di master a trasferirsi da un nodo all'altro. E in questo caso è tutto semplice. Abbiamo un errore di interazione con il sistema di archiviazione. Il master ha capito che non può lavorare con DCS, cioè si è presentato un problema di interazione. E dice che non può più essere master e si dimette. Questa riga "demoted self" parla proprio di questo.

Se guardiamo agli eventi che hanno preceduto il failover, possiamo vedere le cause che hanno creato problemi per il proseguimento del lavoro del master.
Se guardiamo ai log di Patroni, vedremo che ci sono molti tipi di errori, timeout, cioè l'agente Patroni non può lavorare con DCS. In questo caso è l'agente Consul, con cui si comunica tramite la porta 8500.
E il problema qui è che Patroni e il database sono in esecuzione sulla stessa host. E su questo stesso nodo sono stati avviati i server Consul. Creando un carico sul server, abbiamo creato problemi anche per server Consul. Non sono riusciti a comunicare correttamente.

Dopo un po', quando il carico è diminuito, il nostro Patroni è riuscito a comunicare di nuovo con gli agenti. Il lavoro normale è ripreso. E lo stesso server Pgdb-2 è tornato a essere master. Cioè c'è stata una piccola fluttuazione, per la quale il nodo ha rinunciato ai diritti di master e poi li ha ripresi, cioè tutto è tornato come era.

E questo può essere considerato come un falso attivazione, oppure può essere interpretato che Patroni ha fatto tutto correttamente. Cioè ha capito che non poteva mantenere lo stato del cluster e ha rinunciato ai diritti.
E qui il problema è sorto dal fatto che i server Consul si trovano sullo stesso hardware delle basi. Pertanto, qualsiasi carico: sia esso sul disco o sui processori, influisce anche sull'interazione con il cluster Consul.

E abbiamo deciso che non dovevano convivere, abbiamo creato un cluster separato per Consul. E Patroni funzionava già con un Consul separato, cioè c'era un cluster Postgres separato e un cluster Consul separato. Questa è una guida di base su come distribuite tutte queste cose e tenerle separate, in modo che non vivano insieme.
Come opzione, si possono modificare i parametri ttl, loop_wait, retry_timeout, cioè si può cercare di superare questi picchi di carico a breve termine aumentando questi parametri. Ma questa non è la soluzione più adatta, perché questo carico potrebbe durare nel tempo. E semplicemente supereremmo i limiti di questi parametri. E questo potrebbe non aiutare molto.

Il primo problema, come avete capito, è semplice. Abbiamo messo DCS insieme con il database, e abbiamo ottenuto un problema.

Il secondo problema è simile al primo. È simile in quanto abbiamo di nuovo problemi di interazione con il sistema DCS.

Se guardiamo i log, vedremo che abbiamo di nuovo un errore di comunicazione. E Patroni dice che non riesce a interagire con DCS, quindi il master attuale passa in modalità replica.
Il vecchio master diventa replica, qui Patroni agisce come dovrebbe. Avvia pg_rewind per tornare indietro nel log delle transazioni e poi connettersi al nuovo master, e quindi raggiungere il nuovo master. Qui Patroni agisce come deve.

Qui dobbiamo trovare il punto che ha preceduto il file system, cioè quegli errori che hanno causato l'insorgere del file system. E in questo senso i log di Patroni sono piuttosto facili da lavorare. Scrive gli stessi messaggi a intervalli regolari. E se iniziamo a scorrere rapidamente questi log, vedremo che i log sono cambiati, il che significa che sono iniziati dei problemi. Torniamo rapidamente a quel punto e vediamo cosa sta succedendo.
E in una situazione normale, i log appaiono più o meno così. Viene controllato il proprietario del blocco. E se il proprietario, ad esempio, è cambiato, possono accadere eventi a cui Patroni deve reagire. Ma in questo caso va tutto bene. Stiamo cercando il punto in cui sono iniziati gli errori.

E facendo scorrere fino a quel punto in cui gli errori hanno iniziato a comparire, vediamo che abbiamo avuto un auto-failover. E poiché gli errori erano legati all'interazione con DCS e nel nostro caso abbiamo utilizzato Consul, guardiamo anche nei log di Consul per vedere cosa sia successo lì.
Circa allineando il momento del failover e il tempo nei log di Consul, vediamo che i nostri vicini nel cluster Consul hanno iniziato a dubitare dell'esistenza degli altri membri del cluster Consul.

E se guardiamo anche i log di altri agenti Consul, possiamo vedere che lì sta accadendo qualche tipo di collasso di rete. E tutti i membri del cluster Consul dubitano dell'esistenza reciproca. E questo ha provocato il failover.
Se guardiamo a cosa è successo prima di questi errori, possiamo vedere che ci sono vari errori, ad esempio, scadenza, errore RPC, cioè chiaramente ci sono problemi nell'interazione tra i membri del cluster Consul.

La risposta più semplice è riparare la rete. Ma per me, in piedi al podio, è facile dirlo. Tuttavia, le circostanze sono tali che non sempre il cliente può permettersi di riparare la rete. Può vivere in un Data Center e potrebbe non avere la possibilità di riparare la rete, influenzare l'hardware. E quindi sono necessarie altre opzioni.

Le opzioni ci sono:
- L'opzione più semplice, che è scritta, penso, anche nella documentazione, è disattivare i controlli di Consul, cioè semplicemente passare un array vuoto. E diciamo all'agente Consul di non utilizzare alcun controllo. Grazie a questi controlli possiamo ignorare queste tempeste di rete e non avviare il failover.
- Un'altra opzione è controllare il raft_multiplier. Questo è un parametro del server Consul stesso. Di default è impostato su 5. Questo valore è consigliato dalla documentazione per gli ambienti di staging. Sostanzialmente influisce sulla frequenza dello scambio di messaggi tra i membri della rete Consul. Di fatto, questo parametro influisce sulla velocità della comunicazione tra i membri del cluster Consul. E per la produzione si consiglia di ridurlo affinché i nodi scambino messaggi più frequentemente.
- Un'altra opzione che abbiamo iniziato a usare è quella di aumentare la priorità dei processi di Consul rispetto ad altri processi per il pianificatore dei processi del sistema operativo. Esiste un parametro chiamato "nice", che determina esattamente la priorità dei processi che il pianificatore OS considera durante la pianificazione. Abbiamo deciso di ridurre il valore di nice per gli agenti di Consul, cioè di aumentare la priorità, in modo che il sistema operativo fornisca ai processi di Consul più tempo per lavorare e per eseguire il proprio codice. Nel nostro caso, questo ha risolto il nostro problema.
- Un'altra opzione è non utilizzare Consul. Ho un amico che è un grande sostenitore di Etcd. E noi discutiamo regolarmente su quale sia meglio, Etcd o Consul. Ma di solito concordiamo sul fatto che Consul ha un agente che deve essere eseguito su ogni nodo con il database. Cioè, l'interazione di Patroni con il cluster di Consul avviene tramite questo agente. E questo agente diventa il collo di bottiglia. Se succede qualcosa all'agente, Patroni non può più lavorare con il cluster di Consul. E questo è un problema. In Etcd non c'è un agente. Patroni può lavorare direttamente con l'elenco dei server Etcd e comunicare con essi. In questo senso, se nella vostra azienda utilizzate Etcd, allora probabilmente sarà una scelta migliore rispetto a Consul. Ma per i nostri clienti siamo sempre limitati a ciò che scelgono e utilizzano. E per la maggior parte dei casi, Consul è utilizzato da tutti i clienti.
- E l'ultimo punto è riesaminare i valori dei parametri. Possiamo aumentare questi parametri sperando che i nostri problemi di rete a breve termine siano brevi e non superino l'intervallo di questi parametri. In questo modo possiamo ridurre l'aggressività di Patroni nell'eseguire il failover automatico se si verificano problemi di rete.

Penso che molti di coloro che utilizzano Patroni siano familiari con questo comando.

Questo comando mostra lo stato attuale del cluster. E a prima vista questa situazione potrebbe sembrare normale. Abbiamo un master, abbiamo una replica, non ci sono ritardi nella replicazione. Ma questa situazione è normale solo finché non sappiamo che in questo cluster dovrebbero esserci tre nodi, non due.

Di conseguenza, si è verificato un auto-failover. E dopo questo auto-failover la nostra replica è scomparsa. Dobbiamo capire perché è scomparsa e riportarla indietro, ripristinarla. E torniamo di nuovo ai log per vedere perché si è verificato l'auto-failover.

In questo caso la seconda replica è diventata master. Qui va tutto bene.

E dobbiamo già guardare la replica che è andata giù e che non è nel cluster. Apriamo i log di Patroni e vediamo che durante il tentativo di connessione al cluster si è verificato un problema durante la fase di pg_rewind. Per connettersi al cluster è necessario ripristinare il log delle transazioni, richiedere il log delle transazioni necessario dal master e seguirlo per raggiungere il master.
In questo caso non abbiamo il log delle transazioni e la replica non può avviarsi. Di conseguenza, fermiamo Postgres con un errore. E per questo non è nel cluster.

Dobbiamo capire perché non è nel cluster e perché non ci sono log. Andiamo sul nuovo master e verifichiamo cosa ha nei log. Si scopre che durante il pg_rewind si è verificato un checkpoint. E parte dei vecchi log delle transazioni è stata semplicemente rinominata. Quando il vecchio master ha cercato di connettersi al nuovo master e richiedere questi log, erano già stati rinominati, semplicemente non c'erano.

Ho confrontato i timestamp quando si sono verificati questi eventi. E lì la differenza è di appena 150 millisecondi, ossia, in 369 millisecondi si è concluso il checkpoint e i segmenti WAL sono stati rinominati. E letteralmente 517 millisecondi dopo, quindi dopo 150 millisecondi, è iniziato il rewind sulla vecchia replica. Cioè, ci sono voluti solo 150 millisecondi perché la replica non riuscisse a connettersi e avviarsi.

Quali sono le opzioni?
Inizialmente abbiamo utilizzato gli slot di replica. Ci sembrava una buona idea. Anche se nella prima fase di utilizzo abbiamo disattivato gli slot. Ci sembrava che, se gli slot accumulassero molti segmenti WAL, potremmo far cadere il master. Sarebbe crollato. Dopo aver sopportato un po' di tempo senza slot, abbiamo capito che ne avevamo bisogno, quindi li abbiamo riattivati.
Ma qui c'è un problema: quando il master passa alla replica, elimina gli slot e con gli slot vengono eliminati i segmenti WAL. Per escludere l'emergere di questo problema, abbiamo deciso di aumentare il parametro wal_keep_segments. Di default sono 8 segmenti. Lo abbiamo aumentato a 1.000 e abbiamo controllato quanto spazio libero avevamo. Abbiamo riservato 16 gigabyte per wal_keep_segments. Cioè, durante il passaggio abbiamo sempre a disposizione su tutti i nodi 16 gigabyte di registri di transazioni.
Inoltre, questo è particolarmente rilevante per attività di manutenzione prolungate. Supponiamo di dover aggiornare una delle repliche. Vogliamo spegnerla. Dobbiamo aggiornare il software, forse il sistema operativo, o qualcos'altro. E quando spegniamo la replica, per questa replica viene anche eliminato lo slot. Se utilizziamo un piccolo wal_keep_segments, durante l'assenza prolungata della replica, i registri di transazioni verranno riprodotti. Riattraeremo la replica, essa richiederà i registri di transazioni in cui si era fermata, ma sul master potrebbero non essere disponibili. E la replica non potrà collegarsi. Perciò manteniamo una grande riserva di registri.


Abbiamo un database di produzione. Lì sono già in corso progetti.
Si è verificato un failover. Siamo entrati e abbiamo controllato: tutto a posto, le repliche sono al loro posto, non ci sono ritardi nella replica. Non ci sono errori nei registri, tutto va bene.
Il team di prodotto dice che dovrebbero esserci dei dati, ma noi li vediamo in una fonte e non nel database. Dobbiamo capire cosa è successo con loro.

È chiaro che pg_rewind li ha sovrascritti. Lo abbiamo capito subito, ma siamo andati a vedere cosa stava succedendo.

Nei log possiamo sempre trovare quando è avvenuto il failover, chi è diventato il master e possiamo determinare chi era il vecchio master e quando ha voluto diventare replica, cioè abbiamo bisogno di questi log per determinare la quantità di registri di transazioni che è stata persa.
Il nostro vecchio master si è riavviato. E nel caricamento automatico era registrato Patroni. Patroni è partito. Ha immediatamente avviato Postgres. Più precisamente, prima di avviare Postgres e prima di farlo diventare replica, Patroni ha avviato il processo pg_rewind. Di conseguenza, ha cancellato parte dei registri di transazioni, scaricato nuovi e si è connesso. Qui Patroni ha funzionato magnificamente, cioè come doveva. Il cluster si è ripristinato. Avevamo 3 nodi, dopo il failover 3 nodi, tutto fantastico.

Abbiamo perso parte dei dati. Dobbiamo capire quanto abbiamo perso. Stiamo cercando il momento esatto in cui è avvenuto il rewind. Possiamo trovarlo grazie a queste registrazioni nel registro. È partito il rewind, ha fatto qualcosa e si è concluso.

Dobbiamo trovare la posizione nel registro delle transazioni in cui si è fermato il vecchio master. In questo caso, è questa marca. E abbiamo bisogno di un secondo marcatore, cioè della distanza che separa il vecchio master dal nuovo.
Prendiamo il normale pg_wal_lsn_diff e confrontiamo queste due marche. In questo caso otteniamo 17 megabyte. Se siano tanti o pochi, ognuno lo decide per sé. Perché per qualcuno 17 megabyte sono pochi, mentre per altri sono tanti e inaccettabili. Ognuno, quindi, definisce individualmente in base alle esigenze del proprio business.

Ma cosa abbiamo scoperto per noi stessi?
In primo luogo, dobbiamo decidere se abbiamo sempre bisogno dell'avvio automatico di Patroni dopo il riavvio del sistema. Spesso capita che dobbiamo accedere al vecchio master, vedere quanto si è spostato. Potrebbe essere necessario ispezionare i segmenti del registro delle transazioni, vedere cosa c'è. E capire se possiamo perdere questi dati o se dobbiamo avviare il vecchio master in modalità standalone per recuperare questi dati.
E solo dopo questo dobbiamo prendere decisioni su se possiamo scartare questi dati o se possiamo recuperarli, collegando questo nodo come replica nel nostro cluster.
Inoltre, c'è il parametro «maximum_lag_on_failover». Di default, se non ricordo male, questo parametro ha un valore di 1 megabyte.
Come funziona? Se la nostra replica è in ritardo di 1 megabyte di dati a causa del lag di replicazione, allora questa replica non partecipa alle elezioni. E se improvvisamente si verifica un failover, Patroni verifica quali repliche sono in ritardo. Se sono in ritardo di un gran numero di registri delle transazioni, non possono diventare master. Questa è una funzione di protezione molto utile, che impedisce di perdere molti dati.
Ma qui c'è il problema: il lag di replicazione nel cluster Patroni e DCS viene aggiornato con un determinato intervallo. Credo che il valore di ttl di default sia di 30 secondi.
Di conseguenza, può esistere una situazione in cui il lag di replica per le repliche in DCS è uno, mentre in realtà potrebbe esserci un lag completamente diverso o addirittura non esserci affatto lag, cioè questa cosa non è in tempo reale. E non riflette sempre la situazione reale. Non vale la pena costruire logiche complesse su di essa.
E il rischio di perdite di dati rimane sempre. E nel peggiore dei casi una formula, mentre nel caso medio un'altra formula. Cioè, quando pianifichiamo l'implementazione di Patroni e valutiamo quanti dati possiamo perdere, dobbiamo basarci su queste formule e avere un'idea di quanti dati possiamo perdere.
E c'è una buona notizia. Quando il vecchio master si allontana, può farlo a causa di alcuni processi in background. Cioè, c'è stato qualche autovacuum, ha scritto dei dati, li ha salvati nel log delle transazioni. E quei dati possiamo tranquillamente ignorarli e perderli. Non c'è alcun problema in questo.

Ecco come appaiono i log nel caso in cui sia impostato maximum_lag_on_failover e si sia verificato un failover, pertanto è necessario scegliere un nuovo master. La replica si considera incapacità di partecipare alle votazioni. E si ritira dalla corsa per diventare leader. Aspetta che venga scelto un nuovo master per potersi connettere a lui. Questo è un ulteriore passo per evitare la perdita di dati.


Qui il nostro team di prodotto ha scritto che il loro prodotto ha problemi quando lavora con Postgres. Nel frattempo, non è possibile accedere al master stesso, poiché non è disponibile tramite SSH. E non si verifica neanche un auto-failover.
Questo host è stato forzato a riavviarsi. A causa del riavvio si è verificato un auto-failover, anche se si poteva effettuare un auto-failover manuale, come ora capisco. E dopo il riavvio andremo a vedere cosa è successo con l'attuale master.

Nel frattempo, sapevamo già che avevamo problemi con i dischi, cioè già dal monitoraggio sapevamo dove scavare e cosa cercare.

Siamo entrati nel log di postgres e abbiamo iniziato a vedere cosa stava succedendo. Abbiamo notato commit che durano da un secondo a tre secondi, il che non va affatto bene. Abbiamo visto che l'autovacuum si avvia molto lentamente e in modo strano. E abbiamo trovato file temporanei sul disco. Cioè, questi sono tutti indicatori di problemi con i dischi.

Abbiamo controllato il dmesg di sistema (il log dei messaggi del kernel) e abbiamo visto che ci sono problemi con uno dei dischi. Il sottosistema disk era un RAID software. Abbiamo guardato /proc/mdstat e abbiamo notato che manca un disco. Cioè, qui abbiamo un RAID di 8 dischi, e ci manca uno. Se si guarda attentamente la diapositiva, si può vedere che manca sde. In termini pratici, abbiamo perso un disco. Questo ha innescato problemi con i dischi, e anche le applicazioni hanno avuto problemi nel funzionamento con il cluster Postgres.

In questo caso, Patroni non ci avrebbe aiutato, perché Patroni non ha il compito di monitorare lo stato del server e dello stato del disco. Dobbiamo monitorare tali situazioni con un monitoraggio esterno. Abbiamo rapidamente aggiunto il monitoraggio dei dischi al monitoraggio esterno.
E ci siamo chiesti se il fencing o un watchdog software potessero aiutarci. Abbiamo pensato che probabilmente non ci sarebbero stati di aiuto in questo caso, perché durante i problemi Patroni continuava a interagire con il cluster DCS e non vedeva alcun problema. Cioè, dal punto di vista di DCS e Patroni, tutto andava bene con il cluster, anche se in realtà c'erano problemi con il disco e problemi di disponibilità del database.

A mio parere, questo è uno dei problemi più strani che ho esplorato; ho passato molto tempo a esaminarlo, ho riletto molti log e l'ho definito cluster-simulator.

Il problema era che il vecchio master non poteva diventare una replica normale, cioè Patroni lo avviava, Patroni mostrava che quel nodo era presente come replica, ma allo stesso tempo non era una replica normale. Ora vedrete perché. Questo mi è rimasto dall'analisi di quel problema.

E come è iniziato tutto? È iniziato, come nel problema precedente, con dei rallentamenti nei dischi. Avevamo commit di uno o due al secondo.

Ci sono stati interruzioni delle connessioni, cioè i clienti venivano disconnessi.

Ci sono stati blocchi di varia gravità.

E, di conseguenza, il sottosistema disk non era molto reattivo.

E la cosa più misteriosa per me è stata la richiesta di spegnimento immediato. Postgres ha tre modalità di spegnimento:
- C'è la modalità graciale, in cui aspettiamo che tutti i clienti si disconnettano autonomamente.
- C'è la modalità veloce, in cui costringiamo i clienti a disconnettersi perché stiamo per spegnere.
- E immediate. In questo caso, immediate non informa nemmeno i clienti che devono disconnettersi, si spegne semplicemente senza preavviso. E a tutti i clienti viene già inviato dal sistema operativo un messaggio RST (messaggio TCP che indica che la connessione è stata interrotta e il cliente non ha più nulla da fare).
Chi ha inviato questo segnale? I processi in background di Postgres non si inviano tali segnali tra loro, cioè è un kill-9. Non si inviano messaggi del genere tra di loro, ma reagiscono solo a ciò, cioè si tratta di un riavvio di emergenza di Postgres. Chi lo ha inviato, non lo so.
Ho guardato con il comando «last» e ho visto una persona che ha effettuato l'accesso a questo server insieme a noi, ma ho esitato a porre la domanda. Forse è stato un kill -9. Avrei visto il kill -9 nei log, poiché Postgres scrive che ha ricevuto il kill -9, ma non l'ho visto nei log.

Continuando a indagare, ho notato che Patroni non ha scritto nel log per un periodo piuttosto lungo – 54 secondi. E se si confrontano i due timestamp, qui non ci sono stati messaggi per circa 54 secondi.

E in quel periodo si è verificato un failover automatico. Patroni ha funzionato bene qui. Il nostro vecchio master era non disponibile, qualcosa stava accadendo con esso. E sono iniziate le elezioni per un nuovo master. Qui tutto ha funzionato bene. Il nostro pgsql01 è diventato il nuovo leader.

Abbiamo una replica che è diventata master. E c'è una seconda replica. E con la seconda replica c'erano proprio dei problemi. Stava cercando di riconfigurarsi. Come capisco, stava cercando di cambiare recovery.conf, riavviare Postgres e connettersi al nuovo master. Ogni 10 secondi invia messaggi che sta provando, ma non riesce.

E durante questi tentativi, al vecchio master arriva un segnale di immediate-shutdown. Il master si riavvia. E anche il recovery viene interrotto, perché il vecchio master va in riavvio. Cioè, la replica non può connettersi a lui perché è in modalità spegnimento.

A un certo punto ha funzionato, ma la replica non è stata avviata.
Ho solo un'ipotesi, che in recovery.conf ci fosse l'indirizzo del vecchio master. E quando è apparso il nuovo master, la seconda replica continuava a cercare di connettersi al vecchio master.

Quando Patroni si è avviato sulla seconda replica, il nodo si è avviato, ma non è riuscito a connettersi per la replica. E si è creato un ritardo nella replica, che sembrava circa così. Cioè, tutti e tre i nodi erano al loro posto, ma il secondo nodo era in ritardo.

Se si guarda ai log registrati, si può notare che la replica non può avviarsi perché i registri delle transazioni sono diversi. E quei registri delle transazioni suggeriti dal master, indicati in recovery.conf, non sono adatti al nostro nodo attuale.

E qui ho commesso un errore. Dovevo controllare cosa c'era in recovery.conf per verificare la mia ipotesi che ci stessimo collegando al master sbagliato. Ma all'epoca stavo solo iniziando a capire questo e non mi è venuto in mente, oppure ho visto che la replica è in ritardo e che dovevo ricaricarla, cioè ho lavorato un po' superficialmente. È stato un mio errore.

Dopo 30 minuti è arrivato l'amministratore, cioè ho riavviato Patroni sulla replica. Avevo già perso le speranze, pensavo che dovessimo ricaricarla. E ho pensato: riavvio Patroni, forse esce qualcosa di buono. Il ripristino è partito. E il database si è persino aperto, era pronto ad accettare connessioni.

La replica è ripartita. Ma dopo un minuto si è fermata con un errore, che i registri delle transazioni non erano adatti.

Ho pensato di riavviarla ancora una volta. Ho riavviato di nuovo Patroni, e non ho riavviato Postgres, ma ho riavviato proprio Patroni nella speranza che in qualche modo riavviasse il database.

La replica è ripartita di nuovo, ma i segni nei registri delle transazioni erano diversi, non erano quelli della precedente tentativo di avvio. La replica si è fermata di nuovo. E il messaggio era già un po' diverso. E non era molto informativo per me.

E qui mi è venuta in mente un'idea: e se riavviassi Postgres, in quel momento sul master corrente facessi un checkpoint, per spostare il punto nei registri delle transazioni un po' più avanti, per iniziare il ripristino da un altro momento? Inoltre, avevamo anche delle riserve di WAL.

Ho riavviato Patroni, ho fatto un paio di checkpoint sul master, un paio di punti di riavvio sulla replica, quando si è aperta. E questo ha funzionato. Ho pensato a lungo perché ha funzionato e come è successo. E la replica si è avviata. E la replica non si è più interrotta.

Questo problema per me è uno dei più misteriosi, su cui sto ancora riflettendo, su cosa fosse realmente successo.
Quali conclusioni possiamo trarre? Patroni può funzionare come previsto e senza errori. Tuttavia, questo non garantisce al 100% che tutto vada bene. La replica può avviarsi, ma potrebbe trovarsi in uno stato semi-funzionale, e l'applicazione non può lavorare con una tale replica, perché ci saranno dati obsoleti.
Dopo ogni failover, è sempre necessario verificare che il cluster sia in ordine, ovvero che ci siano il numero necessario di repliche e che non ci sia lag nella replica.

Durante l'analisi di questi problemi, formulerò delle raccomandazioni. Ho cercato di riunirle in due diapositive. Probabilmente, tutte le storie avrebbero potuto essere riunite in due diapositive e solo queste raccontate.

Quando usate Patroni, è obbligatorio avere un monitoraggio. Dovete sempre sapere quando si è verificato un failover automatico, perché se non siete a conoscenza del failover, non controllate il cluster. E questo è un problema.
Dopo ogni failover, dobbiamo sempre verificare manualmente il cluster. Dobbiamo assicurarci che ci sia sempre il numero attuale di repliche, che non ci sia lag nella replica, e che nei log non ci siano errori relativi alla replica streaming, a Patroni, o al sistema DCS.
L'automazione può funzionare con successo, Patroni è uno strumento molto valido. Può funzionare, ma ciò non porterà il cluster allo stato desiderato. E se non ne veniamo a conoscenza, avremo problemi.
E Patroni non è una soluzione miracolosa. Dobbiamo comunque avere una comprensione di come funziona Postgres, come funziona la replica e di come Patroni opera con Postgres, e di come avviene l'interazione tra i nodi. Questo è necessario per poter risolvere manualmente i problemi che si presentano.

Come mi avvicino alla diagnosi? È successo che lavoriamo con diversi clienti e che nessuno di loro ha uno stack ELK, e dobbiamo analizzare i log aprendo 6 console e 2 schede. In una scheda ci sono i log di Patroni per ogni nodo, nell'altra scheda ci sono i log di Consul, oppure Postgres se necessario. Diagnosticare questo è molto difficile.
Quali approcci ho sviluppato? Per prima cosa, guardo sempre quando si è verificato il failover. Per me questo rappresenta una sorta di spartiacque. Osservo cosa è successo prima del failover, durante e dopo il failover. Il failover ha due marcatori: l'orario di inizio e quello di fine.
Poi guardo nei log gli eventi precedenti al failover, cioè cerco le cause per cui si è verificato il failover.
E questo fornisce un quadro di comprensione di ciò che è accaduto e di cosa si può fare in futuro per evitare che tali circostanze si ripetano (e di conseguenza per evitare un failover).
E dove guardiamo di solito? Io guardo:
- Inizialmente nei log di Patroni.
- Poi guardo i log di Postgres o i log di DCS, a seconda di ciò che trovo nei log di Patroni.
- E i log di sistema a volte forniscono anche comprensione delle cause del failover.

Come mi sento riguardo a Patroni? Mi sento molto bene riguardo a Patroni. A mio avviso, è il migliore disponibile oggi. Conosco molti altri prodotti. Sono Stolon, Repmgr, Pg_auto_failover, PAF. Quattro strumenti. Li ho provati tutti. Patroni mi è piaciuto di più.
Se mi chiedono: «Consiglierei Patroni?». Direi di sì, perché mi piace Patroni. E mi sembra di aver imparato a utilizzarlo.
Se sei interessato a vedere quali altri problemi possono sorgere con Patroni, oltre a quelli che ho menzionato, puoi sempre visitare la pagina su GitHub. Ci sono molte storie diverse e vengono discusse molte problematiche interessanti. Alla fine, alcuni bug sono stati segnalati e risolti, quindi è una lettura interessante.
Ci sono storie interessanti su come le persone si fanno del male da sole. Molto istruttivo. Leggi e capisci che non dovresti fare così. Ho messo una spunta.
E vorrei ringraziare molto l'azienda Zalando per aver sviluppato questo progetto, in particolare Alexander Kukushkin e Alexey Klyukin. Alexey Klyukin è uno dei coautori, non lavora più in Zalando, ma sono due persone che hanno iniziato a lavorare su questo prodotto.
E credo che Patroni sia una cosa fantastica. Sono contento che esista, è interessante. E un grande grazie a tutti i contributori che scrivono patch per Patroni. Spero che Patroni diventi sempre più maturo, straordinario e funzionante con il tempo. È già funzionante, ma spero che diventi ancora migliore. Quindi, se stai pensando di utilizzare Patroni, non avere paura. È una buona soluzione, può essere implementata e utilizzata.
Questo è tutto. Se hai domande, chiedi.

Domande
Grazie per la presentazione! Se dopo il failover è comunque necessario guardare lì con molta attenzione, perché abbiamo bisogno di un failover automatico?
Perché si tratta di una cosa nuova. Lavoriamo con essa da solo un anno. È meglio essere prudenti. Vogliamo entrare e controllare che tutto funzioni davvero come dovrebbe. Questo è un livello di sfiducia adulta: è meglio ricontrollare e verificare.
Ad esempio, siamo entrati al mattino e abbiamo guardato, vero?
Non al mattino, di solito veniamo a sapere dell'auto-failover praticamente subito. Riceviamo notifiche e vediamo che si è verificato un auto-failover. Entriamo e controlliamo quasi subito. Ma tutti questi controlli devono essere portati a un livello di monitoraggio. Se facciamo riferimento a Patroni tramite REST API, c'è la cronologia. Nella cronologia possiamo vedere i timestamp quando si è verificato il failover. Sulla base di questo si può fare monitoraggio. Possiamo guardare la cronologia, quanti eventi ci sono stati. Se abbiamo avuto più eventi, significa che c'è stato un auto-failover. Possiamo andare a controllare. Oppure la nostra automazione nel monitoraggio ha verificato che tutte le nostre repliche sono a posto, non ci sono ritardi e va tutto bene.
Grazie!
Grazie mille per la splendida esposizione! Se abbiamo spostato il cluster DCS lontano dal cluster Postgres, dobbiamo servirlo periodicamente? Quali sono le best practices in merito a quando alcuni segmenti del cluster DCS devono essere spenti, cosa fare con essi, ecc.? Come vive l'intera struttura in questi casi? E come si fanno queste operazioni?
Per un'azienda era necessario creare una matrice di problemi, per cosa accade se uno o più componenti si guastano. Su questa matrice esaminiamo sequenzialmente tutti i componenti e costruiamo scenari in caso di guasto di questi componenti. Pertanto, per ogni scenario di guasto, possiamo avere un piano d'azione per il ripristino. E nel caso del DCS, questo fa parte dell'infrastruttura standard. E l'amministratore si occupa di questo, e noi ci affidiamo già agli amministratori per la loro capacità di ripararlo in caso di emergenze. Se il DCS non è presente, lo implementiamo noi, ma senza seguirlo attentamente, poiché non siamo responsabili per l'infrastruttura, ma forniamo raccomandazioni su cosa e come monitorare.
Cioè, ho capito bene che bisogna disattivare Patroni, disattivare il failover, disattivare tutto prima di fare qualcosa con gli host?
Dipende da quanti nodi abbiamo nel cluster DCS. Se ci sono molti nodi e se mettiamo fuori uso solo uno di essi (una replica), il cluster mantiene il quorum. E Patroni rimane funzionante. E non si attiva nulla. Se abbiamo operazioni complesse che coinvolgono più nodi, la cui assenza potrebbe compromettere il quorum, allora sì, potrebbe avere senso mettere Patroni in pausa. Ha il comando corrispondente: patronictl pause, patronictl resume. Semplicemente mettiamo in pausa, e l'auto-failover non si attiva in quel momento. Facciamo manutenzione nel cluster DCS, poi togliamo la pausa e continuiamo a operare.
Grazie mille!
Grazie mille per la relazione! Come si sente il team di prodotto riguardo alla possibilità di perdita di dati?
Ai team di prodotto non importa, mentre i team leader sono preoccupati.
Quali garanzie ci sono?
Le garanzie sono molto complicate. C'è una presentazione di Aleksandr Kukushkin "Come calcolare RPO e RTO", ovvero il tempo di recupero e quanto dati possiamo perdere. Penso sia necessario trovare queste diapositive e studiarle. Quanto ricordo, ci sono passi specifici su come calcolare queste cose. Quante transazioni possiamo perdere, quanti dati possiamo perdere. Come alternativa possiamo usare la replica sincrona a livello di Patroni, ma è un'arma a doppio taglio: abbiamo affidabilità dei dati oppure perdiamo in velocità. C'è la replica sincrona, ma non garantisce comunque una protezione al 100% contro la perdita di dati.
Aleksy, grazie per la meravigliosa relazione! Hai esperienza nell'uso di Patroni per la protezione di livello zero? Cioè, in combinazione con standby sincrono? Questa è la prima domanda. E la seconda domanda. Hai utilizzato diverse soluzioni. Noi abbiamo usato Repmgr, ma senza auto-failover e ora stiamo pianificando di aggiungere auto-failover. E stiamo considerando Patroni come soluzione alternativa. Cosa puoi dire riguardo ai vantaggi rispetto a Repmgr?
La prima domanda riguardava le repliche sincrone. Nessuno di noi utilizza la replica sincrona, perché fa paura a tutti (Alcuni clienti già la usano, problemi di performance sostanzialmente non riscontrati — Nota del relatore). Ma abbiamo elaborato una regola secondo cui nel cluster di replica sincrona devono esserci almeno tre nodi, poiché se abbiamo due nodi e se il master o la replica smettono di funzionare, Patroni trasferisce quel nodo in modalità Standalone affinché l'applicazione continui a funzionare. In questo caso ci sono rischi di perdita di dati.
Per quanto riguarda la seconda domanda, abbiamo utilizzato Repmgr e tuttora lo usiamo con alcuni clienti per motivi storici. Cosa si può dire? In Patroni, l'auto-failover è integrato, mentre in Repmgr l'auto-failover è già una funzionalità aggiuntiva che deve essere attivata. È necessario avviare il demone Repmgr su ogni nodo e poi possiamo configurare l'auto-failover.
Repmgr verifica se i nodi Postgres sono vivi. I processi di Repmgr controllano l'esistenza reciproca, ma questo non è un approccio molto efficace, poiché potrebbero verificarsi casi complessi di isolamento di rete, in cui un grande cluster Repmgr può rompersi in più piccoli e continuare a funzionare. Non seguo più Repmgr da un po', forse hanno risolto questo problema… o forse no. La trasmissione delle informazioni sullo stato del cluster in DCS, come fa Stolon o Patroni, è l'opzione più sostenibile.
Alexey, ho una domanda, forse da principiante. In uno dei primi esempi, hai spostato DCS da una macchina locale a un nodo remoto. Sappiamo che la rete ha le sue peculiarità, vive di vita propria. E cosa succede se per qualche motivo il cluster DCS diventa non disponibile? Non parlerò dei motivi, potrebbero essere molteplici: da errori degli operatori di rete a problemi reali.
Non l'ho detto ad alta voce, ma il cluster DCS deve essere anche resistente ai guasti, cioè deve avere un numero dispari di nodi affinché possa essere raggiunto il quorum. Cosa succede se il cluster DCS diventa non disponibile o non riesce a raggiungere il quorum, ad esempio a causa di uno split di rete o di guasti dei nodi? In questo caso, il cluster Patroni passa in modalità di sola lettura. Il cluster Patroni non riesce a determinare lo stato del cluster e cosa fare. Non riesce a comunicare con DCS e a salvare il nuovo stato del cluster, quindi l'intero cluster passa in modalità di sola lettura. E attende o l'intervento manuale dell'operatore o il ripristino di DCS.
In parole povere, DCS diventa per noi un servizio altrettanto importante quanto il database stesso?
Sì, sì. In molte moderne aziende, il Service Discovery è una parte indispensabile dell'infrastruttura. Viene implementato anche prima che esista un database nell'infrastruttura. In altre parole, l'infrastruttura viene avviata, si fa il deployment in un centro dati, e abbiamo immediatamente il Service Discovery pronto. Se si tratta di Consul, può essere costruito anche il DNS su di esso. Se si tratta di Etcd, potrebbe essere parte di un cluster Kubernetes, in cui si dispiegherà tutto il resto. Mi sembra che il Service Discovery sia già una parte imprescindibile delle infrastrutture moderne. E se ne comincia a pensare molto prima che ai database.
Grazie!
Fonte: habr.com
