Evoluzione dell’alta affidabilità su Linux: realizzare un NAS con Pacemaker, DRBD ed exportfs

8

linux-ha    Pacemaker    

Nel precedente articolo è stato effettuato un confronto tra le tipologie possibili di utilizzo relativo ad Heartbeat, nella modalità classica ed in quella mediante CRM (Cluster Resource Manager) per l’erogazione in alta affidabilità del servizio Apache.
In quest’ultimo articolo verrà proposto un vero case study relativo alla configurazione di un cluster erogante uno storage NFS in alta affidabilità i cui dati verranno ridondati via DRBD (Network Raid 1), mediante la configurazione di Pacemaker.
Peculiarità del progetto sarà l’utilizzo per l’erogazione delle directory NFS di entrambi i nodi, in modo da sfruttare tutte le macchine presenti, per un cluster di tipo active-active.

Il progetto

Quanto si propone di realizzare è un sistema in alta affidabilità che consenta di impiegare tutte le macchine presenti all’interno del cluster per rendere disponibile spazio all’interno di una rete. In altre parole, un Network Attached Storage (NAS), un’entità composta da due macchine collegate alla rete che consentano a dei client di montare le partizioni e fruire dello spazio reso disponibile.
Le componenti impiegate all’interno del progetto saranno:

  1. Heartbeat e Pacemaker: per la gestione del cluster;
  2. DRBD: per la replica dei dati fra le due macchine attraverso un network raid 1;
  3. NFS: per la condivisione attraverso la rete dello spazio;

Il progetto si rifà nelle sue fondamenta a quanto illustrato nella seconda parte del precedente articolo e, se non lo si è ancora fatto, è bene dare una rapida lettura in modo da poter applicare i concetti lì illustrati senza perplessità.
Il cluster verrà costruito su due sistemi (debian-lenny-nodo1 e debian-lenny-nodo2) collegati ciascuno alla rete locale (192.168.1.0/24) e l’uno all’altro da un cavo cross (10.0.0.0/24, connessione necessaria per realizzare il network raid 1 con DRBD), lo spazio locale verrà esportato attraverso il protocollo NFS. In particolare verranno definite due condivisioni distinte, corrispondenti a due cartelle locali chiamate /share-a e /share-b.
Obiettivo ultimo è quello di ottenere ciascuna condivisione esportata da un nodo, in modo da avere (a regime ed in condizioni ottimali) un cluster in cui nessun nodo risulti passivo.
Unico requisito richiesto sui nodi è la presenza dei pacchetti heartbeat e pacemaker (la cui installazione è descritta nell’articolo precedente) e del demone NFS. Nei sistemi Debian ed Ubuntu sarà quindi necessario installare il pacchetto nfs-kernel-server mentre in RedHat/SuSe il pacchetto sarà nfs.

La gestione locale dei dati: device, md, LVM o tutti e tre?

Prima di iniziare a configurare il cluster è bene decidere come verranno gestiti sulle macchine i dati che andranno esposti. Questa scelta, la più delicata poiché intacca direttamente le performance, va fatta sulla base dell’hardware disponibile, partendo da tre ambiti operativi:

  • device: partizioni fisiche (ad esempio /dev/sda3, /dev/sda4 e via dicendo);
  • md (Multiple Disk): gestione locale dei raid, i dati sono ridondati sulle singole macchine (oltre che in rete);
  • LVM (Logical Volume Manager): volumi logici, per una gestire più elastica dello spazio locale;

Partendo dal presupposto che l’utilizzo dei device è indispensabile (visto che si sta parlando di dischi locali), scegliere se configurare raid locali o LVM dipende da quanto si dispone.
Se le macchine utilizzate possiedono una controller RAID hardware, allora si potrà evitare di configurare i dispositivi md, così come se si ritiene la ridondanza locale superflua (del resto i dati verranno comunque ridondati in rete).
Se il sistema necessiterà di frequenti variazioni in termini di spazio sulle partizioni esposte sarà doveroso impiegare volumi logici in modo da gestire con facilità operazioni di ridimensionamento. Inoltre la gestione dei volumi logici potrebbe essere preferita dai più, proprio per la semplicità con cui è possibile apportare variazioni allo spazio disponibile, soprattutto in situazioni complesse.
Lampante è come più livelli di complessità si aggiungono, maggiore sarà il carico che graverà sul sistema operativo e maggiore dovrà essere la capacità di gestire eventuali malfunzionamenti.
Come sempre quindi, la libertà di scelta è totale e dipende unicamente dell’utente.
Nel caso illustrato la scelta è caduta sull’impiego di LVM su due partizioni (/dev/sda3 e /dev/sdb3), in quanto le macchine su cui la soluzione viene implementata possiedono una controller RAID integrata che si occupa di ridondare i due dischi disponibili.
Su entrambe le macchine sono stati definiti due Physical Volume:

# pvcreate /dev/sd[ab]3

Ai quali è stato associato un Volume Group denominato vg_share:

# vgcreate vg_share /dev/sd[ab]3

Ed infine sono stati creati due Logical Volume, denominati lv_drbd0 ed lv_drbd1, i quali rappresenteranno le due condivisioni relative ai device drbd che verranno in seguito creati:

# lvcreate -n lv_drbd0 -L 1G vg_share
# lvcreate -n lv_drbd1 -L 1G vg_share

l’opzione “-L” definisce lo spazio massimo occupabile dal volume logico, chiaramente essendo un progetto con fini puramente didattici lo spazio è stato ridotto per facilitare e velocizzare le operazioni di creazione ed aggiornamento.
All’interno della cartella /dev/mapper/ saranno quindi visibili i seguenti file:

# ls -la /dev/mapper/
totale 0
drwxr-xr-x  2 root root     100 2010-09-14 12:15 .
drwxr-xr-x 17 root root    3700 2010-09-14 12:15 ..
crw-rw----  1 root root  10, 59 2010-09-14 12:15 control
brw-rw----  1 root disk 251,  0 2010-09-14 12:15 vg_share-lv_drbd0
brw-rw----  1 root disk 251,  1 2010-09-14 12:15 vg_share-lv_drbd1

a dimostrazione che i volumi logici sono pronti ad essere utilizzati.

Configurazione preliminare di Heartbeat

La configurazione preliminare di Heartbeat si rifà totalmente a quanto illustrato nel precedente articolo al paragrafo “Configurazione di Heartbeat in modalità CRM con Pacemaker”:

/etc/ha.d/ha.cf:

autojoin none
keepalive 1
deadtime 10
warntime 5
initdead 20
mcast eth0 239.0.0.43 694 1 0
bcast eth1
node    debian-lenny-nodo1
node    debian-lenny-nodo2
crm respawn

/etc/ha.d/authkeys:

auth 1
1 crc

Quindi una volta avviato Heartbeat:

# /etc/init.d/heartbeat start

è possibile definire le configurazioni preliminari del cluster:

# crm configure property stonith-enabled="false"
# crm configure property no-quorum-policy="ignore"
# crm configure primitive ping ocf:pacemaker:ping params host_list="192.168.1.1" name="ping" op monitor interval="10s" timeout="60s" op start timeout="60s" op stop timeout="60s"
# crm configure clone ping_clone ping meta globally-unique="false"

In breve viene disabilitato lo stonith, viene imposto al cluster di ignorare l’assenza di quorum e viene definita una risorsa ping che controlla la connettività in entrambi i nodi attraverso il clone della risorsa stessa.
La situazione del cluster viene così riassunta:

# crm status
============
Last updated: Tue Sep 14 12:17:29 2010
Stack: Heartbeat
Current DC: debian-lenny-nodo1 (1627f3ec-ec7e-4f0c-b69e-e9729933a2cc) - partition with quorum
Version: 1.0.8-042548a451fce8400660f6031f4da6f0223dd5dd
2 Nodes configured, unknown expected votes
1 Resources configured.
============

Online: [ debian-lenny-nodo1 debian-lenny-nodo2 ]

 Clone Set: ping_clone
     Started: [ debian-lenny-nodo1 debian-lenny-nodo2 ]

Per qualsiasi dubbio in merito alle configurazioni sopra riportate, come già detto, è bene far riferimento al precedente articolo.

Configurazione preliminare di DRBD

In questa fase del progetto è necessario configurare i due volumi logici creati in precedenza affinché possano essere utilizzati con DRBD. Per prima cosa il sistema deve essere predisposto per supportare DRBD, il che significa che vanno installati i pacchetti relativi al modulo che verrà inserito all’interno del kernel. Come sempre tutto dipende dalla distribuzione che si sta utilizzando, nel caso illustrato Debian offre il meta pacchetto drbd8-modules-2.6-686 che si occupa di installare la corretta versione associata al kernel utilizzato:

apt-get install drbd8-modules-2.6-686 drbd8-utils

Con Ubuntu è sufficiente installare il pacchetto drbd8-utils che risolverà le dipendenze ed installerà il modulo, stesso dicasi per SuSe, mentre per RedHat è necessario utilizzare i pacchetti Extra di CentOS.
Maggiori informazioni sulle versioni disponibili sono qui: http://www.drbd.org/download/packages/.

Installato il modulo, per configurare i due volumi logici i passi da seguire sono semplicissimi. In prima istanza va modificato il file /etc/drbd.conf come segue:

global { usage-count no; }

common { protocol C; syncer { rate 100M; } }

resource r0 {
        device /dev/drbd0;
        meta-disk internal;
        disk /dev/mapper/vg_share-lv_drbd0;

        on debian-lenny-nodo1 {
                address 10.0.0.1:7788;
        }

        on debian-lenny-nodo2 {
                address 10.0.0.2:7788;
        }
}

resource r1 {
        device /dev/drbd1;
        meta-disk internal;
        disk /dev/mapper/vg_share-lv_drbd1;

        on debian-lenny-nodo1 {
                address 10.0.0.1:7789;
        }

        on debian-lenny-nodo2 {
                address 10.0.0.2:7789;
        }
}

Le dichiarazioni presenti nel file sono auto esplicative: Il tipo di algoritmo con cui verrà gestita la sincronizzazione è C (replicazione sincrona, standard, qui i dettagli sui diversi protocolli disponibili), mentre la velocità a cui i dati verranno sincronizzati è 100 Mega (qui come calcolare la corretta velocità di sincronizzazione).
sono state definite due risorse (resource) che poggeranno su volumi logici interni (disk) e registreranno i meta-dati relativi alla sincronizzazione internamente (meta-disk), ciascuna risorsa farà viaggiare le informazioni per la sincronizzazione sull’interfaccia locale relativa alla rete 10.0.0.0 (address) ad una porta differente (7788 e 7789).

A questo punto i volumi logici vanno inizializzati in modo che il sistema possa iniziare a servirsi dei device. Le operazioni da compiere in sequenza, su entrambi i nodi, sono le seguenti:

# modprobe drbd
# drbdadm create-md r0
# drbdadm create-md r1
# drbdadm up r0
# drbdadm up r1

Linea per linea, ecco quanto fatto:

  1. Caricamento del modulo drbd: il sistema è in grado di supportare dispositivi drbd;
  2. Creazione del meta-device sopra il volume logico r0, così come dichiarato nel file di configurazione, il volume /dev/mapper/vg_share-lv_drbd0 viene inizializzato;
  3. Creazione del meta-device sopra il volume logico r1, così come dichiarato nel file di configurazione, il volume /dev/mapper/vg_share-lv_drbd1 viene inizializzato;
  4. Attivazione del meta-device, da questo momento il sistema “vede” /dev/drbd0;
  5. Attivazione del meta-device, da questo momento il sistema “vede” /dev/drbd1;

Come ultima fase per l’inizializzazione dei dispositivi è necessario effettuare la prima sincronizzazione in modo che i volumi remoti vengano allineati. Per far ciò è necessario lanciare il seguente comando su uno solo dei due nodi:

# drbdadm -- --overwrite-data-of-peer primary all

Il comando forza lo stato primario del nodo su cui è stato lanciato ed avvia nel contempo la prima sincronizzazione, al termine della quale lo stato dei meta-device drbd sarà il seguente:

# cat /proc/drbd 
version: 8.3.7 (api:88/proto:86-91)
GIT-hash: ea9e28dbff98e331a62bcbcc63a6135808fe2917 build by root@debian-lenny-nodo1, 2010-09-13 18:03:02
 0: cs:Connected ro:Secondary/Secondary ds:UpToDate/UpToDate C r----
    ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:b oos:0
 1: cs:Connected ro:Secondary/Secondary ds:UpToDate/UpToDate C r----
    ns:0 nr:0 dw:0 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:b oos:0

Per concludere la configurazione preliminare di drbd andrà creato un filesystem su ciascuno dei device creati:

# mkfs.ext3 /dev/drbd0
# mkfs.ext3 /dev/drbd1

E rimosso l’avvio automatico del demone dal sistema, poiché questo verrà gestito totalmente dal cluster. Per Debian ed Ubuntu il comando da lanciare sarà:

# update-rc.d -f drbd remove

mentre nel caso di RedHat/CentOS/SuSe:

# chkconfig --level 123456 drbd off

E’ giunto il momento di configurare lo storage condiviso.

Configurazione di Heartbeat e DRBD

Prima implementare l’esposizione dello spazio che abbiamo creato via NFS è necessario configurare il cluster affinché gestisca i dispositivi DRBD e ne amministri lo stato. Come stabilito in precedenza infatti, il controllo di tali componenti sarà totalmente nelle mani del cluster e non del sistema operativo. In altre parole i due componenti DRBD non saranno gestiti come demoni, ma come due risorse distinte.
DRBD, rispetto a quanto illustrato sinora, non è una risorsa standard: non è assimilabile infatti alle risorse comuni poiché necessità di risiedere su (almeno) due nodi, questa peculiarità però non permette comunque di creare un clone della risorsa da ripartire sulle altre macchine, poiché in ciascuno dei nodi su cui essa sarà attiva questa dovrà assumere uno stato differente.
Per questa ragione, dopo aver definito le risorse standard associate ai dispositivi DRBD in precedenza creati (r0 ed r1):

# crm configure primitive drbd0 ocf:linbit:drbd params drbd_resource="r0" op monitor interval="20s" timeout="40s" op start interval="0" timeout="240s" op stop interval="0" timeout="100s"
# crm configure primitive drbd1 ocf:linbit:drbd params drbd_resource="r1" op monitor interval="20s" timeout="40s" op start interval="0" timeout="240s" op stop interval="0" timeout="100s"

è necessario configurare due risorse multi-state:

# crm configure ms ms-drbd0 drbd0 meta master-max="1" notify="true"
# crm configure ms ms-drbd1 drbd1 meta master-max="1" notify="true"

I due comandi, ciascuno relativo ad una risorsa DRBD, definiscono una risorsa multi-state che può avere un solo nodo master (master-max) il quale una volta assunto il proprio stato (master o slave che sia) deve notificare il successo agli altri nodi (notify), in modo che la risorsa sia in uno stato coerente su entrambi i nodi.
Dopo qualche secondo sarà possibile osservare all’interno dello stato del cluster quanto realizzato:

============
Last updated: Wed Sep 15 20:16:44 2010
Stack: Heartbeat
Current DC: debian-lenny-nodo1 (1627f3ec-ec7e-4f0c-b69e-e9729933a2cc) - partition with quorum
Version: 1.0.8-042548a451fce8400660f6031f4da6f0223dd5dd
2 Nodes configured, unknown expected votes
3 Resources configured.
============

Online: [ debian-lenny-nodo1 debian-lenny-nodo2 ]

 Clone Set: ping_clone
     Started: [ debian-lenny-nodo1 debian-lenny-nodo2 ]
 Master/Slave Set: ms-drbd0
     Masters: [ debian-lenny-nodo1 ]
     Slaves: [ debian-lenny-nodo2 ]
 Master/Slave Set: ms-drbd1
     Masters: [ debian-lenny-nodo1 ]
     Slaves: [ debian-lenny-nodo2 ]

Come si evince dall’output mostrato il nodo master per entrambe le risorse è debian-lenny-nodo1.

Ora che il cluster controlla i dispositivi è necessario esporli. Per fare ciò sono necessarie tre componenti:

  1. Indirizzo IP: ossia l’indirizzo di riferimento per l’intera rete a quela specifica condivisione;
  2. Filesystem: ovvero il mount del filesystem relativo alla risorsa DRBD in modo che ci si possa scrivere sopra;
  3. NFS: in modo da esporre la condivisione con il protocollo NFS e permettere il mount da parte di client di rete;

Esisteranno quindi due gruppi di risorse contenenti le tre risorse descritte, tali gruppi verranno nominati come le cartelle che dovranno esporre, così come definito in fase di studio del progetto: share-a e share-b.

Indirizzo IP

La configurazione degli indirizzi IP è identica a quanto illustrato nel precedente articolo:

# crm configure primitive share-a-ip ocf:heartbeat:IPaddr2 params ip="192.168.1.33" nic="eth0" op monitor interval="20s" timeout="40s"
# crm configure primitive share-b-ip ocf:heartbeat:IPaddr2 params ip="192.168.1.34" nic="eth0" op monitor interval="20s" timeout="40s"

al gruppo che eroga share-a pertanto verrà associato l’indirizzo 192.168.1.33 mentre a share-b 192.168.1.34, entrambe le risorse sono gestite dal il resource agent IPAddr2.

Filesystem

Il dispositivo /dev/drbd0 verrà associato a share-a ed alla directory /share-a, così come share-b monterà /dev/drbd1 sulla directory /share-b.
Andranno quindi create le directory per i mount su entrambi i nodi:

# mkdir /share-a
# mkdir /share-b

Ed infine configurate le risorse attraverso il resource agent Filesystem:

# crm configure primitive share-a-fs ocf:heartbeat:Filesystem params device="/dev/drbd0" directory="/share-a" fstype="ext3" op monitor interval="20s" timeout="40s" op start interval="0" timeout="60s" op stop interval="0" timeout="60s" meta is-managed="true" 
# crm configure primitive share-b-fs ocf:heartbeat:Filesystem params device="/dev/drbd1" directory="/share-b" fstype="ext3" op monitor interval="20s" timeout="40s" op start interval="0" timeout="60s" op stop interval="0" timeout="60s" meta is-managed="true"

NFS

Per permettere l’esportazione di ciascuna share in maniera indipendente, fermo restando che su ogni sistema il demone NFS deve essere in esecuzione, è possibile utilizzare un resource agent denominato exportfs.
Questo resource agent creato da Holger Teutsch, potrebbe non essere disponibile se non si possiede una versione recente di Heartbeat (successiva al marzo 2010). In questo caso il problema è facilmente risolvibile installando il resource agent su entrambi i nodi come segue:

# cd /usr/lib/ocf/resource.d/heartbeat/
# wget http://hg.linux-ha.org/agents/raw-file/c8d8b1126ffd/heartbeat/exportfs
# chmod +x exportfs

In questo modo sarà possibile dichiarare due risorse exportfs affinché espongano le due cartelle:

# crm configure primitive share-a-exportfs ocf:heartbeat:exportfs params directory="/share-a" clientspec="192.168.1.0/24" options="rw,async,no_subtree_check,no_root_squash" fsid="1" op monitor interval="10s" timeout="30s" op start interval="0" timeout="40s" op stop interval="0" timeout="40s"
# crm configure primitive share-b-exportfs ocf:heartbeat:exportfs params directory="/share-b" clientspec="192.168.1.0/24" options="rw,async,no_subtree_check,no_root_squash" fsid="2" op monitor interval="10s" timeout="30s" op start interval="0" timeout="40s" op stop interval="0" timeout="40s"

Il resource agent exportfs si occupa di aggiornare in tempo reale lo stato delle export NFS di sistema, utilizzando appunto il comando exportfs.
Come è facile notare la dichiarazione del resource agent si rifà totalmente alla sintassi del comando exportfs, o più semplicemente al contenuto del file /etc/exports (ossia il file di configurazione di NFS), in particolare:

  • directory definisce la directory locale che verrà esportata;
  • client_spec definisce quali client potranno accedere alla condivisione;
  • options contiene le opzioni di esportazione: lettura/scrittura (rw), l’allineamento del filesystem sarà asincrono (async) e tutto il volume verrà esportato senza controllo alle sotto directory (no_subtree_check), infine sarà possibile per l’utente root del client remoto avere privilegi di root sulla condivisione (no_root_squash);
  • fsid definisce un identificatore univoco per la condivisione;

Raggruppare le risorse create

Ora che è terminata la creazione delle risorse, è facile notare come queste abbiano nomi riconducibili al loro utilizzo, ma siano in realtà slegate le une dalle altre. Necessitano quindi, per una corretta gestione, di essere raggruppate. Pacemaker permette di creare quindi gruppi di risorse, la cui definizione sequenziale stabilisce anche l’ordine con cui le risorse verranno avviate:

# crm configure group share-a share-a-fs share-a-exportfs share-a-ip
# crm configure group share-b share-b-fs share-b-exportfs share-b-ip

Sono stati quindi definiti due gruppi share-a e share-b che avvieranno innanzitutto il filesystem, seguito dall’export dello stesso via NFS ed infine l’indirizzo IP. Chiaramente in fase di stop l’ordine sarà logicamente invertito, garantendo in caso di disservizi ai client remoti in prima istanza di perdere connettività con il server (i client non rilevando più l’indirizzo del server tratterranno le connessioni esistenti in stato di attesa, TIME_WAIT) e, una volta che le risorse saranno ripristinate sul nodo attivo, di continuare a scrivere senza alcuna perdita di dati.

Collocazione, ordinamento e controllo connettività delle risorse

La configurazione del cluster non è però ancora completa. Le risorse drbd ed i gruppi share al momento non sono correlati. Infatti per come è stato configurato il sistema su un nodo potrebbe avviarsi share-a, ma non esserci la risorsa drbd0 promossa allo stato di master, quindi non utilizzabile come mount point, né scrivibile.
Per ovviare a questa potenziale situazione, è necessario associare i gruppi share ai nodi su cui la risorsa drbd è master.
Pacemaker gestisce queste operazioni attraverso le colocation:

# crm configure colocation share-a_on_ms-drbd0 inf: share-a ms-drbd0:Master
# crm configure colocation share-b_on_ms-drbd1 inf: share-b ms-drbd1:Master

Viene definita per entrambi i gruppi share una colocation di peso infinito (inf, quindi senza possibilità di variazione) per cui il gruppo si avvierà solo laddove la risorsa multi-state è in stato master.

E’ necessaria un’altra condizione per aggiungere coerenza alla configurazione e riguarda l’ordinamento nell’avvio delle risorse. Chiaramente ciascun gruppo share non ha ragione di avviarsi se non dopo che il dispositivo drbd è attivo. Pacemaker gestisce la cosa attraverso le definizioni order:

# crm configure order share-a_after_ms-drbd0 inf: ms-drbd0:promote share-a:start
# crm configure order share-a_after_ms-drbd1 inf: ms-drbd1:promote share-b:start

Viene definita una order di peso infinito in cui l’operazione di promozione a master della risorsa multi-state vincola l’avvio del gruppo share.

A completamento della configurazione non rimane che associare le risorse ai nodi con connettività, facendo riferimento alla risorsa ping dichiarata in precedenza e clonata in tutti i nodi:

# crm configure location share-a_on_connected_node share-a rule -inf: not_defined ping or ping lte 0
# crm configure location share-b_on_connected_node share-b rule -inf: not_defined ping or ping lte 0

Viene definita una location relativa ai gruppi share la cui regola di peso meno infinito (-inf) si verifica in assenza di connettività: la risorsa ping non è definita oppure restituisce un valore negativo.
In questo modo quando un nodo che ospita un gruppo perderà la connettività Pacemaker tenterà di effettuare uno switch del gruppo su un altro nodo attivo.

Cleanup finale

Ora che la configurazione del cluster è completa è facile immaginare come tutte le operazioni effettuate possano aver falsato lo stato generale del cluster, quindi, al fine di partire da una situazione pura, è possibile lanciare i seguenti comandi:

# crm resource cleanup share-a
# crm resource cleanup share-b

L’operazione di cleanup della risorsa come dice il nome, azzera il conteggio degli errori per le risorse e le riavvia ordinatamente in modo da, come detto, partire da zero a vagliare eventuali problemi.
Lo stato del cluster a configurazione ultimata dovrebbe essere quindi il seguente:

# crm status
============
Last updated: Fri Sep 17 14:33:16 2010
Stack: Heartbeat
Current DC: debian-lenny-nodo1 (1627f3ec-ec7e-4f0c-b69e-e9729933a2cc) - partition with quorum
Version: 1.0.8-042548a451fce8400660f6031f4da6f0223dd5dd
2 Nodes configured, unknown expected votes
5 Resources configured.
============

Online: [ debian-lenny-nodo1 debian-lenny-nodo2 ]

 Clone Set: ping_clone
     Started: [ debian-lenny-nodo1 debian-lenny-nodo2 ]
 Master/Slave Set: ms-drbd0
     Masters: [ debian-lenny-nodo2 ]
     Slaves: [ debian-lenny-nodo1 ]
 Master/Slave Set: ms-drbd1
     Masters: [ debian-lenny-nodo1 ]
     Slaves: [ debian-lenny-nodo2 ]
 Resource Group: share-a
     share-a-fs (ocf::heartbeat:Filesystem):    Started debian-lenny-nodo1
     share-a-exportfs   (ocf::heartbeat:exportfs):      Started debian-lenny-nodo1
     share-a-ip (ocf::heartbeat:IPaddr2):       Started debian-lenny-nodo1
 Resource Group: share-b
     share-b-fs (ocf::heartbeat:Filesystem):    Started debian-lenny-nodo2
     share-b-exportfs   (ocf::heartbeat:exportfs):      Started debian-lenny-nodo2
     share-b-ip (ocf::heartbeat:IPaddr2):       Started debian-lenny-nodo2

Creare una configurazione active active

Il progetto iniziale prevede che nessun nodo, in condizioni normali, sia passivo. Pertanto per completare la configurazione del cluster è necessario assegnare ciascun gruppo share ad uno specifico nodo.
Tale risultato è ottenibile mediante l’aggiunta di due ulteriori regole di tipo location:

location cli-prefer-share-a share-a rule inf: #uname eq debian-lenny-nodo1
location cli-prefer-share-b share-b rule inf: #uname eq debian-lenny-nodo2

In questo modo, la regola creata impone che share-a venga associata al nodo debian-lenny-nodo1 mentre share-b al nodo debian-lenny-nodo2: i due nodi saranno entrambi operativi, completando così l’obiettivo deciso in fase di progettazione.

Considerazioni sulla stickiness

La politica del cluster in merito al posizionamento di una risorsa dipende dal peso che questa ha. Il peso viene stabilito in base alle condizioni del cluster e della risorsa stessa. Molte delle regole relative a definizioni di location e order fanno infatti riferimento a pesi infiniti in modo che se queste si verificano lo stato del cluster DEVE variare.
Lo stickiness di una risorsa rappresenta il peso necessario al suo spostamento dalla posizione attuale nel momento in cui il cluster subisce variazioni.

Ad esempio…

Il valore di default della stickyness è 0. se una risorsa viene migrata a mano, il cluster aggiunge una regola di location come quelle descritte poco sopra. Se il nodo che eroga la risorsa perde di connettività, allora questa, per la sua sopravvivenza, verrà migrata sul nodo attivo. Quando però il nodo originale tornerà a vivere, la risorsa, per via della regola impostata, verrà nuovamente migrata in quanto non ha vincoli (la stickiness è 0) che la legano al nodo attuale. Questo significa che nel giro i poco tempo si assisterà a due switch della risorsa, assimilabili a due interruzioni di servizio.
Facile capire come non sempre questo possa essere il comportamento desiderato. Basti pensare al caso di database Oracle, ed a tutti i problemi che comporta uno switch.
Inutile dire che meno sono, meglio è.
Fortunatamente Pacemaker consente di impostare il valore di default di stickiness a infinito:

crm configure property default-resource-stickiness=INFINITY

In questo modo se una risorsa associata ad un nodo migra, alla ricomparsa del nodo questa rimarrà dov’è, limitando ulteriori disservizi.
A seconda della decisione presa in merito alla stickiness, si dovrà quindi scegliere se questa dovrà rimanere al suo valore di default o avere altri valori (numeri maggiori di zero per arrivare ad INFINITY).

Configurazione finale

La configurazione estesa (modificabile attraverso il comando crm configure edit) è la seguente:

node $id="1627f3ec-ec7e-4f0c-b69e-e9729933a2cc" debian-lenny-nodo1
node $id="f4380aee-c67b-4472-9449-5fa8e5ddae34" debian-lenny-nodo2
primitive drbd0 ocf:linbit:drbd \
	params drbd_resource="r0" \
	op monitor interval="20s" timeout="40s" \
	op start interval="0" timeout="240s" \
	op stop interval="0" timeout="100s"
primitive drbd1 ocf:linbit:drbd \
	params drbd_resource="r1" \
	op monitor interval="20s" timeout="40s" \
	op start interval="0" timeout="240s" \
	op stop interval="0" timeout="100s"
primitive ping ocf:pacemaker:ping \
	params host_list="192.168.1.1" name="ping" \
	op monitor interval="10s" timeout="60s" \
	op start interval="0" timeout="60s" \
	op stop interval="0" timeout="60s"
primitive share-a-exportfs ocf:heartbeat:exportfs \
	params directory="/share-a" clientspec="192.168.1.0/24" options="rw,async,no_subtree_check,no_root_squash" fsid="1" \
	op monitor interval="10s" timeout="30s" \
	op start interval="0" timeout="40s" \
	op stop interval="0" timeout="40s"
primitive share-a-fs ocf:heartbeat:Filesystem \
	params device="/dev/drbd0" directory="/share-a" fstype="ext3" \
	op monitor interval="20s" timeout="40s" \
	op start interval="0" timeout="60s" \
	op stop interval="0" timeout="60s" \
	meta is-managed="true"
primitive share-a-ip ocf:heartbeat:IPaddr2 \
	params ip="192.168.1.33" nic="eth0" \
	op monitor interval="20s" timeout="40s"
primitive share-b-exportfs ocf:heartbeat:exportfs \
	params directory="/share-b" clientspec="192.168.1.0/24" options="rw,async,no_subtree_check,no_root_squash" fsid="2" \
	op monitor interval="10s" timeout="30s" \
	op start interval="0" timeout="40s" \
	op stop interval="0" timeout="40s"
primitive share-b-fs ocf:heartbeat:Filesystem \
	params device="/dev/drbd1" directory="/share-b" fstype="ext3" \
	op monitor interval="20s" timeout="40s" \
	op start interval="0" timeout="60s" \
	op stop interval="0" timeout="60s" \
	meta is-managed="true"
primitive share-b-ip ocf:heartbeat:IPaddr2 \
	params ip="192.168.1.34" nic="eth0" \
	op monitor interval="20s" timeout="40s"
group share-a share-a-fs share-a-exportfs share-a-ip
group share-b share-b-fs share-b-exportfs share-b-ip
ms ms-drbd0 drbd0 \
	meta master-max="1" notify="true"
ms ms-drbd1 drbd1 \
	meta master-max="1" notify="true"
clone ping_clone ping \
	meta globally-unique="false"
location cli-prefer-share-a share-a \
	rule $id="cli-prefer-rule-share-a" inf: #uname eq debian-lenny-nodo1
location cli-prefer-share-b share-b \
	rule $id="cli-prefer-share-b-rule" inf: #uname eq debian-lenny-nodo2
location share-a_on_connected_node share-a \
	rule $id="share-a_on_connected_node-rule" -inf: not_defined ping or ping lte 0
location share-b_on_connected_node share-b \
	rule $id="share-b_on_connected_node-rule" -inf: not_defined ping or ping lte 0
colocation share-a_on_ms-drbd0 inf: share-a ms-drbd0:Master
colocation share-b_on_ms-drbd1 inf: share-b ms-drbd1:Master
order share-a_after_ms-drbd0 inf: ms-drbd0:promote share-a:start
order share-a_after_ms-drbd1 inf: ms-drbd1:promote share-b:start
property $id="cib-bootstrap-options" \
	dc-version="1.0.8-042548a451fce8400660f6031f4da6f0223dd5dd" \
	cluster-infrastructure="Heartbeat" \
	stonith-enabled="false" \
	no-quorum-policy="ignore" \
	last-lrm-refresh="1284661245"

Test funzionale

Una volta montato da parte di un client lo share NFS:

# mount -t nfs 192.168.1.33:/share-a /mnt/

è possibile lanciare sempre sul client una copia arbitraria di file:

cp -av /usr/ /mnt/

partirà la copia dei file e sarà possibile agire quindi sul cluster, tenendo monitorato sia la copia che lo stato attraverso crm_mon.

Primo test: disconnessione cavo LAN nodo master

Alla disconnessione del cavo la risorsa ping avverte immediatamente l’assenza di connettività e forza la migrazione del gruppo share-a al nodo rimanente, in questo caso il debian-lenny-nodo1. Il tutto è osservabile dal file /var/log/syslog:

Sep 17 18:05:35 debian-lenny-nodo2 heartbeat: [1079]: info: Link debian-lenny-nodo1:eth0 dead.
...
Sep 17 18:05:46 debian-lenny-nodo2 kernel: [20384.996634] block drbd1: peer( Primary -> Secondary ) 
...
Sep 17 18:05:50 debian-lenny-nodo2 Filesystem[27581]: [27636]: INFO: Running start for /dev/drbd0 on /share-a
...
Sep 17 18:05:50 debian-lenny-nodo2 kernel: [20389.636665] EXT3-fs: mounted filesystem with ordered data mode.
...
Sep 17 18:05:51 debian-lenny-nodo2 exportfs[27676]: [27739]: INFO: File system exported
...
Sep 17 18:05:52 debian-lenny-nodo2 IPaddr2[27764]: [27817]: INFO: ip link set eth0 up
...

Lato client, la scrittura su NFS si ferma per qualche secondo e riparte appena l’ultima operazione di start è eseguita da Pacemaker.

Secondo test: migrazione manuale delle risorse

Lanciando il comando migrate sulla risorsa share-a viene forzato manualmente lo switch della risorsa dal nodo attuale al nodo indicato:

crm resource migrate share-a debian-lenny-nodo1

Quanto il cluster fa in questo genere di operazione è quello di modificare la stessa location descritta poco sopra:

# crm configure show
...
...
location cli-prefer-share-a share-a \
	rule $id="cli-prefer-rule-share-a" -inf: #uname eq debian-lenny-nodo1
...
...

In sostanza la location prima impostata su share-a è stata modificata da Pacemaker da inf a -inf invertendo il vincolo che legava la risorsa al nodo debian-lenny-nodo1.
E’ necessario porre particolare attenzione a questa regola, poiché il peso -inf implica che la risorsa sarà da qui in poi SEMPRE associata al nodo debian-lenny-nodo2, poiché non potrà più risiedere sul nodo originale.

Per ripristinare la situazione originaria e rimuovere la regola aggiunta esistono due modi:

  1. Lanciare il comando precedente utilizzando unmigrate, in modo che la risorsa torni sul nodo originale (e la regola verrà cancellata, eliminando anche l’impostazione della configurazione originale);
  2. Modificare a mano la configurazione del cluster attraverso il comando crm configure edit rimuovendo la linea aggiunta automaticamente, in modo da lasciare la risorsa dove si trova ora senza vincolarne i futuri spostamenti;

Lato client l’operazione è ancora più indolore di prima: dai log il comportamento è simile al precedente test e la scrittura sullo share NFS si ferma il tempo che la risorsa migra.

Terzo test: spegnimento improvviso nodo master

Lo spegnimento improvviso del nodo master rende il comportamento del cluster del tutto simile a quello illustrato primo test, con una differenza sostanziale:

...
Sep 17 18:36:37 debian-lenny-nodo2 heartbeat: [1079]: WARN: node debian-lenny-nodo1: is dead
...
Sep 17 18:36:37 debian-lenny-nodo2 crmd: [1120]: WARN: check_dead_member: Our DC node (debian-lenny-nodo1) left the cluster
...
Sep 17 18:36:37 debian-lenny-nodo2 kernel: [22236.912107] block drbd0: peer( Primary -> Unkno
wn ) conn( Connected -> NetworkFailure ) pdsk( UpToDate -> DUnknown ) 
...

Il cluster ha dovuto rimpiazzare il proprio DC (Designated Coordinator) poiché spento ed ovviamente il dispositivo drbd è in stato Primary sul nodo sopravvissuto, ma ovviamente in stato Unknown nella sua replica.

Anche in questo caso, lato client tutto si è svolto in maniera trasparente, la copia si è fermata per qualche secondo per poi ripartire.

Conclusioni

La soluzione illustrata descrive a pieno le potenzialità del progetto Pacemaker. E’ possibile combinare i suggerimenti espressi in questo lungo articolo immaginando di disporre di un cluster composto da più di due macchine, o segmentando ulteriormente le share esposte, creando ulteriori volumi logici e dispositivi drbd.
Il tutto con dei costi decisamente contenuti rispetto alle soluzioni NAS in commercio: in pratica il solo prezzo dell’hardware!
Bene, con quest’ultima parte la serie “Evoluzione dell’alta affidabilità su Linux” può dirsi conclusa. Sicuramente “Mia Mamma Usa Linux” tornerà su temi inerenti l’argomento, magari trattando l’integrazione delle tecniche di virtualizzazione nel cluster, utilizzando virtual machines come fossero risorse da migrare, magari senza necessità che i sistemi virtualizzati vengano spenti!
Commenti sulla serie sono ben accetti, suggerimenti su futuri argomenti anche di più, proposte di collaborazione sono in assoluto il meglio.
Lunga vita al software libero.

Da sempre appassionato del mondo open-source e di Linux nel 2009 ho fondato il portale Mia Mamma Usa Linux! per condividere articoli, notizie ed in generale tutto quello che riguarda il mondo del pinguino, con particolare attenzione alle tematiche di interoperabilità, HA e cloud.
E, sì, mia mamma usa Linux dal 2009.

8 risposte a “Evoluzione dell’alta affidabilità su Linux: realizzare un NAS con Pacemaker, DRBD ed exportfs”

  1. Avatar Mauro
    Mauro

    Ciao Raoul, ottimo lavoro!!! Continua/ate cosi’… 😉

  2. Avatar Raoul Scarazzini

    Grande Mauro, noi continuiamo così, tu ricorda che sei sempre il benvenuto!

  3. Avatar Paolo Mancuso
    Paolo Mancuso

    Complimenti veramente, per le brillanti spiegazioni….a me sta venendo voglia di clasterizzare. Se volessi farlo con un db tipo Oracle 10g cosa si dovrebbe fare?

  4. Avatar Raoul Scarazzini

    Ciao Paolo e grazie per i complimenti. Ci sono parecchi modi per clusterizzare Oracle, dipendenti dalle esigenze, dalla disponibilità economica e dalla tipologia di impiego.
    Considera che Oracle distribuisce una sua Linux personalizzata, che integra tecnologie cluster personalizzate, ma esiste la possibilità di creare un cluster Oracle/Pacemaker, esiste un resource agent apposito: http://linux-ha.org/doc/re-ra-oracle.html.

    A presto!

  5. Avatar Diego Raschi
    Diego Raschi

    Bellissimo articolo e ben descritto!
    In questi giorni stò provando varie soluzioni con Pacemaker e corosync. Mi rimane un dubbio.. con questo progetto si riescono a “clusterizzare” tutte le applicazioni senza perdite di dati? anche applicazioni proprietarie?

  6. Avatar Raoul Scarazzini

    Ciao Diego e grazie per i complimenti, se la questione Cluster e Pacemaker ti interessa, ti invito sin da ora al seminario gratuito che si terrà a fine Giugno (a breve tutti i dettagli sul portale). Sarà una giornata in cui affronteremo tutti i temi “caldi” nell’ambito cluster su Linux.

    Per quanto riguarda le applicazioni “proprietarie”: se un software è gestito da script di avvio (LSB o OCF che siano), allora è possibile clusterizzarlo. Il caso più frequente è Oracle, db proprietario che però è assolutamente clusterizzabile.

    A presto!

  7. Avatar leopoldo
    leopoldo

    salve, sto provando il vostro laboratorio,
    alcune cose fuzionano altre al rigor di logica elementare no:

    gli ip 192.168.1.1-33-34, il crm non crea la scheda
    bcast eth0:0 ecc…

    da lì continuo ad avere col ptest errori tipo

     ERROR: create_notification_boundaries: Creating boundaries for ms-drbd1
    ERROR: create_notification_boundaries: Creating boundaries for ms-drbd0
    

    anche se lo status

    2 Nodes configured, unknown expected votes
    5 Resources configured.
    ============
    
    Online: [ server01 server02 ]
    
     Master/Slave Set: ms-drbd0
         Masters: [ server01 ]
         Slaves: [ server02 ]
     Master/Slave Set: ms-drbd1
         Masters: [ server01 ]
         Slaves: [ server02 ]
     Clone Set: ping_clone
         Started: [ server01 server02 ]
    

    e il resource show evidenzia le risorse fermate

      share-a-fs	(ocf::heartbeat:Filesystem) Stopped 
         share-a-exportfs	(ocf::heartbeat:exportfs) Stopped 
         share-a-ip	(ocf::heartbeat:IPaddr2) Stopped 
     Resource Group: share-b
         share-b-fs	(ocf::heartbeat:Filesystem) Stopped 
         share-b-exportfs	(ocf::heartbeat:exportfs) Stopped 
         share-b-ip	(ocf::heartbeat:IPaddr2) Stopped 
     Master/Slave Set: ms-drbd0
         Masters: [ server01 ]
         Slaves: [ server02 ]
     Master/Slave Set: ms-drbd1
         Masters: [ server01 ]
         Slaves: [ server02 ]
     Clone Set: ping_clone
         Started: [ server01 server02 ]
    
  8. Avatar Raoul Scarazzini

    Ciao Leopoldo,
    per quanto riguarda gli IP se la risorsa si avvia senza errori allora gli IP puoi vederli mediante il comando:

    # ip addr
    

    Gli errori relativi alle risorse ms-drbd0 ed ms-drbd1 potrebbero essere dei residui di prove precedenti, prova a lanciare un

    # crm resource cleanup ms-drbd0
    

    e vedere se gli errori spariscono.
    Per tutto il resto un riferimento sul perché le cose non funzionano si trova all’interno dei log, che variano a seconda del sistema operativo che stai usando /var/log/syslog per Debian/Ubuntu e /var/log/messages per RedHat/SuSe. Prova a lanciare dei cleanup seguendo nei log quel che accade ed avrai un riscontro sugli errori che si verificano.

    A presto!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *