Esperienza su High Availability (HA) con heartbeat, DRBD e mon |
|
|
|
Per la realizzazione di questo modulo useremo
Netkit4TIC
con la
connettività
con la rete reale (leggere il file README).
In questo modulo sperimenteremo un
High availability cluster
(HA Cluster)
costituito da un
two-node cluster.
Un cluster HA ha il compito di erogare i servizi limitando al
minimo i danni provocati dal blocco di un nodo. Lo scopo infatti
è quello di eliminare i punti deboli del sistema chiamati
"Single Points of Failures" (SPoF).
Per questa esercitazione ci siamo ispirati alla tesi di laurea
"Un Cluster in Alta Disponibilità in Ambiente Linux"
di Sabino Calò.
Tra le due tipologie Active-Passive e
Active-Active
abbiamo
scelto la seconda in modo da ottenere maggiori prestazioni
in mancanza di guasti a scapito di peggiori prestazioni
nel caso opposto. I due nodi li abbiamo chiamati left
e right con attivi due servizi indipendenti:
su left è attivato il servizio http (apache) e su
right è attivato il servizio NFS. In caso di guasto
il nodo superstite offrirà entrambe i servizi.
Il motivo guida è quello di evitare la presenza di SPoF e quindi sono stati considerati i seguenti casi:
ifenslave e dal modulo del kernel
bonding.o.drbd0.7-utils e da
drbd0.7-module-source (compilato diventa un
modulo del kernel).Gli strumenti per realizzare HA sono implementati da:
heartbeat è un sottosistema base
per il controllo di Linux-HA. È in grado di
eseguire script al momento della inizializzazione e
quando i nodi vengono accesi o spenti. La versione che
utilizziamo è in grado di operare anche il
cambio di IP address. È preconfigurato per 2-nodi
ed è estensibile ad un numero superiore.mon è uno strumento per monitorare
la disponibilità di servizi. Quando un servizio
diventa non più disponibile mon può
comunicarlo utilizzando syslog, email o SNMP traps.
Schema |
|
La rete descritta nella mappa (pdf, xml) è costituita principalmente da due nodi collegati in modalità cluster High Availability (HA). Ogni nodo ha due schede di rete configurate in modalità bonding e connesse all'altro nodo. Una terza scheda di rete rende disponibile la connettività dalla quale vengono erogati i servizi. Un terzo e ultimo nodo rappresenta il nodo client generico da dove possono essere provati i servizi o dal quale si amministra il cluster.
Il bonding delle interfacce eth0 ed eth1 permette la tollerabilità ai guasti della comunicazione "interna" tra i due nodi. In particolare scegliendo l'algoritmo di distribuzione di tipo round-robin il carico, in assenza di guasti, viene equamente suddiviso tra le interfacce slave.
La configurazione di DRBD è stata pensata per offrire
nei nodi due servizi totalmente indipendenti. Quindi ogni
nodo ha la copia dei dati dell'altro nodo.
Il nodo left con apache ha un primo blocco drbd in modalità
primary e un secondo blocco drbd in modalità slave.
La radice del server apache utilizzerà il primo blocco.
Viceversa il nodo right con NFS ha un primo blocco drbd
in modalità slave e un secondo blocco drbd in modalità primary.
La radice del server NFS utilizzerà il secondo blocco.
Il primo blocco del nodo left è in corrispondenza con il
primo blocco del nodo right e
il secondo blocco del nodo left è in corrispondenza con il
secondo blocco del nodo right.
Per come viene configurato DRBD ne segue che ogni nodo del cluster avrà delle copie esatte del contenuto degli altri nodi e quindi in presenza di un guasto può dare, con prestazioni degradate, l'intero insieme di servizi erogati in origine dal cluster.
In seguito gli argomenti vengono esposti suddivisi nelle seguenti sezioni:
./lab start) dal
tarball.
Prerequisiti |
|
Per lo svolgimento di questa esercitazione serve una macchina con almeno 1 GB di RAM o in alternativa una macchina anche con 256 MB di RAM ma con un partizione temporanea di disco con almeno 581 MB liberi e ovviamente con permessi di scrittura.
Nel secondo caso occorre impostare la variabile d'ambiente:
export BACK_ST=/mnt/hdaX
Preparazione dell'ambiente bonding |
|
Per la configurazione della modalità "channel-bonding"
occorre caricare il modulo bonding, specificando al driver di
utilizzare tutte le schede slave in modalità round-robin
(mode=0) e specificando di attivare la modalità di monitoraggio
sulle schede (miimon=100) espressa in millisecondi.
In seguito si configura l'interfaccia bond0 appena definita e poi
si associano tutte le schede (slave) che si vogliono far partecipare
al bonding (eth0 e eth1):
left# modprobe bonding mode=0 miimon=100 && \
ifconfig bond0 192.168.0.1 up && \
ifenslave bond0 eth0 eth1
Il pacchetto ifenslave è un "empty package" che si
traduce in ifenslave-2.4 o in
ifenslave-2.6 in dipendenza del kernel utilizzato.
Nel caso della configurazione di un nodo reale
per il caricamento di un modulo al boot consigliamo l'utilizzo di
modconf (eventualmente apt-get install modconf)
che permette tramite un comodo menu di selezionare il modulo desiderato e
di specificare gli eventuali parametri.
Nota bene che i comandi lsmod|grep bonding
visualizzano un "Used by" pari a 0 ma se noi rimoviamo il modulo con
modprobe -r bonding viene rilasciato bond0 e con esso
tutti i suoi slaves.
Per controllare il corretto funzionamento:
left# cat /proc/net/bonding/bond0 Ethernet Channel Bonding Driver: v2.6.1 (October 29, 2004) Bonding Mode: load balancing (round-robin) MII Status: up MII Polling Interval (ms): 100 Up Delay (ms): 0 Down Delay (ms): 0 Slave Interface: eth0 MII Status: up Link Failure Count: 0 Slave Interface: eth1 MII Status: up Link Failure Count: 0
Preparazione dell'ambiente DRBD |
|
Per la configurazione di DRBD occorre per prima cosa generare il
modulo drbd.o fondamentale per la gestione dal lato kernel. Per il
lato user space occorre aver installato drbd0.7-utils.
In seguito passiamo a configurare il file /etc/drbd.conf,
uguale per entrambe i nodi,
secondo le nostre necessità che sono:
# /etc/drbd.conf
# primo device
resource drbd0 {
protocol C;
startup {
# Wait for connection timeout.
# Default is 0, which means unlimited. Unit is seconds.
#
wfc-timeout 1;
}
net {
}
syncer {
rate 800K;
group 1;
}
on left {
device /dev/drbd/0;
disk /dev/ubd/1;
address 192.168.0.1:7788;
meta-disk internal;
}
on right {
device /dev/drbd/0;
disk /dev/ubd/1;
address 192.168.0.2:7788;
meta-disk internal;
}
}
######################################################
# secondo device
resource drbd1 {
protocol C;
startup {
# Wait for connection timeout.
# Default is 0, which means unlimited. Unit is seconds.
#
wfc-timeout 1;
}
net {
}
syncer {
rate 800K;
group 2;
}
on left {
device /dev/drbd/1;
disk /dev/ubd/2;
address 192.168.0.1:7789;
meta-disk internal;
}
on right {
device /dev/drbd/1;
disk /dev/ubd/2;
address 192.168.0.2:7789;
meta-disk internal;
}
}
Nella precedente configurazione abbiamo specificato che i metadata
vengono memorizzati all'interno dello stesso blocco. Lo spazio
occupato dai metadata è 128MB indipendentemente
dalla dimensione del device che può essere al più 4TB.
Questo limite ci costringe quindi a fare delle prove su device
di dimensione > 128MB.
Moltiplicando 128MB+nfsMB per due (blocchi) e il risultato per due (nodi)
deriva l'alto consumo di spazio disco di questa esperienza (circa 700MB).
La definizione del gruppo serve nella fase di risincronizzazione
a definire una sequenza di priorità. Nel caso definissimo
un unico gruppo allora tutte le risorse associate verrebbero
sincronizzare simultaneamente e questo è utile quando
le risorse sono su dischi separati.
Per default il numero di risorse definibili sono due e se dovessimo averne in numero superiore occorre passarne il numero al modulo al momento del caricamento. Il nome del parametro è minor_count=#.
Nel nodo left inizializziamo, una tantum, drbd
e costruiamo il filesystem XFS nel primo blocco:
left# modprobe drbd && \
drbdadm up all && \
drbdadm -- --do-what-I-say primary drbd0 && \
mkfs.xfs -q /dev/drbd/0
Operiamo le stesse operazioni nel nodo right questa
volta sul secondo blocco:
right# modprobe drbd && \
drbdadm up all && \
drbdadm -- --do-what-I-say primary drbd1 && \
mkfs.xfs -q /dev/drbd/1
In ogni nodo occorre dare disposizioni sul montaggio di questi blocchi:
# /etc/fstab [...] /dev/drbd/0 /var/www xfs noatime,noauto 0 0 /dev/drbd/1 /nfs xfs noatime,noauto 0 0
La sequenza di startup dell'esperimento virtuale costruisce prima
il nodo left, con l'inizializzazione del bonding e del
drbd che non riuscendo a contattare il nodo right
forza Primary il blocco 0 e lo formatta xfs in modalità standalone
(Wait For Connection [WFConnection]) e
marca Secondary il secondo blocco.
left# cat /proc/drbd version: 0.7.10 (api:77/proto:74) SVN Revision: 1743 build by doros@picinin.dorogroup.com, 2005-08-01 06:57:56 0: cs:WFConnection st:Primary/Unknown ld:Consistent ns:0 nr:0 dw:5250 dr:151 al:4 bm:2 lo:0 pe:0 ua:0 ap:0 1: cs:WFConnection st:Secondary/Unknown ld:Inconsistent ns:0 nr:0 dw:0 dr:0 al:0 bm:2 lo:0 pe:0 ua:0 ap:0
Alla partenza il nodo right forza
Primary il blocco 1 e lo formatta xfs in questo caso però
riesce a contattare il nodo left e quindi comanda
il mirroring che viene fatto sia dal blocco 1 (right) verso il
blocco 1 (left) che dal blocco 0 (left) verso il blocco 0 (right)
rimasto pendente da prima.
left# cat /proc/drbd version: 0.7.10 (api:77/proto:74) SVN Revision: 1743 build by doros@picinin.dorogroup.com, 2005-08-01 06:57:56 0: cs:SyncSource st:Primary/Secondary ld:Consistent ns:1700 nr:0 dw:5250 dr:1851 al:4 bm:2 lo:0 pe:4 ua:0 ap:0 [=======> sync'ed: 40.0% (15724/17408)K finish: 0:00:47 speed: 280 (280) K/sec 1: cs:SyncTarget st:Secondary/Primary ld:Inconsistent ns:0 nr:1200 dw:1200 dr:0 al:0 bm:2 lo:9 pe:20 ua:9 ap:0 [=======> sync'ed: 40.0% (16336/17408)K finish: 0:00:54 speed: 268 (268) K/sec
In seguito terminate le operazioni di allineamento (sync) la situazione su entrambe i nodi sarà stabilizzata:
left# cat /proc/drbd version: 0.7.10 (api:77/proto:74) SVN Revision: 1743 build by doros@picinin.dorogroup.com, 2005-08-01 06:57:56 0: cs:Connected st:Primary/Secondary ld:Consistent ns:17424 nr:0 dw:5250 dr:17575 al:4 bm:4 lo:0 pe:0 ua:0 ap:0 1: cs:Connected st:Secondary/Primary ld:Consistent ns:0 nr:19440 dw:19440 dr:0 al:0 bm:4 lo:0 pe:0 ua:0 ap:0 right# cat /proc/drbd version: 0.7.10 (api:77/proto:74) SVN Revision: 1743 build by doros@picinin.dorogroup.com, 2005-08-01 06:57:56 0: cs:Connected st:Secondary/Primary ld:Consistent ns:0 nr:17412 dw:17412 dr:0 al:0 bm:4 lo:0 pe:0 ua:0 ap:0 1: cs:Connected st:Primary/Secondary ld:Consistent ns:19440 nr:0 dw:4964 dr:17600 al:4 bm:4 lo:0 pe:0 ua:0 ap:0
Preparazione ambiente pconsole |
|
Pconsole stà per parallel console. Questo pacchetto
permette di metterci in contatto simultaneamente con ogni nodo di un
cluster utilizzando SSH.
Attraverso una shell specializzata si possono impartire
comandi che vengono spediti a tutte le connessioni aperte. Può
essere usata anche in modalità non grafica. La sua installazione
deve essere fatta solo sul nodo da cui si intende operare (nel nostro
caso il nodo client). Nei nodi del cluster occorre invece
attivare il servizio SSH e per una migliore usabilità
abilitiamo l'autorizzazione attraverso chiavi RSA.
Per prima cosa costruiamo una chiave RSA attraverso la quale impostare una connessione ssh basata su "RSA based authentication".
client# ssh-keygen -t dsa -b 1024 -N ''
e aggiungiamo la chiave publica appena generata nei rispettivi
file /root/.ssh/authorized_keys2 di ogni nodo del cluster.
Prima di lanciare il comando impostiamo due variabili d'ambiente che servono per impostare il nome del comando per la connessione (ssh), le dimensioni delle windows (geometry):
client# export P_CONNECT_CMD=ssh && \
export P_TERM_OPTIONS="-geometry 140x10 +sb"
Preparazione ambiente heartbeat |
|
Per configurare Heartbeat occorrono tre file tutti residenti
sulla directory /etc/ha.d. Essi sono:
ha.cf: il file principale di configurazioneharesources: per la definizione delle risorseauthkeys: per la specifica dell'autenticazione# /etc/ha.d/ha.cf [...] # What UDP port to use for communication? udpport 694 # What interfaces to heartbeat over? bcast bond0 # auto_failback on # Tell what machines are in the cluster node left right
Il file haresources contiene la lista di risorse che vengono
spostate da un nodo all'altro in dipendenza dello stato
dei nodi stessi. La prima risorsa consiste dell'indirizzo
IP 193.206.185.10, del blocco DRBD numero 0 montato in
/var/www con filesystem xfs. Su tale risorsa viene
attivato il servizio apache e abilitato il watch cluster-http.
La seconda risorsa consiste dell'indirizzo IP 193.206.185.11,
del blocco DRBD numero 1 montato in /mnt/nfs con filesystem xfs.
Su tale risorsa vengono attivati i servizi
NFS (nfs-common nfs-user-server) e abilitato il watch cluster-nfs:
# /etc/ha.d/haresources left 193.206.185.10 \ drbddisk::drbd0 \ Filesystem::/dev/drbd/0::/var/www::xfs \ drbdlinks-http apache mon-cluster-http right 193.206.185.11 \ drbddisk::drbd1 \ Filesystem::/dev/drbd/1::/nfs::xfs \ drbdlinks-nfs nfs-common nfs-user-server mon-cluster-nfs
Gli script mon-cluster-*, che verranno descritti in
seguito assieme all'ambiente di mon, sono delle
attivazioni di attività di monitoraggio dei relativi
servizi http e nfs.
Nel file authkeys viene specificato il metodo di autenticazione tra i nodi del cluster. Nel nostro caso avendo dei canali dedicati (crossover cable) abbiamo scelto di non cifrare la comunicazione:
# /etc/ha.d/authkeys auth 1 1 crc
Preparazione ambiente mon |
|
La strategia che vogliamo adottare per il monitoraggio di questa configurazione di cluster active-active deve funzionare ovviamente anche nel caso di failover cioè quando un unico nodo eroga tutti i servizi.
Per questo motivo abbiamo pensato di attivare il servizio mon
al boot di ogni nodo con tutti gli swatch disattivati attraverso
l'uso dello switch -l e
utilizzando il file di stato
/var/state/mon/disabled inizialmente riempito
con l'elenco di tutti gli swatch:
disable watch cluster-nfs disable watch cluster-http
L'idea è quindi quella di utilizzare heartbeat in congiunzione
con moncmd in modo che quando heartbeat attiva un servizio
contemporaneamente abilita il corrisponden watch.
Quando parte un servizio
viene abilitato il watch relativo attraverso il comando
moncmd contenuto negli script mon-cluster-*.
Ad esempio il file mon-cluster-http ha questo contenuto:
#! /bin/sh
# /etc/ha.d/resource.d/mon-cluster-http
case "$1" in
start)
echo -n "Enabling cluster-http"
cat <<EOF | moncmd -l mon -a
PASS=mon
enable watch cluster-http
EOF
echo "."
;;
stop)
echo -n "Disabling cluster-http"
cat <<EOF | moncmd -l mon -a
PASS=mon
disable watch cluster-http
EOF
echo "."
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0
Fortunatamente tutto si basa su un solo file di configurazione:
# /etc/mon/mon.cf, configuration file for mon
#
# global options
#
maxprocs = 20
histlength = 100
randstart = 60s
#
# authentication types:
# getpwnam standard Unix passwd, NOT for shadow passwords
# shadow Unix shadow passwords (not implemented)
# userfile "mon" user file
#
authtype = userfile
userfile = /etc/mon/uf
#
# NB: hostgroup and watch entries are terminated with a blank line (or
# end of file). Don't forget the blank lines between them or you lose.
#
#
# group definitions (hostnames or IP addresses)
#
hostgroup cluster-http 193.206.185.10
hostgroup cluster-nfs 193.206.185.11
watch cluster-http
service http
description "check http service availability"
interval 1m
monitor http.monitor
period month {Jan-Dec}
alertafter 2
alert file.alert /var/www/alert.txt
alert apacheRestart.alert
service http-takeover
description "check http service availability for takeover"
interval 1m
monitor http.monitor
period month {Jan-Dec}
alertafter 3 10m
numalerts 1
alert file.alert /var/www/alert.txt
alert haStop.alert
watch cluster-nfs
service nfs
description "check NFS service availability"
interval 1m
monitor rpc.monitor -r mountd -r nfs
period month {Jan-Dec}
alertafter 2
alert file.alert /nfs/alert.txt
alert nfsRestart.alert
service nfs-takeover
description "check NFS service availability for takeover"
interval 1m
monitor rpc.monitor -r mountd -r nfs
period month {Jan-Dec}
alertafter 3 10m
numalerts 1
alert file.alert /nfs/alert.txt
alert haStop.alert
Diamo una spiegazione dettagliata solo per watch cluster-http. È composto da due service: il service http ha il compito di verificare il funzionamento del server apache e nel caso di fallimento cerca di farlo ripartire. Il service http-takeover ha il compito di verificare il funzionamento del server apache e nel caso di fallimento ripetuto ferma il servizio heartbeat facendo migrare il servizio http nel nodo superstite.
Il terzo e ultimo file di configurazione
/etc/mon/auth.cf permette di controllare
le autorizzazioni per l'esecuzione di moncmd. Nel nostro
caso abbiamo utilizzato il file /etc/mon/uf per
memorizzare l'elenco di coppie (user, password) che possono
essere autorizzate all'uso di moncmd. Per la
costruzione abbiamo inserito il solo utente mon
con password mon. L'utility per impostare questi
valori è htpasswd.
Preparazione ambiente drbdlinks |
|
Il piccolo pacchetto drbdlinks non è ancora entrato in Debian Sarge ma lo abbiamo utilizzato lo stesso. È una piccola serie di utility che serve per costruire allo startup un link simbolico al blocco configurato e per distruggere alla chiusura il medesimo link.
L'aspetto del file di configurazione per nfs:
# /etc/drbdlinks-nfs.conf
mountpoint('/nfs')
link('/var/lib/nfs/')
Preparazione ambiente apache |
|
Per la configurazione del servizio utilizziamo un file
/etc/apache/httpd.conf standard. Per il test
utilizziamo la pagina speciale index.php:
<php phpinfo() ?>
Il servizio utilizza il primo blocco drbd, è
radicato sul /var/www e assegnato
allo statup al nodo left. Il servizio
non è gestito dallo startup del nodo ma da
heartbeat:
left# update-rc.d -f apache remove right# update-rc.d -f apache remove
inoltre poichè un successivo eventuale fix di sicurezza rigenera la struttura /etc/rc?.d occorre mettere il pacchetto nello stato "hold":
left# echo apache hold | dpkg --set-selections right# echo apache hold | dpkg --set-selections
Preparazione ambiente NFS |
|
Per la configurazione del servizio basta impostare il file
/etc/exports:
/nfs 193.206.185.0/255.255.255.0(rw,no_root_squash)
Il servizio utilizza il secondo blocco drbd, è radicato
in /nfs e assegnato
allo statup al nodo right.
Il servizio
non è gestito dallo startup del nodo ma da
heartbeat:
left# update-rc.d -f nfs-common remove && \
update-rc.d -f nfs-user-server remove
right# update-rc.d -f nfs-common remove && \
update-rc.d -f nfs-user-server remove
inoltre poichè un successivo eventuale fix di sicurezza rigenera la struttura /etc/rc?.d occorre mettere il pacchetto nello stato "hold":
left# echo nfs-common hold | dpkg --set-selections; \
echo nfs-user-server hold | dpkg --set-selections
right# echo nfs-common hold | dpkg --set-selections; \
echo nfs-user-server hold | dpkg --set-selections
Preparazione ambiente NFS lato client |
|
Dal momento che vogliamo testare il funzionamento del servizio
NFS occorre configurare opportunamente il nodo client
per poter montare il filesystem NFS. Per prima cosa
occorre aggiungere un mount point, ad esempio
/mnt/nfs e aggiungere una riga nel file
/etc/fstab che indichi che l'IP 193.206.185.11
(quello fluttuante)
esporta /nfs:
# /etc/fstab 193.206.185.11:/nfs /mnt/nfs nfs noauto 0 0
Quindi per montare la partizione NFS (attualmente i moduli NFS
del kernel non sono "a bordo" della vm e quindi li forniamo
separatamente nel file modules.tgz e li precarichiamo) diamo il
comando:
client# modprobe nfs lockd sunrpc
Sperimentazione interattiva |
|
Per prima vogliamo mostrare le potenzialità di pconsole. Quindi
abilitiamo il nodo client alla visualizzazione di
applicazioni X sul desktop reale:
realHost$ xhost +192.168.77.2
inoltre occorre istruire i client grafici all'interno del
nodo client di dirigere l'output verso il
desktop reale:
client# export DISPLAY=192.168.77.1:0.0
e ora proviamo pconsole in connessione SSH simultanea con i due nodi del cluster:
client# /usr/share/doc/pconsole/examples/pconsole.sh left right
Nello (screenshot) possiamo vedere pconsole in azione utilizzata per visualizzare lo stato di drbd e lo stato dei watch. In seguito possiamo completare la sequenza di boot impartendo in pconsole il comando di start per heartbeat e controllando in seguito il file di log:
pconsole > /etc/init.d/heartbeat start pconsole > tail -f /var/log/ha-log
(screenshot).
Ora possiamo sperimentare la migrazione dei servizi. Andiamo ad
esempio sul nodo right e impartiamo il comando takeover
che implica la migrazione del servizio http dal nodo left al nodo
right:
right# /usr/lib/heartbeat/hb_takeover
(screenshot) e la visualizzazione di drbd e dei watch disabilitati (screenshot).
Per testare il servizio NFS diamo i comandi:
client# mount /mnt/nfs && \
df /mnt/nfs
Filesystem 1K-blocks Used Available Use% Mounted on
193.206.185.11:/nfs 11584 32 11552 1% /mnt/nfs
client# arp -n 193.206.185.11
Address HWtype HWaddress Flags Mask Iface
193.206.185.11 ether FE:FD:C1:CE:B9:02 C eth1
e dopo aver azionato il takeover sul nodo left
rileviamo il cambio di MAC address:
client# df /mnt/nfs Filesystem 1K-blocks Used Available Use% Mounted on 193.206.185.11:/nfs 11584 32 11552 1% /mnt/nfs client# arp -n 193.206.185.11 Address HWtype HWaddress Flags Mask Iface 193.206.185.11 ether FE:FD:C1:CE:B9:01 C eth1
Per testare il servizio apache diamo il comando:
realHost$ mozilla-firefox http://193.206.185.10/index.php
riassunto nello
(screenshot).
In seguito al takeover sul nodo right il refresh sul browser
riassunto nello
(screenshot)
differisce dal precedente per il nome dell'host.
Riferimenti ad altri sistemi di virtualizzazione |
|
|
|
Sandro Doro (email me)