Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Introduzione

Qualche tempo fa, mi è stato affidato il compito di sviluppare un cluster resiliente per PostgreSQL, che opera in diversi data center collegati da fibra ottica all'interno della stessa città e in grado di resistere a un guasto (come un'interruzione di corrente) di un data center. Ho scelto come software per la resilienza Pacemaker, perché è la soluzione ufficiale di RedHat per creare cluster resilienti. È vantaggioso poiché RedHat fornisce supporto e perché è una soluzione versatile (modulare). Con esso sarà possibile garantire la resilienza non solo di PostgreSQL, ma anche di altri servizi, sia utilizzando moduli standard, sia creandoli per esigenze specifiche.

A questo punto è sorto un legittimo interrogativo: quanto sarà resiliente il cluster resiliente? Per esplorare questo aspetto, ho sviluppato un banco di prova che simula vari guasti sui nodi del cluster, attende il ripristino della funzionalità, riporta online il nodo guasto e continua il test in ciclo. Inizialmente questo progetto si chiamava hapgsql, ma col tempo mi sono stancato di un nome con solo una vocale. Pertanto, ho iniziato a chiamare i database resilienti (e l'IP fluttuante che li indica) krogan (un personaggio di un videogioco, il cui corpo ha tutti gli organi principali duplicati), mentre nodi, cluster e il progetto stesso sono stati denominati tuchanka (il pianeta dove vivono i krogan).

Attualmente la direzione ha autorizzato l'apertura del progetto alla comunità open source con licenza MIT. Il README sarà presto tradotto in inglese (poiché ci si aspetta che i principali utilizzatori siano gli sviluppatori di Pacemaker e PostgreSQL), mentre il precedente README in russo lo sistemerò (parzialmente) sotto forma di questo articolo.

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

I cluster vengono distribuiti su macchine virtuali VirtualBox. In totale saranno distribuite 12 macchine virtuali (per un totale di 36 GiB), che formeranno 4 cluster resilienti (diverse configurazioni). I primi due cluster sono composti da due server PostgreSQL, che si trovano in diversi data center, e da un server comune witness c quorum device (ospitato su una macchina virtuale economica in un terzo data center), che risolve l'incertezza 50%/50%, esprimendo il proprio voto a favore di una delle parti. Il terzo cluster è distribuito su tre data center: un master, due slave, senza quorum device. Il quarto cluster è composto da quattro server PostgreSQL, due per centro dati: uno master, gli altri repliche, e utilizza anche witness c quorum device. Il quarto supporta il guasto di due server o di un centro dati. Questa soluzione può essere, se necessario, scalata a un maggior numero di repliche.

Servizio di ora esatta ntpd è anch'esso configurato per la tolleranza ai guasti, ma utilizza il metodo stesso ntpd (orphan mode). Il server generale witness svolge il ruolo di server NTP centrale, distribuisce il suo tempo a tutti i cluster, sincronizzando così tutti i server tra loro. Se witness si guasta o risulta isolato, allora uno dei server del cluster (all'interno del cluster) inizierà a distribuire il suo tempo. Un proxy di caching HTTP è anch'esso attivo su witness, grazie al quale le altre macchine virtuali hanno accesso ai repository Yum. Nella realtà, servizi come l'ora esatta e il proxy saranno sicuramente ospitati su server dedicati, mentre nello stand sono ospitati su witness soltanto per risparmiare il numero di macchine virtuali e spazio.

Versioni

v0. Funziona con CentOS 7 e PostgreSQL 11 su VirtualBox 6.1.

Struttura dei cluster

Tutti i cluster sono progettati per essere ospitati in diversi centri dati, uniti in una rete piatta e devono tollerare il guasto o l'isolamento di rete di un centro dati. Pertanto non è possibile utilizzare per proteggersi da split-brain la tecnologia standard Pacemaker, chiamata STONITH (Shoot The Other Node In The Head) oppure fencing. Il principio è: se i nodi nel cluster iniziano a sospettare che un certo nodo possa avere dei problemi, non risponde o si comporta in modo anomalo, allora lo disattivano forzatamente tramite dispositivi “esterni”, ad esempio, una scheda di controllo IPMI o un UPS. Ma questo funziona solo se, in caso di guasto di un server singolo, IPMI o UPS continuano a funzionare. Qui si prevede una protezione contro un guasto molto più catastrofico, quando fallisce (ad esempio, viene disalimentato) l'intero centro dati. E in caso di tale guasto, tutti dispositivi stonith(IPMI, UPS, ecc.) non funzioneranno nemmeno.

Invece, il sistema si basa sull'idea di quorum. Tutti i nodi hanno diritto di voto e possono lavorare solo quelli che vedono più della metà di tutti i nodi. Questo numero di 'metà+1' è chiamato quorum. Se non si raggiunge il quorum, il nodo decide che si trova in isolamento di rete e deve disattivare le proprie risorse, cioè è una sorta di protezione contro lo split-brain. Se il software che gestisce questo comportamento non funziona, allora dovrebbe attivarsi il watchdog, ad esempio, sulla base di IPMI.

Se il numero di nodi è pari (cluster in due data center), può sorgere quella che viene chiamata incertezza. 50%/50% (cinquanta e cinquanta), quando l'isolamento di rete divide il cluster esattamente a metà. Pertanto, per un numero pari di nodi si aggiunge quorum device — un demone non esigente, che può essere eseguito sulla virtual machine più economica nel terzo data center. Esso dà la propria voce a uno dei segmenti (che vede), e in tal modo risolve l'incertezza 50%/50%. Il server su cui verrà eseguito il dispositivo di quorum l'ho chiamato witness (terminologia di repmgr, mi è piaciuta).

Le risorse possono spostarsi da un luogo all'altro, ad esempio, dai server guasti a quelli funzionanti, o su comando degli amministratori di sistema. Affinché i clienti sappiano dove si trovano le risorse di cui hanno bisogno (dove collegarsi?), si utilizzano IP fluttuanti (float IP). Questi sono IP che Pacemaker può spostare tra i nodi (tutto si trova in una rete piatta). Ognuno di essi simboleggia una risorsa (servizio) e si troverà lì dove è necessario collegarsi per accedere a questo servizio (nel nostro caso al DB).

Tuchanka1 (schema di sigillatura)

Struttura

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

L'idea era che abbiamo molte piccole banche dati con un carico basso, per le quali non è conveniente mantenere un server slave dedicato in modalità hot standby per le transazioni in sola lettura (non c'è bisogno di uno spreco di risorse simile).

In ogni data center c'è un server. Su ogni server ci sono due istanze di PostgreSQL (nella terminologia di PostgreSQL si chiamano cluster, ma per evitare confusione li chiamerò istanze (analogamente ad altri database), e solo i cluster di Pacemaker saranno definiti cluster). Un'istanza opera in modalità master e solo essa fornisce servizi (su di essa c'è un float IP). La seconda istanza opera come slave per il secondo data center e fornirà servizi solo se il suo master dovesse guastarsi. Poiché per la maggiore parte del tempo fornirà servizi (eseguendo query) solo un'istanza su due (il master), tutte le risorse del server sono ottimizzate per il master (viene allocata memoria per il cache shared_buffers, ecc.), ma in modo che anche la seconda istanza abbia sufficienti risorse (anche se per un'operazione non ottimale tramite la cache del filesystem) nel caso di un guasto di uno dei data center. Lo slave non fornisce servizi (non esegue query in sola lettura) durante il normale funzionamento del cluster, per evitare conflitti per le risorse con il master sulla stessa macchina.

Nel caso di due nodi, la tolleranza ai guasti è possibile solo con la replica asincrona, poiché con la replica sincrona il guasto dello slave porterà all'arresto del master.

Guasto witness

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Guasto witness (quorum device) considererò solo per il cluster Tuchanka1, con tutti gli altri sarà la stessa storia. In caso di guasto del witness, la struttura del cluster non cambierà, continuerà a funzionare come ha sempre fatto. Ma il quorum diventerà 2 su 3, e quindi qualsiasi guasto successivo diventerà fatale per il cluster. Sarà comunque necessario ripararlo urgentemente.

Guasto Tuchanka1

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Guasto di uno dei data center per Tuchanka1. In questo caso witness dà il suo voto al secondo nodo nel secondo data center. Qui l'ex slave si trasforma in master, quindi su un server funzionano entrambi i master e su di essi puntano entrambi i loro float IP.

Tuchanka2 (classico)

Struttura

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Schema classico di due nodi. Su uno funziona il master, sull'altro lo slave. Entrambi possono eseguire query (lo slave solo in sola lettura), quindi su entrambi ci sono float IP: krogan2 — sul master, krogan2s1 — sullo slave. La tolleranza ai guasti sarà sia per il master che per lo slave.

Nel caso di due nodi, la tolleranza ai guasti è possibile solo con la replica asincrona, poiché con la replica sincrona il guasto dello slave porterà all'arresto del master.

Guasto Tuchanka2

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

In caso di guasto di uno dei data center witness vota per il secondo. Sul data center attualmente funzionante verrà attivato un master, e su di esso verranno puntati entrambi gli IP float: quello master e quello slave. Ovviamente, l'istanza deve essere configurata in modo tale da avere risorse sufficienti (limiti per connessione, ecc.) per gestire simultaneamente tutte le connessioni e le richieste provenienti dall'IP float master e slave. Quindi, durante il normale funzionamento, deve avere un adeguato margine sui limiti.

Tuchanka4 (molti schiavi)

Struttura

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

È già un'altra estremità. Ci sono DB che ricevono un numero molto elevato di richieste in sola lettura (un caso tipico di un sito ad alta capacità). Tuchanka4 è una situazione in cui possono esserci tre o più schiavi per gestire tali richieste, ma comunque non troppi. Con un numero molto elevato di schiavi sarà necessario inventare un sistema gerarchico di replica. Nel caso minimo (nella figura), in ciascuno dei due data center ci sono due server, ciascuno con un'istanza di PostgreSQL.

Un'altra caratteristica di questo schema è che qui si può organizzare una replica sincrona. Essa è configurata per replicare, se possibile, in un altro data center, piuttosto che su una replica nello stesso data center dove si trova il master. Sia il master che ogni schiavo hanno un IP float. A dire il vero, tra gli schiavi bisogna implementare un bilanciamento delle richieste con qualche sql proxy, ad esempio, lato cliente. Diversi tipi di clienti possono avere bisogno di un diverso tipo sql proxy, e solo gli sviluppatori dei client sanno chi ha bisogno di cosa. Questa funzionalità può essere realizzata sia come demone esterno, sia come libreria client (connection pool), ecc. Tutto ciò esula dall'argomento del cluster DB altamente disponibile (l'alta disponibilità SQL proxy può essere realizzata indipendentemente, insieme all'alta disponibilità del client).

Guasto di Tuchanka4

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

In caso di guasto di un data center (cioè di due server), il testimone vota per il secondo. Di conseguenza, nel secondo data center operano due server: su uno c'è il master, e su di esso punta l'IP float master (per ricevere richieste read-write); sull'altro server opera uno schiavo con replica sincrona, e su di esso punta uno degli IP float slave (per richieste in sola lettura).

La prima cosa da notare è che l'IP float slave non sarà solo uno, ma solo uno. E per un funzionamento corretto con esso sarà necessario che sql proxy reindirizzava tutte le richieste all'unico float IP rimanente; e se sql proxy no, è possibile elencare tutti i float IP dei server slave separandoli con una virgola nell'URL di connessione. In questo caso con libpq la connessione avverrà al primo IP funzionante, come impostato nel sistema di test automatico. Potrebbe non funzionare in altre librerie, ad esempio JDBC, e sarà necessario sql proxy. Questo è stato fatto perché per gli float IP dei server slave è vietato essere attivi contemporaneamente su un singolo server, in modo che vengano distribuiti uniformemente tra i server slave, qualora ce ne siano più di uno attivo.

Secondo: anche in caso di guasto del data center verrà mantenuta la replica sincrona. E anche se si verifica un guasto secondario, cioè se uno dei due server nel data center rimanente si guasta, il cluster, pur smettendo di fornire servizi, manterrà comunque le informazioni su tutte le transazioni confermate, per le quali ha dato conferma di commit (non ci sarà perdita di informazioni in caso di guasto secondario).

Tuchanka3 (3 data center)

Struttura

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Questo è un cluster per situazioni in cui ci sono tre data center completamente operativi, ognuno dei quali ha un server DB completamente funzionante. In questo caso quorum device non è necessario. In un data center opera il master, negli altri due — gli slave. La replica è sincrona, di tipo ANY (slave1, slave2), cioè al cliente verrà fornita una conferma di commit quando uno qualsiasi degli slave risponderà per primo di aver accettato il commit. Le risorse sono indicate da un float IP per il master e due per gli slave. A differenza di Tuchanka4, tutti e tre gli float IP sono a prova di guasto. Per bilanciare le query SQL read-only è possibile utilizzare sql proxy (con resistenza individuale), oppure si può assegnare un float IP slave a metà dei clienti e un altro float IP slave all'altra metà.

Guasto Tuchanka3

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

In caso di guasto di uno dei data center, ne rimangono due. In uno è attivo il master e il float IP del master, nell'altro — lo slave e entrambi i float IP slave (l'istanza deve avere una riserva di risorse doppia, per accettare tutte le connessioni da entrambi i float IP slave). Tra il master e lo slave c'è replica sincrona. Inoltre, il cluster manterrà le informazioni sulle transazioni confermate e registrate (non ci sarà perdita di informazioni) in caso di distruzione di due data center (se non vengono distrutti contemporaneamente).

Ho deciso di non inserire una descrizione dettagliata della struttura dei file e del deployment. Chi vuole sperimentare, può leggere tutto questo nel README. Riporto solo la descrizione dei test automatici.

Sistema di test automatici

Per verificare la resilienza dei cluster con simulazione di vari guasti, è stato creato un sistema di test automatici. Viene avviato tramite script test/failure. Lo script può accettare come parametri i numeri dei cluster che si desidera testare. Ad esempio, questo comando:

test/failure 2 3

testerà solo il secondo e il terzo cluster. Se i parametri non sono specificati, verranno testati tutti i cluster. Tutti i cluster vengono testati parallelamente e il risultato viene visualizzato nel pannello tmux. Tmux utilizza un server tmux dedicato, quindi lo script può essere eseguito da tmux predefinito, risultando in un tmux annidato. Consiglio di utilizzare un terminale in una finestra grande e con un carattere piccolo. Prima di iniziare i test, tutte le macchine virtuali vengono ripristinate a uno snapshot al momento del completamento dello script. setup.

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Il terminale è diviso in colonne in base al numero di cluster in fase di test, di default (nello screenshot) ce ne sono quattro. Il contenuto delle colonne verrà descritto con l'esempio di Tuchanka2. I pannelli nello screenshot sono numerati:

  1. Qui viene visualizzata la statistica dei test. Colonne:
    • failure — nome del test (funzione nello script) che simula un guasto.
    • reaction — tempo medio in secondi, che indica quanto tempo ha impiegato il cluster a ripristinare le sue funzionalità. Viene misurato dall'inizio dell'esecuzione dello script che simula il guasto fino a quando il cluster ripristina le sue capacità e può continuare a fornire servizi. Se il tempo è molto breve, ad esempio sei secondi (cosa che accade nei cluster con più schiavi (Tuchanka3 e Tuchanka4)), significa che il guasto ha interessato un slave asincrono e non ha influenzato affatto il funzionamento, non ci sono stati cambiamenti nello stato del cluster.
    • deviation — indica la dispersione (precisione) del valore reaction attraverso il "metodo della deviazione standard".
    • count — quante volte è stato eseguito questo test.
  2. Il diario sintetico consente di valutare cosa sta facendo il cluster in questo momento. Viene visualizzato il numero dell'iterazione (test), un timestamp e il nome dell'operazione. Un'esecuzione troppo lunga (> 5 minuti) indica un qualche problema.
  3. heart (cuore) — tempo attuale. Per valutare visivamente le prestazioni maestro nella sua tabella viene continuamente scritto il tempo attuale utilizzando l'IP float del maestro. In caso di successo, il risultato viene mostrato in questo pannello.
  4. battito (pulsazione) — «tempo attuale», che era stato precedentemente registrato dallo script heart nel maestro, ora letto da schiavo attraverso il suo IP float. Permette di valutare visivamente le prestazioni dello schiavo e la replica. In Tuchanka1 non ci sono schiavi con IP float (nessuno schiavo che offre servizi), ma ci sono due istanze (DB), quindi qui non verrà mostrato battito, ma heart la seconda istanza.
  5. Monitoraggio dello stato del cluster utilizzando l'utility pcs mon. Mostra la struttura, la distribuzione delle risorse tra i nodi e altre informazioni utili.
  6. Qui viene visualizzato il monitoraggio di sistema da ogni virtual machine del cluster. Ci possono essere più pannelli — quanti virtual machine ha il cluster. Due grafici Carico CPU (nelle virtual machine ci sono due processori), nome della virtual machine, Carico di Sistema (chiamato Load Average, perché è mediato su 5, 10 e 15 minuti), dati sui processi e distribuzione della memoria.
  7. Tracciamento dello script che esegue i test. In caso di malfunzionamento — interruzione improvvisa o ciclo di attesa infinito — qui sarà possibile vedere la causa di questo comportamento.

I test vengono eseguiti in due fasi. Prima lo script passa attraverso tutte le varietà di test, scegliendo casualmente la virtual machine a cui applicare il test. Poi viene eseguito un ciclo di test infinito, le virtual machine e il malfunzionamento vengono ogni volta scelti casualmente. Un'interruzione improvvisa dello script di test (pannello inferiore) o un ciclo di attesa infinito per qualcosa (> 5 minuti di tempo di esecuzione per un'operazione, questo è visibile nel tracciamento) indica che uno dei test su questo cluster è fallito.

Ogni test consiste nelle seguenti operazioni:

  1. Avvio di una funzione che emula un malfunzionamento.
  2. Pronto? — attesa del ripristino delle prestazioni del cluster (quando vengono forniti tutti i servizi).
  3. Viene mostrato il tempo di attesa per il ripristino del cluster (reaction).
  4. Riparare — il cluster «è in riparazione». Dopo di che dovrebbe tornare a uno stato completamente operativo e pronto per il prossimo malfunzionamento.

Ecco l'elenco dei test con la descrizione di ciò che fanno:

  • ForkBomb: genera un "Out of memory" usando una fork bomb.
  • OutOfSpace: il disco è pieno. Tuttavia, il test è piuttosto simbolico, data la leggera carico generato durante il test; di solito, quando il disco è pieno, PostgreSQL non va in errore.
  • Postgres-KILL: uccide PostgreSQL con il comando killall -KILL postgres.
  • Postgres-STOP: sospende PostgreSQL con il comando killall -STOP postgres.
  • PowerOff: VBoxManage controlvm "virtual machine" poweroff.
  • Reset: riavvia la macchina virtuale con il comando VBoxManage controlvm "virtual machine" reset.
  • SBD-STOP: sospende il demone SBD con il comando killall -STOP sbd.
  • ShutDown: invia alla macchina virtuale un comando via SSH systemctl poweroff, il sistema termina correttamente l'operazione.
  • UnLink: isolamento di rete, comando VBoxManage controlvm "virtual machine" setlinkstate1 off.

La chiusura del test può avvenire tramite il comando standard di tmux "kill-window" Ctrl-b &, oppure con il comando "detach-client" Ctrl-b d: in questo modo il test si completa, tmux si chiude, le macchine virtuali si spengono.

Problemi rilevati durante il test

  • Attualmente il demone watchdog sbd gestisce l'arresto dei demoni osservati, ma non i loro blocchi. E, di conseguenza, gestisce in modo errato i malfunzionamenti che portano a blocchi solo Corosync e Pacemaker, ma che non li sospendono sbd. Per verifica Corosync già un PR#83 (su GitHub da sbd), è stato accettato nel ramo master. È stato promesso (in PR#83) che ci sarà qualcosa di simile anche per Pacemaker, spero che entro RedHat 8 lo faranno. Ma tali "malfunzionamenti" sono ipotetici e possono essere facilmente simulati artificialmente, ad esempio, killall -STOP corosync, ma non si verificano mai nella vita reale.

  • A Pacemaker nella versione per CentOS 7 è stata impostata in modo errato sync_timeout a quorum device, di conseguenza in caso di guasto di un nodo, con una certa probabilità si riavviava anche il secondo nodo, su cui il master avrebbe dovuto migrare. Questo è stato risolto aumentando sync_timeout a quorum device durante il deploy (allo script setup/setup1). Questa correzione non è stata accettata dagli sviluppatori Pacemaker, invece hanno promesso di riprogettare l'infrastruttura in modo tale (in un futuro indeterminato), che questo timeout venga calcolato automaticamente.

  • Se, durante la configurazione del database, è stato specificato che LC_MESSAGES (messaggi di testo) può utilizzare Unicode, ad esempio, ru_RU.UTF-8, allora, durante l'esecuzione postgres in un ambiente dove il locale non è UTF-8, ad esempio, in un ambiente vuoto (qui pacemaker+pgsqlms(paf) avvia postgres), allora nel log verranno visualizzati dei punti interrogativi al posto dei caratteri UTF-8.Gli sviluppatori PostgreSQL non hanno mai deciso cosa fare in questo caso. Si risolve impostando LC_MESSAGES=en_US.UTF-8 configurando (creando) un'istanza del DB.

  • Se è impostato wal_receiver_timeout (di default è 60s), durante il test PostgreSQL-STOP sul master nei cluster tuchanka3 e tuchanka4 non si verifica la riconnessione della replica al nuovo master.La replica è sincrona, quindi si ferma non solo il worker, ma anche il nuovo master. Si aggira impostando wal_receiver_timeout=0 durante la configurazione di PostgreSQL.

  • Raramente ho notato un'impasse della replica su PostgreSQL nel test ForkBomb (overflow di memoria). Dopo ForkBomb, a volte i worker possono non riconnettersi al nuovo master.Ho riscontrato questo solo nei cluster tuchanka3 e tuchanka4, dove, a causa della replica sincrona, il master si bloccava. Il problema si risolveva da solo dopo un certo periodo di tempo (circa due ore). È necessaria un'ulteriore indagine per risolvere questo problema. Dai sintomi sembra un bug precedente, causato da un'altra motivazione, ma con le stesse conseguenze.

L'immagine del krogan è stata presa da Deviant Art con il permesso dell'autore:

Modellazione di cluster ad alta disponibilità basati su PostgreSQL e Pacemaker

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster