====== Galera Cluster Test e Manutenzione ======
===== Introduzione =====
Il cluster Galera (Percona XtraDB Cluster) in uso ai LNF è composto da 3 nodi in configurazione multi-master, connessi verso l'utenza attraverso un load-balancer come rappresentato nel diagramma seguente:
{{:strutture:lnf:dr:calcolo:sistemi:cluster_nodes.jpg|}}
I nodi sono cosi organizzati:
* Il nodo **F** viene utilizzato per effettuare il backup secondo le schedule previste, e non viene solitamente impiegato dal load-balancer per soddisfare le richieste utente. Tuttavia, viene utilizzato per mantenere il servizio disponibile in casi di particolare emergenza dove i restanti nodi non risultano essere disponibili.
* Il nodo **G** e il nodo **L** vengono utilizzati per fornire il servizio durante il funzionamento ordinario, secondo una politica round-robin.
* Il cluster è organizzato in modo che fino a che un nodo nel sistema risulta funzionante, il servizio viene garantito.
* I nodi che compongono il cluster sono interconnesi tra loro in maniera completa, le relative connessioni sono schematizzate dai segmenti **A**, **B** e **C** nel diagramma sopra.
===== Definizione dei test =====
I test eseguiti mirano a verificare (in maniera non formale) il corretto funzionamento del cluster nel rispetto delle caratteristiche attese:
* **Fault tolerance**
* **Disaster recovery**
In maggior dettaglio, queste caratteristiche sono garantite dalla replicazione dei dati sui vari nodi (disaster recovery), che interagiscono coordinandosi nella fornitura del servizio al fine di ovviare a eventuali malfunzionamneti delle diverse componenti HW/SW del cluster (fault tolerance).
I test riguardano tre casistiche distinte:
* Partizionamento della rete
* Variazione dei nodi componenti il cluster in maniera //graceful//
* Variazione dei nodi componenti il cluster in maniera //ungraceful//
Queste tipologie di test non hanno lo scopo di coprire o indentificare tutte le situazioni che portano ad un malfunzionamento, ma piuttosto ad individuare i problemi più comuni nella gestione di un tale cluster e sopratutto a definire gli interventi necessari in questi casi.
Vengono di seguito riportati le definizioni dei test e le osservazioni a riguardo.
===== Network Partition =====
Il partizionamento della rete è stato simulato attraverso la definizione di alcune regole per //iptables//: il guasto su un segmento di connessione è quindi simulato introducendo quindi due regole distinte:
iptables -A INPUT -s -j DROP
iptables -A OUTPUT -d -j DROP
quindi il nodo con ip ''%%%%'' apparirà completamente isolato rispetto all'host sul quale si imposta la regola.
Un cluster Galera viene considerato il concetto di //Primary Component//, ovvero l'insieme di nodi che formano una partizione in numero sufficiente a raggiungere la maggioranza dei nodi totali.
==== Isolamento parziale ====
In questo caso di studio viene simulato il fault di un solo segmento di rete, come mostrato dallo schema:
{{:strutture:lnf:dr:calcolo:sistemi:part_1segmento.jpg|}}
In questo scenario, i nodi riescono ancora a condividere lo stato del cluster, e il funzionamento non viene inificiato.
==== Isolamento totale ====
In questo caso viene simulato il partizionamento della rete interrompendo la comunicazione tra due segmenti (1):
{{:strutture:lnf:dr:calcolo:sistemi:part_2segmenti.jpg|}}
A questo punto un nodo del cluster (**F** nello schema) non riuscendo più a raggiungere gli altri due nodi, si ritrova isolato e interrompe il funzionamento (2). Ad ogni interrogazione risponderà con un messaggio del genere:
ERROR 1047 (08S01) at line 1: WSREP has not yet prepared node for application use
Lo stato del nodo rispetto al cluster può essere verificato interrogando la variabile ''%%wsrep_ready%%'':
SHOW STATUS WHERE Variable_name='wsrep_ready';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wsrep_ready | OFF |
+---------------+-------+
Possiamo inoltre verificare la cardinalità del cluster con:
SHOW STATUS WHERE Variable_name='wsrep_cluster_size';
Se la query viene effettuata sul nodo isolato (**F** nel nostro caso) la risposta sarà:
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 1 |
+--------------------+-------+
Mentre se l'interrogazione avviene su uno degli altri nodi si avrà:
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 2 |
+--------------------+-------+
In questa situazione, i nodi che continuano a comunicare tra loro costituiscono il //Primary Component//.
A questo punto, se si procede al recovery, il nodo isolato potrebbe contattare nuovamente i nodi, e tornare a far parte del cluster. Immaginando di rispristinare un segmento di rete alla volta, diciamo il segmento **F-G**, il nodo precedentemente isolato tenta di sincronizzarsi con fli altri nodi, quindi individua un nodo all'interno del //Primary Component// al quale richiedere una copia aggiornata dei dati (chiamato //donor//). Se la scelta dovesse ricadere sul nodo non **direttamente** raggiungibile a causa del fault di rete non ancora risolto (nel caso **L**), il nodo non raggiungerebbe il nodo //donor// designato (3). In questa situazione, il nodo isolato non può effettuare il sync e dopo un certo numero di tentativi spegne il servizio (4).
Per ovviare al problema, è possibile forzare il //donor// utilizzato per il sync impostando una voce nel file di configurazione del servizio, ora spento sul server isolato:
wsrep_sst_donor=''
Dove ''%%%%'' è il nome del nodo //donor// come specificato sul file di configurazione del servizio mysqld sul nodo //donor//, ad esempio:
...
wsrep_node_name=''
...
Prima di riavviare il servizio sul nodo precedentemente isolato, è necessario assicurarsi che tutti i nodi presenti nella la lista alla voce ''%%wsrep_cluster_address%%'' nel file di configurazione del servizio siano effettivamente raggiungibili. Ad esempio, se abbiamo:
...
wsrep_cluster_address="gcomm://,,"
...
e supponendo che il nodo isolato abbia ip ''%%%%'', dobbiamo assicurarci che gli indirizzi ''%%%%'' e ''%%%%'' siano raggungibili (nel caso in esempio, questo presuppone che entrambi i fault sulla rete siano stati risolti). In caso contrario, il servizio non potrà partire, e l'avvio fallirà. Per ovviare al problema, supponendo che il collegamento ripristinato sia quello in figura al punto (3) che congiunge i nodi **G** e **F**, possiamo impostare:
...
wsrep_cluster_address="gcomm://,"
wsrep_node_name=''
...
E avviare il server. Dopo aver constatato il corretto riavvio, e dopo aver ripristinato per intero le connessioni di rete, possiamo ripristinare le voci sul file di configurazione ai valori originari, e modificare la variabile (dinamica) per rispecchiare la configurazione del cluster con la query:
SET GLOBAL wsrep_cluster_address='gcomm://,,';
===== Stop dei servizi (alterazione struttura cluster graceful) =====
Durante i test effettuati non sono stati riscontrati interruzioni del servizio o malfunzionamenti anche nel caso in cui i nodi che vengono spenti siano due.
{{:strutture:lnf:dr:calcolo:sistemi:graceful.jpg|}}
Il cluster in questo caso, diviene consapevole della riduzione della cardinalità del cluster (e del //Primary Component//) e modifica il metodo di calcolo del quorum per consentire il funzionamento, in maniera del tutto automatica.
===== Crash della macchina (alterazione struttura cluster ungraceful) =====
Nel test precedente i nodi "uscenti" dal cluster si preoccupano di informare gli altri nodi del proprio cambiamento di stato prima di spegnersi. Se lo spegnimento avviene in maniera //ungraceful// (in parole povere "staccando la spina" del nodo) questo scambio di informazioni non può avvenire, con tutte le conseguenze del caso.
Questo fa capire come questo tipo di situazioni causano i problemi pià difficili da gestire, e spesso richiedono manutenzione //supervised// sul cluster. Lo spegninmento improvviso è stato simulato con la funzione "Power Off" fornita da OVirt che spegne nella maniera più veloce (e brusca) possibile una macchina virtuale.
==== Spegnimento di un nodo ====
Spegnendo un solo nodo, il cluster viene mantenuto in vita dai nodi restanti che si accorgono velocemente della mancanza del nodo spento; non è necessario nessun intervento manuale.
==== Spegnimento di due nodi ====
Quando un ulteriore nodo viene spento, il nodo superstite non riceve nessuna informazione sullo spegnimento imminente, e non ha la possibilità di rimodulare il calcolo del quorum necessario al funzionamento del cluster. Quindi è costretto a interrompere la gestione delle richieste (come schematizzato in figura).
{{:strutture:lnf:dr:calcolo:sistemi:ungraceful.jpg|}}
Infatti, se immettiamo la query:
SHOW STATUS WHERE Variable_name='wsrep_ready' OR Variable_name='wsrep_local_state_comment' OR Variable_name='wsrep_cluster_status' OR Variable_name='wsrep_cluster_size';
Possiamo analizzare la risposta ricevuta:
+---------------------------+--------------+
| Variable_name | Value |
+---------------------------+--------------+
| wsrep_local_state_comment | Initialized |
| wsrep_cluster_size | 1 |
| wsrep_cluster_status | Non-Primary |
| wsrep_ready | OFF |
+---------------------------+--------------+
E quindi constatare che il nodo non è operativo e inoltre non far parte del //Primary Component// (dato che non esiste una partizione di nodi del cluster funzionanti, connessi e in maggioranza). Se un nodo precedentemente spento torna in funzione, il cluster torna ad essere operativo in maniera canonica (ovviamente anche se tornano operativi più nodi). Il nodo superstite può comunque riprendere le attività in autonomia, con la digitazione della query:
SET GLOBAL wsrep_provider_options='pc.bootstrap=true';
A questo punto il nodo continua ad operare in attesa che gli altri nodi vengano rispristinati, garantendo il servizio ma ovviamente ne //fault tolerance// ne //disaster recovery//, non essendoci replicazione.
==== Spegnimento di tutti i nodi ====
Quando l'interruzione del servizio riguarda l'intero cluster, il problema si complica, poichè possono presentarsi casi di //split brain// e inconsistenza dei dati. I nodi del cluster devono quindi essere riavviati attivando per primo il nodo con la sequenza di modifiche più recenti sui datafile del DB. Possiamo individuare quindi il numero di sequenza dei nodi lanciando:
# mysqld_safe --wsrep-recover
151105 15:41:34 mysqld_safe Logging to '/var/lib/mysql/.err'.
151105 15:41:34 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
151105 15:41:34 mysqld_safe WSREP: Running position recovery with --log_error='/var/lib/mysql/wsrep_recovery.PATt6w' --pid-file='/var/lib/mysql/-recover.pid'
151105 15:41:36 mysqld_safe WSREP: Recovered position 35114f7b-7320-11e5-9e1e-7f73435b73aa:259056
151105 15:41:39 mysqld_safe mysqld from pid file /var/lib/mysql/.pid ended
La stringa alla penultima voce indica, a fine riga, il numero di sequenza (nel nostro caso 259056). Il nodo che risulta avere il numero di sequenza più alto deve essere riavviato per primo. Per riavviare il server con numero di sequenza superiore digitare:
# systemctl start mysql@bootstrap.service
In seguito poi è possibile riavviare i restanti nodi, che entreranno a far parte del cluster in maniera automatica.
===== Monitoraggio dello stato durante un'operazione di sync dei dati =====
I problemi evidenziati ai punti precedenti si presentamo principalmente quando un'operazione di sync (link alle operazioni di sst) ha luogo all'interno del cluster. Per monitorare lo stato dei nodi rispetto alla sincronizzazione all'interno dei cluster, possiamo utilizzare sui vari nodi la query:
SHOW STATUS LIKE 'wsrep_local_state_comment';
I valori che la variable può assumere sono:
* "Synced" : il nodo è sincronizzato rispetto al cluster, è lo stato assunto quando il funzionamento del cluster è regolare.
* "Joining: receiving state transfer" : il nodo sta ricevendo aggiornamenti sullo stato del cluster (da uno specifico nodo //donor//).
* "Donor/Desynced" : il nodo sta fornendo ad un altro nodo del cluster aggiornamenti sui dati.
Si noti che durante le fasi di sincronizzazione i due nodi coinvolti non risultano operativi per tutta la durata del trasferimento, quindi le comunicazioni verso un nodo che si trova in stato "Donor/Desynced" o "Joining: receiving state transfer" risulteranno sospese;