Archive

Posts Tagged ‘HA’

Hochverfügbarkeit mit Linux – Teil 7.1

March 9th, 2011

Teil 7.1 wird eine Ausnahme der Reihe, denn dieser Artikel befasst sich mit der Thematik aus Teil 7, jedoch auf Red Hat/CentOS basierten Systemen.

Die Konfiguration ist hier mehstufig, zunächst wird der “bond” selbst konfiguriert; wichtig ist nur, daß diese Konfiguration vor allen anderen Netzwerk-Optionen steht.
/etc/modprobe.conf:

1
2
alias bond0 bonding
options bond0 mode=X miimon=100

Der Mode wird hier als Zahl definiert: mode=X.

Mögliche Modi für den “bond” sind:

  • balance-rr – ein Round Robin über die NICs, mode=0
  • active-backup – active/standby-Setup, mode=1
  • balance-xor – die MAC-Adresse des Ziels wird gehashed und entsprechend ein Interface für die Kommunikation ausgewählt. Damit ist grundlegend ein Load Balancing auf Netzwerk-Ebene realisiert, genauso wie eine ausreichende Fehlertolleranz, mode=2
  • broadcast – jedes Paket wird auf allen Interfaces gesendet. Damit entsteht 100%ige Fehlertolleranz, mode=3
  • 802.3addynamic link aggregation per IEEE 802.3ad – erfordert jedoch spezielle Switch-Konfiguration, mode=4
  • balance-tlb – der Host entscheidet je nach Last welche NIC genutzt werden soll, mode=5

Das Interface bond0 muß noch konfiguriert werden:
/etc/sysconfig/network-scripts/ifcfg-bond0:

1
2
3
4
5
6
7
DEVICE=bond0
BOOTPROTO=none
ONBOOT=yes
NETWORK=198.18.0.0
NETMASK=255.255.255.0
IPADDR=198.18.0.1
USERCTL=no

Jede reelle NIC die dem “bond” angehören soll, muß noch entsprechend konfiguriert werden:
/etc/sysconfig/network-scripts/ifcfg-ethX:

1
2
3
4
5
6
DEVICE=ethX
BOOTPROTO=none
ONBOOT=yes
MASTER=bond0
SLAVE=yes
USERCTL=no

Zurück zur Übersicht.

,

Hochverfügbarkeit mit Linux – Teil 7

March 8th, 2011
Comments Off

Mit Heartbeat und Load Balancing kann der Ausfall kompletter Maschinen abgefangen werden, was noch fehlt ist die Redundanz auf Netzwerk-Ebene. Linux kann mehrere Ethernet Interfaces zu einem sogenannten “bond” verknuepfen, und so diese notwendige Redundanz schaffen. Bei Ausfall einer Switch-Verbindung, eines LAN-Kabels oder einer NIC fällt das System nicht auf die Nase, sondern arbeitet mit potentiell verbinderten Ressourcen weiter.

Auf Debian wird zunächst das Paket ifenslave-2.6 benötigt.
Die Datei /etc/network/interfaces wird anschließend um die Konfiguration für den “bond” erweitert:

/etc/network/interfaces:

1
2
3
4
5
6
7
iface bond0 inet static
  address 198.18.0.1
  netmask 255.255.255.0
  network 198.18.0.0
  gateway 198.18.0.254
  slaves eth0 eth1
  bond_mode active-backup

In diesem Beispiel werden eth0 und eth1 zu einem Interface (= bond0) gebündelt. bond_mode active-backup definiert hierbei, daß ein Link als Backup für den anderen konfiguriert werden soll.

Andere mögliche Modi sind:

  • balance-rr – ein Round Robin über die NICs
  • balance-xor – die MAC-Adresse des Ziels wird gehashed und entsprechend ein Interface für die Kommunikation ausgewählt. Damit ist grundlegend ein Load Balancing auf Netzwerk-Ebene realisiert, genauso wie eine ausreichende Fehlertolleranz.
  • broadcast – jedes Paket wird auf allen Interfaces gesendet. Damit entsteht 100%ige Fehlertolleranz.
  • 802.3addynamic link aggregation per IEEE 802.3ad – erfordert jedoch spezielle Switch-Konfiguration.
  • balance-tlb – der Host entscheidet je nach Last welche NIC genutzt werden soll.

Zurück zur Übersicht.

,

Hochverfügbarkeit mit Linux – Teil 6

March 7th, 2011
Comments Off

Nach Teil 5, dem Load Balancing per NAT, geht’s heute um Load Balancing per direct routing.
Zur Erinnerung:

Die Anfragen kommen am Load Balancer an, der die Ziel MAC-Adresse in den Paketen abändert. Sehen wir uns diesen Prozess im Detail an:

  1. Eine Anfrage kommt beim Load Balancer auf Port 80 an, da er auf seinem Interface die angefragte IP-Adresse gebunden hat.
  2. Der Load Balancer ändert das Paket ab, in dem er die MAC-Adresse eines der Web-Server als Ziel einträgt. Anschließend kommt das geänderte Paket wieder auf das Ethernet.
  3. Einer der Web-Server erhält das Paket auf z.B. eth0, jedoch mit einer IP die auf einem dummy-Interface gebunden ist, tut was ein Web-Server eben tut und schickt die Antwort an an den Client zurück. Das Paket erhält als Absender-IP die auf dummy gebundene IP.
  4. Je nach Konfiguration (persistent=) behält der Load Balancer die Information auf welchen Web-Server er den Client geschickt hat eine Weile bei oder entfernt diesen Eintrag aus der Adress-Tabelle.

Notwendig für die Funktion dieses Workflows ist, daß alle Maschinen sich im gleichen Layer 2 Segment befinden. Zwischen dem Load Balancer und den Web-Servern darf bei direct routing kein Router stehen.

Konkret mit IPv4 Adressen bestückt, sieht die Konfiguration auf dem Load Balancer folgendermaßen aus:

Die IP 198.18.0.1 muß direkt auf z.B. eth0 gebunden werden. Dies ist wichtig, damit der Router eine valide MAC-Adresse für 198.18.0.1 lernen kann und die Pakete auf den Load Balancer zustellen kann.

/etc/ha.d/conf/ldirectord.cf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
checktimeout=10
checkinterval=2
autoreload=yes
callback="/etc/ha.d/config_sync.sh"
logfile="local0"
quiescent=yes
 
virtual=198.18.0.1:80
        real=198.18.0.11:80 gate
        real=198.18.0.12:80 gate
        real=198.18.0.13:80 gate
	fallback=127.0.0.1:80
	service=http
	request="lb-test.html"
	receive="shiny captain"
	scheduler=wrr
	protocol=tcp
	checktype=negotiate

Bisher ist die Konfiguration in /etc/ha.d/conf/ldirectord.cf von den IP-Adressen und gate anstatt masq stark identisch, allerdings muß für direct routing auch jeder einzelne Web-Server angefasst werden.

Zunächst muß das dummy-Modul geladen werden, so daß dem System das Interface dummy0 zur Verfügung steht.
root@webserver:~# modprobe dummy
root@webserver:~# echo dummy >> /etc/modules

Die Datei /etc/network/interfaces wird um die Konfiguration für dummy0 erweitert:

1
2
3
4
auto dummy0
iface dummy0 inet static
        address 198.18.0.1
        netmask 255.255.255.255

Ein beherztes ifup dummy0, und alles ist bereit für einen ersten Test. Für diesen Test kann der ldirectord von Hand gestartet werden, also per /etc/init.d/ldirectord start, allerdings sollte später die /etc/ha.d/haresources um die entsprechende Zeile erweitert werden. Wenn alles klappt, sollte ipvsadm -Ln nach wenigen Sekunden die ersten Ausgaben liefern:

root@frasier:~# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 198.18.0.1:80 wrr persistent 1200
-> 198.18.0.11:80 Route 1 0 0

Der Load Balancer hat erkannt, daß auf 198.18.0.11 der konfigurierte Service erreichbar ist, sofern bei Weight eine “1″ steht. Bei “0″ konnte keine Verbindung aufgebaut werden, und quiescent=yes ist gesetzt. (Siehe Teil 5)

root@frasier:~# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 198.18.0.1:80 wrr persistent 1200
-> 198.18.0.11:80 Route 0 0 0

Entsprechendes mit IPv6 ist auch kein Hexenwerk.

In der Datei /etc/ha.d/conf/ldirectord.cf auf checktype und checkcommand achten, und die IPv6 Adressen ausschreiben und nicht mit :: abkürzen.
/etc/ha.d/conf/ldirectord.cf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
checktimeout=10
checkinterval=2
autoreload=yes
callback="/etc/ha.d/config_sync.sh"
logfile="local0"
quiescent=no
 
virtual=[2001:db8:0:1:0:0:0:a]:443
        real=[2001:db8:0:1:0:0:0:1]:443 gate
        real=[2001:db8:0:1:0:0:0:2]:443 gate
        real=[2001:db8:0:1:0:0:0:3]:443 gate
	fallback=[::1]:443
        persistent=1200
	scheduler=wrr
	protocol=tcp
	checktype=external
        checkcommand="/usr/share/nagios/libexec/check_http"

Entsprechend der Konfiguration für IPv4 muß auch die IP-Adresse 2001:db8:0:1::a auf jedem einzelnen Web-Server gebunden werden.

1
2
3
4
auto dummy0
iface dummy0 inet6 static
        address 2001:db8:0:1::a
        netmask 64

Mit ipvsadm -Ln werden auch IPv6 Verbindungen angezeigt.
root@frasier:~# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 198.18.0.1:80 wrr persistent 1200
-> 198.18.0.11:80 Route 1 0 0
-> 198.18.0.12:80 Route 1 0 0
-> 198.18.0.13:80 Route 1 0 0
TCP [2001:db8:0:1::a]:80 wrr persistent 1200
-> [2001:db8:0:1::1]:80 Route 1 0 0
-> [2001:db8:0:1::2]:80 Route 1 0 0
-> [2001:db8:0:1::3]:80 Route 1 0 0

Zurück zur Übersicht.

, , , ,

Hochverfügbarkeit mit Linux – Teil 5

March 4th, 2011

In Teil 2 habe in angefangen über den ldirectord zu schreiben. Nochmal zur Erinnerung: der ldirectord ist ein User-Space Script, welches mit Hilfe des IP Virtual Servers (kurz IPVS) im Kernel einen Load Balancer in Software abbildet. Angerissen habe ich bereits zwei Methoden zum Load Balancing,

  • NAT
  • direct routing

Dieser Artikel beschäftigt sich mit Load Balancing per NAT.

Nochmal zum Konzept:

Die Anfragen kommen am Load Balancer an, der anschließend auf die Web-Farm NATet. Sehen wir uns diesen Prozess im Detail an:

  1. Eine Anfrage kommt beim Load Balancer auf dem externen Interface Port 80 an
  2. Der Load Balancer verzeichnet Verbindungsdaten wie
    • Quell-IP
    • Ziel-IP
    • Quell-Port
    • Ziel-Port
    • Zeitstempel der Verbindung

    und ändert das Paket an Hand seiner Konfiguration ab. Die Ziel-IP wird durch eine der Web-Server IPs im internen Netz ausgetauscht und mit der entsprechenden MAC-Adresse versehen. Danach schickt der Load Balancer das Paket auf dem internen Interface wieder auf’s Ethernet.

  3. Einer der Web-Server erhält das Paket, tut was ein Web-Server eben tut und schickt die Antwort an sein default-Gateway (den Load Balancer) zurück.
  4. Der Load Balancer erhält das Paket vom Web-Server und ändert die Quell-IP wieder auf seine eigene IP zurück, so daß für den anfragenden Client die Verbindung die er zum Load Balancer aufgebaut hat konsistent bleibt.
  5. Je nach Konfiguration (persistent=) behält der Load Balancer die Information auf welchen Web-Server er den Client geschickt hat eine Weile bei oder entfernt diesen Eintrag aus der Adress-Tabelle.

Abgesehen von der Entscheidung auf welchen Ziel Web-Server die Verbindung geNATtet wird, unterscheidet sich dieser Prozess nicht vom NAT in z.B. einer Fritz!Box. Zum besseren Verständnis der Konfiguration, obiges Beispiel mit IPv4 Adressen:

Das externe Interface des Load Balancers steht in 198.18.0.0/24 und das interne Netz mit der Web-Farm wird durch 10.0.0.0/24 repräsentiert. Durch dieses Setup lassen sich effizient öffentliche IPv4-Adressen einsparen, denn für die Website www.example.org wird trotz mehrerer Web-Server nur eine einzige IPv4 Adresse benötigt.
Die Konfiguration des ldirectord geschieht in der Datei /etc/ha.d/conf/ldirectord.cf, wobei der Pfad zu dieser Datei in /etc/default/ldirectord überschrieben werden kann, z.B. wenn man den ldirectord nicht mit Heartbeat kombinieren möchte.

/etc/ha.d/conf/ldirectord.cf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
checktimeout=10
checkinterval=2
autoreload=yes
callback="/etc/ha.d/config_sync.sh"
logfile="local0"
quiescent=yes
 
virtual=198.18.0.1:80
        real=10.0.0.21:80 masq
        real=10.0.0.22:80 masq
        real=10.0.0.23:80 masq
	fallback=127.0.0.1:80
	service=http
	request="lb-test.html"
	receive="shiny captain"
	scheduler=wrr
	protocol=tcp
	checktype=negotiate

Die Konfiguration von oben nach unten:

  • checktimeout und checkinterval definieren wie häufig die Ziel-Maschinen geprüft werden sollen, bzw. wie hoch das Timeout definiert werden soll, bis ein Server als tot definiert wird. Die Angaben sind in Sekunden.
  • autoreload=yes definiert, daß die Konfiguration neu geladen wurde, sobald sie sich auf der Festplatte geändert hat. Damit einher geht
  • callback, denn hier kann ein Binary oder Script angegeben werden, das ausgeführt wird, sobald die Konfiguration sich geändert hatte. config_sync.sh ist nur ein Beispiel, dieses Script ist nicht Teil der Distribution.
  • logfile definiert eine Log-Datei bzw. eine Syslog Facility
  • auf quiescent=yes werde ich weiter unten noch genauer eingehen, denn dieser Punkt arbeitet eng mit persistent in der virtual-Sektion zusammen.

Die virtual-Sektion definiert jeweils eine “virtuelle” IP (das, was man in den DNS für www einträgt) und kann mehrfach in der Konfiguration stehen, sofern mehr als nur eine IP gebalanced werden soll. Jede Zeile der Konfiguration die zu einer virtual-Sektion gehört muß mit einem Tabulator (\t) eingerückt werden; leider ist die Syntax-Prüfung etwas seltsam und man bekommt extrem nichts sagende Fehlermeldungen.

  • virtual=198.18.0.1:80 definiert 198.18.0.1 mit Port 80 als virtuelle Adresse. Diese IP muß auf dem Load Balancer lokal (z.B. an eth0, nicht an lo oder dummy0) gebunden werden, denn ldirectord bindet die IP nicht automatisch. Bei einem HA-Setup empfiehlt es sich natürlich diese IP als Cluster-Ressource zu definieren.
  • real=10.0.0.21:80 masq definiert einen Backend-Server inklusive des Ports. Es ist durchaus möglich intern die Web-Server auf anderen Ports zu betreiben, nach außen allerdings weiter auf Port 80 verfügbar zu sein. masq schaltet den IPVS in den NAT-Modus.
  • Mit fallback=127.0.0.1:80 wird ein Notfall-Server definiert, der dann genutzt wird, wenn alle reellen Server als defekt definiert wurden. Hier kann z.B. ein “Wartungsarbeiten, komm später wieder” Text auf einem lokalen Web-Server definiert werden; oder man läßt die Zeile komplett weg, wenn man keinen Fallback-Server hat oder möchte. Anfragen gehen dann allerdings ins Leere.
  • service definiert den zu balancenden Dienst, sofern checktype=negotiate gesetzt ist. Eine Liste der verfügbaren Dienste befindet sich in der man-page des ldirectord, interessant sind zwei besondere Fälle:
    • none: es soll keine Form von Checks stattfinden. Wenn ein Server ausfällt wird der Load Balancer das nicht bemerken und den Server weiterhin in der Verteilung nutzen.
    • simpletcp: ein generischer Service der mit request eine TCP-Verbindung aufbaut und das empfangene Ergebnis mit receive matcht.

    Für “standard” Services wie http, https oder ssh enthält der ldirectord sehr brauchbare Checks, die die Verfügbarkeit des Services exzellent prüfen.

  • scheduler definiert die Load Balancing Methode, den Scheduler Algorithmus. Zur Verfügung stehen 10 verschiedene Algorithmen, unter anderem
    • rrRound Robin: verteile alle Anfragen der Reihe nach auf die Server
    • wrrWeighted Round Robin, gewichteter Round Robin: arbeitet wieder ungewichtete Round Robin, man kann den Servern aber verschieden hohe Prioritäten geben, um beispielsweise ungleich starke Hardware besser ausnutzen zu können
    • lcLeast-Connection: Maschinen mit weniger offenen Verbindungen als andere bekommen bevorzugt neue Verbindungen zugeteilt
    • wlcWeighted Least-Connection: ebenfalls vergleichbar mit Least-Connection, allerdings wieder mit Gewichtung der Maschinen
    • dhDestination Hashing: erstellt einen Hash über die Ziel-IP und weist darüber einen Backend-Server zu
    • shSource Hashing: erstellt einen Hash über die Quell-IP und weist darüber einen Backend-Server zu
      IPVS kennt weiterhin noch Locality-Based Least-Connection (mit und ohne Replikation) sowie Shortest Expected Delay und Never Queue, aber diese Scheduling Alorithmen habe ich noch nie in freier Wildbahn gesehen und vernachlässige sie daher an dieser Stelle. Die man-Page von ipvsadm gibt im Detail Auskunft.
  • Schließlich gibt checktype noch an auf welche Weise die Backend-Server geprüft werden sollen. Mehr dazu gleich mehr.

Mit IPv6 ist die Konfiguration nahezu identisch, abgesehen von den Check-Methoden.

Außerdem wird diesmal https gebalanced.
/etc/ha.d/conf/ldirectord.cf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
checktimeout=10
checkinterval=2
autoreload=yes
callback="/etc/ha.d/config_sync.sh"
logfile="local0"
quiescent=no
 
virtual=[2001:db8:0:1:0:0:0:1]:443
        real=[2001:db8:0:2:0:0:0:21]:443 masq
        real=[2001:db8:0:2:0:0:0:22]:443 masq
        real=[2001:db8:0:2:0:0:0:23]:443 masq
	fallback=[::1]:443
        persistent=1200
	service=http
	request="lb-test.html"
	receive="shiny captain"
	scheduler=wrr
	protocol=tcp
	checktype=negotiate

Zwei Konfigurations-Direktiven bin ich Euch noch schuldig: quiescent und checktype.
Um zu erkennen in welchem Zustand sich die Backend-Server befinden, muß der Load Balancer regelmäßig prüfen, ob die Maschinen wie gewünscht antworten und ihren zugewiesenen Dienst erfüllen können. Wird checktype=connect gesetzt, so baut der ldirectord eine einfache TCP-Verbindung auf und prüft ob ein erwarteter String zurück gemeldet wird. Bei UDP würde es hier schon etwas schwerer werden, und bei Protokollen wie https muß sogar ein SSL-Handshake stattfinden. Mit einem TCP-Connect ist es also in manchen Fällen nicht unbedingt getan, hier wird etwas mehr Logik notwendig. checktype=negotiate in Verbindung mit service=https, um beim IPv6 Beispiel zu bleiben baut eine Verbindung auf Port 443 zu den Backend-Servern auf, macht den erwarteten SSL-Handshare und wertet dann den mit receive angegebenen String aus. Wird request und receive nicht angegeben, so gilt der Server bereits bei korrektem Verbindungsaufbau als intakt, ob aber beispielsweise Datenbank-Verbindungen funktionieren oder nicht wird der Load Balancer nicht erkennen. Hier ist Kreativität gefragt eine “OK“- bzw. “ERROR“-Seite zu basteln, die alle notwendigen Verbindungen der Web-Applikation prüft und entsprechend einen Status abliefert. Sofern notwendig, kann checktimeout nach oben hin angepasst werden, wenn z.B. alle Datenbank-Verbindungen aufzubauen länger als 10 Sekunden dauert.
Wird ein Backend-System auf Grund der Checks als defekt markiert, kommt quiescent ins Spiel. Bei yes wird der betroffene Backend-Server still gelegt, es werden also keine neuen Verbindungen zugelassen, er befindet sich aber noch in der Kernel-Tabelle des IPVS. Wird quiescent=no gesetzt, so wird er restlos aus der Kernel-Tabelle entfernt. In das IPv6/SSL Beispiel habe ich noch den Parameter persistent=1200 geschmuggelt. persistent definiert wie lange .. genau, das stand ja weiter oben schon, wie lange eine Verbindung in der Kernel-Tabelle gehalten werden soll, wieviele Sekunden also ein Client auf den gleichen Backend-Server gebalanced werden soll. Bei quiescent=no wird bei Ausfall eines Servers die Verbindung trotz persistent direkt neu verteilt, bei quiescent=yes muß erst das Timeout von hier 1200 Sekunden (= 20 Minuten) abgewartet werden.

Bleibt nur noch der erwähnte Sonderfall mit den Check-Methoden im Zusammenhang mit IPv6: checktype=negotiate funktioniert mit den meisten Protokollen für IPv6 noch nicht. (Stand März 2011, ldirectord-Version v1.186-ha aus Debian Squeeze) Dies ist allerdinge kein Show-Stopper, denn checktype kennt noch einen weiteren Parameter: external.

1
2
3
4
5
6
7
8
virtual=[2001:db8:0:1:0:0:0:1]:80
        real=[2001:db8:0:2:0:0:0:21]:80 masq
        real=[2001:db8:0:2:0:0:0:22]:80 masq
        real=[2001:db8:0:2:0:0:0:23]:80 masq
	fallback=[::1]:80
	scheduler=rr
	checktype=external
        checkcommand="/usr/share/nagios/libexec/check_http"

Bei checktype=external kann mit checkcommand ein beiliebiges Binary oder Script angegeben werden, welches den Backend-Server prüft. Nagios-Plugins, /bin/true oder was dem geneigten Admin sonst noch so einfällt, wichtig ist nur, daß entsprechende Error-Codes an den ldirectord geliefert werden. Und der Gedanke bei Ausfall eines real-Servers gleich einen passive service check an Nagios weiter zu geben hat doch was, oder? ;-)

Teil 6 beschreibt direct routing im Detail.

Zurück zur Übersicht.

, , , ,

Hochverfügbarkeit mit Linux – Teil 4 – Heartbeat per Puppet

March 3rd, 2011

Das Fazit des ersten Artikes: einen Heartbeat-Cluster zu bauen ist nicht sonderlich schwer, eigentlich nur Fleißarbeit. Und diese Fleißarbeit zu automatisieren ist “die hohe Kunst” der Faulheit. ;-) Puppet hilft dabei zu automatisieren.

Um das Heartbeat-Modul nutzen zu können wird zunächst noch die Modul-Sammlung aus dem folgenden Repository benötigt:
git clone git://github.com/ripienaar/puppet-concat.git
Dann noch das eigentlich Heartbeat-Modul:
git clone git://github.com/shl/ipvs-lb.git

In den node-Definitionen der Cluster-Nodes muß das Modul noch eingebunden und damit konfiguriert werden:

1
2
3
4
5
6
heartbeat { 'ClusterName':
  resources = [ '198.18.23.42', 'nfs-kernel-server' ],
  key = 'banane', 
  iface = 'eth0',
  peer_ip = '198.19.0.1' 
}

Puppet generiert nun beim nächsten run die notwendigen Konfigurationen und baut damit den HA-Cluster. Und weil man damit massig Zeit sparen kann, darf dieser Artikel auch etwas kürzer sein.

Zurück zur Übersicht.

, ,

Hochverfügbarkeit mit Linux – Teil 3 – DRBD

March 2nd, 2011
Comments Off

Der in Teil 1 aufgebauten Heartbeat-Cluster soll nun ein Aufgabe bekommen: hochverfügbarer NFS-Server mit lokalem Storage. Was sind die wichtigen Parameter dabei?

  1. eine gemeinsame Cluster-IP unter der die Clients die NFS-Shares mounten können
  2. das zu exportierende Dateisystem muß auf beiden Nodes lokal vorhanden sein
  3. das zu exportierende Dateisystem muß konstant zwischen den Nodes gespiegelt werden
  4. bei Ausfall eines Nodes muß der andere Node in der Lage sein das Dateisystem wie erwartet per NFS zu exportieren

Hier beitet sich DRBD an, Distributed Replicated Block Device, eine Technologie zur synchronen Replikation von Block Devices über das IP-Netzwerk. Pro Blech wird ein physikalisches Volume (eine Festplatte, eine RAID-Gruppe, ein USB-Stick oder sonstwas…) definiert, welches von DRBD verwaltet und synchronisiert wird. Im User-Space taucht ein neues Device auf, z.B. /dev/drbd0 und kann auf dem aktiven Node wie ein normales Device verwendet werden. Auf dem passiven Node ist das Device nicht mountbar, so lange bis mittels Heartbeat definiert wird, daß dieser Node das Dateisystem exklusiv mounten darf.

Wie funktioniert diese Replikation?
Simpel ausgedrückt: jeder Schreib-Zugriff auf das DRBD-Device macht den Umweg über den IP-Stack und damit auf den anderen Node. Jedes Bit wird also zweimal geschrieben, und erst wenn beide Maschinen die Daten korrekt auf die Platten geschoben haben, bekommt der User das entsprechende Feedback und der Schreibvorgang wird beendet.

Setup:
Auf beiden Nodes benötigen wir zwei neue Partitionen, eine für die Nutz-Daten und eine weitere für die DRBD Meta-Daten. Die Meta-Partition muß minimal 128MB groß sein, je nach Größe der Daten-Partition auch gerne größer. An “Frasier” und “Niles” habe ich der Einfachheit halber je einen USB-Stick angeschlossen und zwei Partitionen erstellt:

  • /dev/sdb1 mit 512MB für die Nutz-Daten
  • /dev/sdb2 mit 256MB für die Meta-Daten

Folgendes ist auf beiden Nodes auszuführen:
root@frasier:~# apt-get install drbd8-utils
root@frasier:~# modprobe drbd
root@frasier:~# echo drbd >> /etc/modules

Die Konfiguration von DRBD wird in /etc/drbd.conf und /etc/drbd.d/* erledigt, wobei /etc/drbd.conf nur zwei include-Statements für /etc/drbd.d/* enthält und nicht weiter angepasst werden muß. Interessanter ist die Datei /etc/drbd.d/global_common.conf, wobei auch hier keine für das Beispiel notwendigen Änderungen vorzunehmen sind.
Für jede Ressource, die von DRBD verwaltet werden soll, wird eine spezielle Konfigurationsdatei in /etc/drbd.d/ angelegt und entsprechend benannt. Wegen dem include in /etc/drbd.conf muß jede Ressourcen-Datei *.res benannt werden.

/etc/drbd.d/r0.res:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
resource r0 {
  protocol C;
  handlers {
    pri-on-incon-degr "echo o > /proc/sysrq-trigger ; halt -f";
    pri-lost-after-sb "echo o > /proc/sysrq-trigger ; halt -f";
    local-io-error "echo o > /proc/sysrq-trigger ; halt -f";
  }
 
  startup {
    degr-wfc-timeout 120;    # 2 minutes.
  }
 
  disk {
    on-io-error detach;
  }
 
  net {
  }
 
  syncer {
    rate 1M; 
    al-extents 257;
  }
 
on frasier {
    device /dev/drbd0;
    disk /dev/sdb1;
    address 198.18.1.10:7788;
    meta-disk /dev/sdb2[0];
  }
 
on niles {
    device /dev/drbd0;
    disk /dev/sdb1;
    address 198.18.1.11:7788;
    meta-disk /dev/sdb2[0];
  }
}

Es wird eine Ressource r0 definiert und entsprechend konfiguriert. Die relevanten Teile der Konfiguration von oben nach unten:

1
2
3
4
5
  handlers {
    pri-on-incon-degr "echo o > /proc/sysrq-trigger ; halt -f";
    pri-lost-after-sb "echo o > /proc/sysrq-trigger ; halt -f";
    local-io-error "echo o > /proc/sysrq-trigger ; halt -f";
  }

Was soll passieren, wenn DRBD zu starke I/O-Fehler feststellt? In diesem Beispiel ist die konsistenz der Daten vorrangig, daher schalte den Node ab und schreibe gar nichts mehr. Wer ein anderes Verhalten wünscht tausche die Befehle in den " " entsprechend aus.

1
2
3
4
  syncer {
    rate 1M; 
    al-extents 257;
  }

Mit welcher Geschwindigkeit sollen die Daten zwischen den Nodes synchronisiert werden? Im Falle der USB-Sticks an “Frasier” und “Niles” hier nur mit maximal 1MB/s, doch falls entsprechend schnelle Links vorhanden sind (z.B. ein dedizierter Gigabit- oder 10-Gigabit-Link) wird hier ein Wert in Megabyte pro Sekunde angegeben.

1
2
3
4
5
6
on frasier {
    device /dev/drbd0;
    disk /dev/sdb1;
    address 198.18.1.10:7788;
    meta-disk /dev/sdb2[0];
  }

Einen solchen Block gibt es pro Node einmal, hier werden die Devices und die IP-Adresse/DNS-Name des Nodes definiert. Wie oben beschrieben ist /dev/sdb1 die Partition für die Nutz-Daten und /dev/sdb2 für die Meta-Daten des DRBD reserviert.

Die Konfigurationen müssen auf allen Nodes identisch sein. Es bietet sich also an z.B. mit rsync das komplette /etc/drbd.d/ zu übertragen. Jetzt wird es Zeit das DRBD-Device zu initialisieren:

root@frasier:~# drbdadm create-md r0
root@niles:~# drbdadm create-md r0
root@frasier:~# drbdadm up all
root@niles:~# drbdadm up all
root@frasier:~# drbdsetup /dev/drbd0 primary -o
root@frasier:~# mkfs.ext3 /dev/drbd0
root@frasier:~# mkdir /data
root@niles:~# mkdir /data
root@frasier:~# mount -t ext3 /dev/drbd0 /data

Im Hintergrund hat das DRBD schon begonnen die Devices zu synchronisieren, was man mit cat /proc/drbd beobachten kann. Sobald der initiale Transfer abgeschlossen ist, wird es Zeit den zugehörigen Service zu starten. Damit ist die Konfiguration von DRBD beendet und es fehlt nur noch die Anpassung des Heartbeats.

root@frasier:~# /etc/init.d/drbd start
root@niles:~# /etc/init.d/drbd start

Sowohl die DRBD Ressource r0 als auch das Filesystem werden als Cluster-Ressource in der Datei /etc/ha.d/haresources auf beiden Nodes verankert.

/etc/ha.d/haresources:

1
2
3
4
frasier IPaddr2::198.18.0.1/32/eth0 \
        drbddisk::r0 \
        Filesystem::/dev/drbd0::/data::ext3 \
        nfs-kernel-server

Auf “frasier” ist /data bereits gemountet, der NFS-Server wird mit entsprechenden Einträgen in /etc/exports direkt loslegen können. Auf “niles” läuft kein NFS-Server und /data ist ein leerer Mountpoint. Sobald “frasier” zur Wartung abgeschaltet wird oder ausfällt, wird Heartbeat auf “niles” erst das Dateisystem mounten und anschließend den NFS-Server starten. Richtig geraten, die Reihenfolge in /etc/ha.d/haresources spielt eine Rolle. :-)

Zurück zur Übersicht.

, , ,

Hochverfügbarkeit mit Linux – Teil 2

March 1st, 2011
Comments Off

Der Begriff Cluster wird häufig in unterschiedlichen Zusammenhängen genutzt und ist daher leider im allgemeinem Sprachgebrauch etwas verwaschen. Von HPC-Clustern oder partitionierten Datenbanken abgesehen, sind folgende zwei Cluster-Typen in dieser Artikel-Reihe interessant:

  • HA-Cluster
    Mit einem High Availability-Cluster ist der Zusammenschluß von zwei oder mehr Maschinen gemeint, der sicherstellt, daß ein Dienst auch bei Ausfall eines Nodes verfügbar ist. Grundsätzlich gibt es verschiedene Modelle einen solchen HA-Cluster zu designen, doch bisher sind mir in der Praxis bei einem HA-Dienst nur active/passive Cluster und bei mehreren Diensten active/active Cluster begegnet.
  • Load Balancing Cluster
    Typisches Einsatzgebiet eines Load Balancing Clusters sind Web-Farmen. Die Web-Farm besteht aus mehreren Web-Servern mit identischem Inhalt, und ein (hoffentlich) redundanter Load Balancer stellt sicher, daß die Anfragen entsprechend auf die Web-Server verteilt werden.

Zur Verdeutlichung:
Ein active/passive HA-Cluster mit verschiedenen Cluster-Ressourcen, die immer nur auf dem aktiven Node ausgeführt werden. Alle weiteren Nodes dienen ausschließlich zu Standby Zwecken.

Ein active/active HA-Cluster, auf dem mehrere Cluster-Ressourcen auf mehreren Nodes ausgeführt werden. Im Ausfall-Moment werden die ausfallenden Ressourcen auf die übrigend Nodes verteilt. (Die Anzahl und Leistungskraft der Nodes sollte entsprechend gewählt werden, daß bei Ausfall oder Wartung eines Nodes die verbleibenden Nodes die Aufgaben erledigen können.)

Dieser Artikel soll sich mit Load Balancing beschäftigen.

Um beim Beispiel der Web-Farm für Load Balancing zu bleiben: Die Aufgabenstellung lautet, ein Balancing über drei Web-Server zu realisieren.

Möglichkeit 1: DNS
Load Balancing per DNS Round Robin. Im DNS wird pro Web-Server ein Eintrag für den entsprechenden DNS-Namen gemacht.

1
2
3
4
5
6
www   IN A    198.18.20.1
www   IN A    198.18.20.2
www   IN A    198.18.20.3
www   IN AAAA 2001:db8::1 
www   IN AAAA 2001:db8::2 
www   IN AAAA 2001:db8::3

Ausfallsicherheit bietet diese Lösung leider keine, denn die DNS-Antwort interessiert sich genau gar nicht für den Zustand der Web-Server. Allerdings führen die Mehrfacheinträge im DNS zu einer gewissen Gleichverteilung der Anfragen. Fazit: machen wir lieber richtiges Load Balancing. ;-)

Möglichkeit 2: Load Balancing per NAT
Mit Hilfe des ldirectord und dem IP Virtual Server im Kernel können wir ein recht zuverlässiges und performantes Load Balancing erreichen. Es wird hierbei zwischen zwei “Klassen” von IP-Adressen unterschieden:

  • virtuellen Adressen
    Diese IPs werden auf dem Load Balancer lokal gebunden und in den DNS für www.example.org eingetragen. Die einzelnen Web-Server kennen diese Adresse nicht.
  • reelle Adressen
    IPs die auf den Web-Servern gebunden sind und für die Kommunikation zum Load Balancer dienen.

Wenn ein Client nun www.example.org aufruft, so wird die Verbindung auf dem Load Balancer terminiert. Der Client wird keine Kommunikation zu den reellen Web-Servern aufbauen. Der Load Balancer allerdings nutzt NAT um die Verbindung auf einen der Web-Server durchzustellen und das entsprechende Antwort-Paket wieder dem Client zuzuordnen.

Leider bringt diese Möglichkeit alle Nachteile die man von NAT kennt mit, bietet allerdings schon deutliche Vorteile gegenüber dem DNS Round-Robin. Ein klarer Vorteil bleibt natürlich: man spart öffentliche IP-Adressen, was bei IPv4 heute ein Argument sein sollte (!).

Um späteren Artikeln etwas vorzugreifen: eine beispielhafte Konfiguration für den ldirectord und Load Balancing per NAT:

1
2
3
4
5
6
7
virtual=198.18.1.100:80
        real=198.18.20.1:80 masq
        real=198.18.20.2:80 masq
        real=198.18.20.3:80 masq
        service=http
        persistent=1200
        checktype=connect

Möglichkeit 3: Load Balancing auf Layer 2
Bei Durchsatz und Verfügbarkeit hat das Load Balancing auf dem ISO/OSI Layer 2 klar gewonnen, allerdings ist das Setup etwas komplizierter. Eine Anfrage die auf der “virtuelle” Adresse eingeht wird vom Load Balancer entgegen genommen und verarbeitet. Die Antwort auf das Paket kommt allerdings vom Web-Server selbst, und zwar direkt an den anfragenden Client.

Die böse Magie™, die der Load Balancer an dieser Stelle treibt geschieht – wie oben bereits geschrieben – auf dem ISO/OSI Layer 2. Das Paket wird vom Balancer in so fern bei der Verarbeitung abgeändert, als das er die MAC-Adresse einer der Web-Server als Ziel-Adresse einträgt und das Paket wieder auf das Ethernet schickt. Die Web-Server sind nun so konfiguriert, daß sie die virtuelle IP auf einem nicht ARP‘enden dummy-Interface konfiguriert haben, so daß der Kernel das Paket mit gutem Gewissen annehmen und verarbeiten kann. Das Antwort-Paket bekommt als Absender die virtuelle IP und wird dem Client direkt zugestellt. Der Client selbst ist nicht in der Lage zu erkennen ob und wenn ja welche Form von Load Balancing involviert war.

Auch hier vorgegriffen: eine beispielhafte Konfiguration für den ldirectord und Load Balancing per direct routing:

1
2
3
4
5
6
7
virtual=198.18.20.10:80
        real=198.18.20.1:80 gate
        real=198.18.20.2:80 gate
        real=198.18.20.3:80 gate
        service=http
        persistent=1200
        checktype=connect

Auf das Thema Load Balancing und die praktische Umsetzung mittels ldirectord werde ich in späteren Artikel noch genauer eingehen.

Zurück zur Übersicht.

, ,

Hochverfügbarkeit mit Linux – Teil 1

February 28th, 2011

Als Vorbereitung empfehle ich die Folge 41 des Heldenfunks. In knappen zwei Stunden diskutieren Constantin und Hartmut jedes notwendige Vorwissen bis ins Detail durch. Ein perfekter Einstieg in das Thema Hochverfügbarkeit. (Die zwei Stunden könnten übrigens genutzt werden um nebenher zwei Maschinen mit Debian zu deployen…)

HELDENFunk Folge 41: High Availability
Der Podcast von systemhelden.com
Echte Systemhelden haben immer ein offenes Ohr für echte Lösungen. Darum hört die Stimmen der Energie und Ihr werdet lernen.
  

Warum man hochverfügbare Dienste möchte ist nun also geklärt, klären wir noch die Begrifflichkeiten:

  • Cluster
    ein Verbund aus mehreren Blechen die einen oder mehrere gemeinsame Dienste anbieten
  • Node
    ein Mitglied eines Cluster-Verbunds
  • aktiver Node
    ein Node der aktiv einen Dienst ausführt bzw. gebunden hat
  • standby Node
    ein Node der einspringt, sobald ein aktiver Node abgeschaltet wird oder ausfällt
  • Cluster-Ressource
    der zu “clusternde” Dienst, also der Unix-Service, die IP-Adresse oder das Filesystem
  • Heartbeat
    der Prozess den die Nodes nutzen um festzustellen welche anderen Nodes arbeiten (können) “lebst Du noch?
  • Takeover
    ein Host wird als tot definiert und seine Ressourcen übernommen

Im “normal”-Zustand würde ein beispielhaftes, sehr simpel gehaltenes Setup in etwa so aussehen:

Es gibt einen aktiven Node der die Cluster-Ressourcen gebunden hat und ein Standby Node daneben der Strom in warme Luft umwandelt.

Sobald der aktive Node ausfällt (oder zur Wartung abgeschaltet wird) übernimmt der Standby Node die Ressourcen und der oder die angebotene(n) Dienst(e) laufen ohne nennenswerte Unterbrechnung weiter.

Im Podcast gut aufgepasst? Richtig, hier gibt es keinen Quorum-Server.

Setup der Nodes:
Die beiden Cluster-Nodes in diesem Artikel heißen “Frasier” und “Niles“:
root@frasier:~# apt-get install heartbeat
root@niles:~# apt-get install heartbeat

In /etc/ha.d/ müssen drei Dateien erstellt werden, z.B. nach den Vorgaben in /usr/share/doc/heartbeat:

  1. /etc/ha.d/authkeys
    Schlüssel zur Kommunikation zwischen den Nodes
  2. /etc/ha.d/haresources
    die Liste der Cluster-Ressourcen inkl. Zuweisung der Nodes
  3. /etc/ha.d/ha.cf
    Konfiguration des Heartbeat-Daemons

Wichtig: /etc/ha.d/authkeys und /etc/ha.d/haresources müssen auf allen Nodes identisch sein, /etc/ha.d/ha.cf kann spezifische Konfigurationen für jeden Node enthalten. Sehen wir uns die Dateien im Detail an.

/etc/ha.d/authkeys:

1
2
auth 1
1 sha1 broccoli

Hier sollten keine großen Erklärungen notwendig sein. Der gemeinsame Schlüssel der Nodes wird als SHA1-Hash des Wortes broccoli konfiguriert.

/etc/ha.d/haresources:

1
2
frasier IPaddr2::198.18.0.1/32/eth0 \
        IPaddr2::198.19.0.1/32/eth1

Wir definiere zwei Cluster-Ressourcen, eine IPv4-Adresse für eth0 und eine für eth1. Die Konfiguration für eth0 und eth1 in /etc/network/interfaces geben das korrekte Subnetz vor, daher sollten in den haresources nur /32-Masken genutzt werden (z.B. eth0 mit 198.18.0.0/16 und eth1 mit 198.19.0.0/16). Beide Adressen sollen beim initialen Cluster-Start auf “frasier” gebunden werden.

Eine etwas kompliziertere Version von haresources zur Veranschaulichung:

1
2
3
4
5
6
7
8
9
frasier IPaddr2::198.18.0.1/32/eth0 \
        IPaddr2::198.19.0.1/32/eth1 \
        IPaddr2::10.111.222.1/32/eth2.82 \
        IPv6addr::2001:db8:0:0:0:0:0:1/128/eth2.103 \
        atftpd \
        radvd \
        isc-dhcp-server \
        nfs-kernel-server \
        ldirectord

Für das Heartbeat-Setup stellen sowohl IP-Adressen als auch Unix-Services Ressourcen da. Jene Datei definiert diese Ressourcen. Für jede “Klassen” von Ressourcen gibt es entsprechende Scripte in /etc/ha.d/resource.d. Um einen konventionellen Unix-Dienst (z.B. den isc-dhcp-server in eine Cluster-Ressource zu verwandeln, genügt es das init-Script aus /etc/init.d/ nach /etc/ha.d/resource.d zu linken und den automatischen Start zu unterbinden.

root@frasier:/etc/ha.d/resource.d# update-rc.d isc-dhcp-server remove
root@frasier:/etc/ha.d/resource.d# ln -s /etc/init.d/isc-dhcp-server isc-dhcp-server

/etc/ha.d/ha.cf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
logfile	/var/log/ha-log
logfacility	local0
keepalive 10
deadtime 100
warntime 60
initdead 360
auto_failback on
 
bcast eth0
mcast eth1 225.0.0.1 694 1 0
ping 198.18.0.201
 
node frasier
node niles

Alle Werte die in ha.cf nicht definiert werden, werden mit entsprechenden default-Werten gesetzt.

  • keepalive definiert die Zeit zwischen den Heartbeat-Paketen in Sekunden
  • deadtime definiert die Zeit in Sekunden, die verstreichen muß, bis ein Node als tot definiert wird. Im Zusammenhang mit keepalive 10 müssen 10 Heartbeat-Pakete verloren gehen, bis der Node deaktiviert wird und gegebenenfalls ein Takeover stattfindet.
  • warntime definiert (ebenfalls in Sekunden) die Zeit bis die ersten Log-Meldungen geschrieben werden, falls Heartbeat-Pakete verloren gehen
  • initdead wann soll beim initialen Start ein Node als tot definiert werden? (Ja, wieder in Sekunden)
  • auto_failback soll nach einem Takeover an den Standby wieder auf den ursprünglichen Master geswitcht werden, sofern dieser wieder verfügbar ist?

Die Heartbeat-Pakete können über verschiedene Methoden verschickt werden. Aus der Praxis empfiehlt sich mindestens zwei unterschiedliche Methoden und am besten noch dazu unterschiedliche Transportwege zu nutzen.

  • bcast verschicke die Heartbeat-Pakete per Broadcast auf dem angegebenen Interface
  • mcast verschicke die Heartbeat-Pakete per Multicast auf dem angegebenen Interface
  • ucast verschicke die Heartbeat-Pakete per Unicast über das angebene Interface an die angegebene IP des anderen Nodes
  • serial verschicke die Heartbeat-Pakete über eine serielle Leitung am angegebenen tty

In der ping-Direktive können Maschinen angegeben werden, die von allen Cluster-Nodes gepingt werden. Hiermit wird festgestellt ob die Nodes nicht nur sich selbst (per Heartbeat) sondern auch den Rest des Netzwerks “sehen” können. Defekte NICs und LAN-Verbindungen können hiermit sinnvoll erkannt und umgangen werden. Der letzte Teil der ha.cf definiert alle verfügbaren Nodes im Cluster.

Testen des Setups:
Wenn “frasier” und “niles” korrekt konfiguriert sind ist es Zeit den Heartbeat zu starten.
root@frasier:~# /etc/init.d/heartbeat start
root@niles:~# /etc/init.d/heartbeat start

Der initiale Start dauert ein wenig, daher empfiehlt es sich auf beiden Maschinen /var/log/ha-log zu beobachten. Fehlermeldungen werden im Normalfall recht sprechend in der Log-Datei verzeichnet. Wenn alles geklappt hat, wird “frasier” die beiden IPv4-Ressourcen innerhalb von wenigen Minuten binden.

Zurück zur Übersicht.

, , ,

Artikel-Reihe: “Hochverfügbarkeit mit Linux”

February 28th, 2011
Comments Off

In der nächsten Zeit werde ich eine Artikel-Reihe zum Thema Hochverfügbarkeit mit Linux schreiben. Die Motivation dahinter ist, daß erstaunlich wenig qualitativer Content zu dieser Thematik im Netz zu finden ist, sofern man nach halbwegs aktuellen Versionen oder Artikeln in deutscher Sprache ausschau hält.
Diese Artikelreihe befasst sich mit dem aktuellen Debian GNU/Linux Release 6.0 “squeeze” und den darin enthaltenen Paketen.

  • Teil 1 – ein simples HA-Setup mit heartbeat
  • Teil 2 – etwas Theorie zu Clustern und Load Balancing
  • Teil 3 – Block Device Replikation mit DRBD
  • Teil 4 – Heartbeat mit Puppet
  • Teil 5 – Load Balancing per NAT
  • Teil 6 – Load Balancing per direct routing
  • Teil 7 – Ethernet Bonding I
  • Teil 7.1 – Ethernet Bonding II

, , , , , ,