19.18. Highly Available Storage (HAST)

Beigetragen von Daniel Gerzo.
Mit Beiträgen von Freddie Cash, Pawel Jakub Dawidek, Michael W. Lucas und Viktor Petersson.
Übersetzt von Benedict Reuschling.

19.18.1. Überblick

Hochverfügbarkeit ist eine der Hauptanforderungen von ernsthaften Geschäftsanwendungen und hochverfügbarer Speicher ist eine Schlüsselkomponente in solchen Umgebungen. Highly Available STorage, oder HASTHighly Available STorage, wurde von Pawel Jakub Dawidek als ein Framework entwickelt, welches die transparente Speicherung der gleichen Daten über mehrere physikalisch getrennte Maschinen ermöglicht, die über ein TCP/IP-Netzwerk verbunden sind. HAST kann als ein netzbasiertes RAID1 (Spiegel) verstanden werden und ist dem DRBD®-Speichersystem der GNU/Linux®-Plattform ähnlich. In Kombination mit anderen Hochverfügbarkeitseigenschaften von FreeBSD wie CARP, ermöglicht es HAST, hochverfügbare Speichercluster zu bauen, die in der Lage sind, Hardwareausfällen zu widerstehen.

Nachdem Sie diesen Abschnitt gelesen haben, werden Sie folgendes wissen:

  • Was HAST ist, wie es funktioniert und welche Eigenschaften es besitzt.

  • Wie man HAST auf FreeBSD aufsetzt und verwendet.

  • Wie man CARP und devd(8) kombiniert, um ein robustes Speichersystem zu bauen.

Bevor Sie diesen Abschnitt lesen, sollten Sie:

Das HAST-Projekt wurde von der FreeBSD Foundation mit Unterstützung der OMCnet Internet Service GmbH und TransIP BV gesponsert.

19.18.2. HAST-Merkmale

Die Hauptmerkmale des HAST-Systems sind:

  • Es kann zur Maskierung von I/O-Fehlern auf lokalen Festplatten eingesetzt werden.

  • Dateisystem-unabhängig, was es erlaubt, jedes von FreeBSD unterstützte Dateisystem zu verwenden.

  • Effiziente und schnelle Resynchronisation: es werden nur die Blöcke synchronisiert, die während der Ausfallzeit eines Knotens geändert wurden.

  • Es kann in einer bereits bestehenden Umgebung eingesetzt werden, um zusätzliche Redundanz zu erreichen.

  • Zusammen mit CARP, Heartbeat, oder anderen Werkzeugen, ist es möglich, ein robustes und dauerhaftes Speichersystem zu bauen.

19.18.3. HAST im Einsatz

HAST stellt auf Block-Ebene eine synchrone Replikation eines beliebigen Speichermediums auf mehreren Maschinen zur Verfügung. Daher werden mindestens zwei Knoten (physikalische Maschinen) benötigt: der primary (auch bekannt als master) Knoten, sowie der secondary (slave) Knoten. Diese beiden Maschinen zusammen werden als Cluster bezeichnet.

Anmerkung:

HAST ist momentan auf insgesamt zwei Knoten im Cluster beschränkt.

Da HAST in einer primär-sekundär-Konfiguration funktioniert, ist immer nur ein Knoten des Clusters zu jeder Zeit aktiv. Der primäre Knoten, auch active genannt, ist derjenige, der alle I/O-Anfragen verarbeitet, die an die HAST-Schnittstelle gesendet werden. Der secondary-Knoten wird automatisch vom primary-Knoten aus synchronisiert.

Die physischen Komponenten des HAST-Systems sind:

  • lokale Platte (am Primärknoten)

  • Platte am entfernten Rechner (Sekundärknoten)

HAST arbeitet synchron auf Blockebene, was es für Dateisysteme und Anwendungen transparent macht. HAST stellt gewöhnliche GEOM-Provider im Verzeichnis /dev/hast/ für die Verwendung durch andere Werkzeuge oder Anwendungen zur Verfügung, somit gibt es keinen Unterschied zwischen dem Einsatz von durch HAST bereitgestellten Geräten und herkömmlichen Platten, Partitionen, etc.

Jede Schreib-, Lösch- oder Entleerungsoperation wird an die lokale und über TCP/IP zu der entfernt liegenden Platte gesendet. Jede Leseoperation wird von der lokalen Platte durchgeführt, es sei denn, die lokale Platte ist nicht aktuell oder es tritt ein I/O-Fehler auf. In solchen Fällen wird die Leseoperation an den Sekundärknoten geschickt.

19.18.3.1. Synchronisation und Replikationsmodi

HAST versucht, eine schnelle Fehlerbereinigung zu gewährleisten. Aus diesem Grund ist es sehr wichtig, die Synchronisationszeit nach dem Ausfall eines Knotens zu reduzieren. Um eine schnelle Synchronisation zu ermöglichen, verwaltet HAST eine Bitmap von unsauberen Bereichen auf der Platte und synchronisiert nur diese während einer regulären Synchronisation (mit Ausnahme der initialen Synchronisation).

Es gibt viele Wege, diese Synchronisation zu behandeln. HAST implementiert mehrere Replikationsarten, um unterschiedliche Methoden der Synchronisation zu realisieren:

  • memsync: meldet Schreiboperationen als vollständig, wenn die lokale Schreiboperation beendet ist und der entfernt liegende Knoten die Ankunft der Daten bestätigt hat, jedoch bevor die Daten wirklich gespeichert wurden. Die Daten werden auf dem entfernt liegenden Knoten direkt nach dem Senden der Bestätigung gespeichert. Dieser Modus ist dafür gedacht, Latenzen zu verringern und zusätzlich eine gute Verlässlichkeit zu bieten. Der memsync-Replikationsmodus ist momentan noch nicht implementiert.

  • fullsync: meldet Schreiboperationen als vollständig, wenn die lokale Schreiboperation beendet ist und die entfernte Schreiboperation ebenfalls abgeschlossen wurde. Dies ist der sicherste und zugleich der langsamste Replikationsmodus. Er stellt den momentanen Standardmodus dar.

  • async: meldet Schreiboperationen als vollständig, wenn lokale Schreibvorgänge abgeschlossen wurden. Dies ist der schnellste und gefährlichste Replikationsmodus. Er sollte verwendet werden, wenn die Latenz zu einem entfernten Knoten bei einer Replikation zu hoch ist für andere Modi. Der async-Replikationsmodus ist zum gegenwärtigen Zeitpunkt nicht implementiert.

Warnung:

Momentan wird nur der fullsync-Replikationsmodus unterstützt.

19.18.4. HAST-Konfiguration

HAST benötigt GEOM_GATE-Unterstützung, um korrekt zu funktionieren. Der GENERIC-Kernel enthält jedoch GEOM_GATE nicht von vornherein, jedoch ist in der Standardinstallation von FreeBSD geom_gate.ko als ladbares Modul vorhanden. Stellen Sie bei Systemen, bei denen nur das Allernötigste vorhanden sein soll, sicher, dass dieses Modul zur Verfügung steht. Als Alternative lässt sich die GEOM_GATE-Unterstützung direkt in den Kernel statisch einbauen, indem Sie die folgende Zeile zu Ihrer Kernelkonfigurationsdatei hinzufügen:

options	GEOM_GATE

Das HAST-Framework besteht aus Sicht des Betriebssystems aus mehreren Bestandteilen:

  • Dem hastd(8)-Dienst, welcher für die Datensynchronisation verantwortlich ist,

  • Dem hastctl(8) Management-Werkzeug,

  • Der Konfigurationsdatei hast.conf(5).

Das folgende Beispiel beschreibt, wie man zwei Knoten als master-slave / primary-secondary mittels HAST konfiguriert, um Daten zwischen diesen beiden auszutauschen. Die Knoten werden als hasta mit der IP-Adresse 172.16.0.1 und hastb mit der IP-Adresse 172.16.0.2 bezeichnet. Beide Knoten besitzen eine dedizierte Festplatte /dev/ad6 mit der gleichen Grösse für den HAST-Betrieb. Der HAST-Pool (manchmal auch Ressource genannt, z.B. der GEOM-Provider in /dev/hast/) wird als test bezeichnet.

Die Konfiguration von HAST wird in der Datei /etc/hast.conf vorgenommen. Diese Datei sollte auf beiden Knoten gleich sein. Die denkbar einfachste Konfiguration ist folgende:

resource test {
	on hasta {
		local /dev/ad6
		remote 172.16.0.2
	}
	on hastb {
		local /dev/ad6
		remote 172.16.0.1
	}
}

Schlagen Sie in der hast.conf(5)-Manualpage nach, wenn Sie an erweiterten Konfigurationsmöglichkeiten interessiert sind.

Tipp:

Es ist ebenfalls möglich, den Hostnamen in den remote-Anweisungen zu verwenden. Stellen Sie in solchen Fällen sicher, dass diese Rechner auch aufgelöst werden können, also in der Datei /etc/hosts aufgeführt sind, oder alternativ im lokalen DNS.

Da nun die Konfiguration auf beiden Rechnern vorhanden ist, sind Sie in der Lage, den HAST-Pool zu erstellen. Lassen Sie die folgenden Kommandos auf beiden Knoten ablaufen, um die initialen Metadaten auf die lokale Platte zu schreiben und starten Sie anschliessend den hastd(8)-Dienst:

# hastctl create test
# /etc/rc.d/hastd onestart

Anmerkung:

Es ist nicht möglich, GEOM-Provider mit einem bereits bestehenden Dateisystem zu verwenden (z.B. um einen bestehenden Speicher in einen von HAST verwalteten Pool zu konvertieren), weil diese Prozedur bestimmte Metadaten auf den Provider schreiben muss und dafür nicht genug freier Platz zur Verfügung stehen wird.

HAST ist nicht dafür verantwortlich, die Rolle (primary oder secondary) für den jeweiligen Knoten festzulegen. Die Rolle des Knotens muss vom Administrator oder einer anderen Software wie Heartbeat mittels des hastctl(8)-Werkzeugs festgelegt werden. Auf dem primären Knoten (hasta) geben Sie nun den folgenden Befehl ein:

# hastctl role primary test

Geben Sie nun, ähnlich wie zuvor, das folgende Kommando auf dem sekundären Knoten (hastb) ein:

# hastctl role secondary test

Achtung:

Es kann passieren, dass beide Knoten nicht in der Lage sind, miteinander zu kommunizieren und dadurch beide als primäre Knoten konfiguriert sind; die Konsequenz daraus wird als split-brain bezeichnet. Um diese Situation zu bereinigen, folgen Sie den Schritten, die in Abschnitt 19.18.5.2, „Auflösung des Split-brain-Zustands“ beschrieben sind.

Es ist möglich das Ergebnis des hastctl(8)-Werkzeugs auf jedem Knoten zu überprüfen:

# hastctl status test

Der wichtigste Teil ist die status-Textzeile der Ausgabe, die auf jedem Knoten complete lauten sollte. Falls der Status als degraded zurückgemeldet wird, ist etwas schief gegangen. Zu diesem Zeitpunkt hat die Synchronisation zwischen den beiden Knoten bereits begonnen. Die Synchronisation ist beendet, wenn das Kommando hastctl status meldet, dass die dirty-Bereiche 0 Bytes betragen.

Der letzte Schritt ist, ein Dateisystem auf dem /dev/hast/test GEOM-Provider anzulegen und dieses ins System einzuhängen. Dies muss auf dem primary-Knoten durchgeführt werden (da /dev/hast/test nur auf dem primary-Knoten erscheint). Dies kann ein paar Minuten dauern, abhängig von der Grösse der Festplatte:

# newfs -U /dev/hast/test
# mkdir /hast/test
# mount /dev/hast/test /hast/test

Sobald das HAST-Framework richtig konfiguriert wurde, besteht der letzte Schritt nun darin, sicherzustellen, dass HAST während des Systemstarts automatisch gestartet wird. Die folgende Zeile sollte zur Datei /etc/rc.conf hinzugefügt werden:

hastd_enable="YES"

19.18.4.1. Failover-Konfiguration

Das Ziel dieses Beispiels ist, ein robustes Speichersystem zu bauen, welches Fehlern auf einem beliebigen Knoten widerstehen kann. Die Schlüsselaufgabe in diesem Szenario besteht darin, zu verhindern, dass der primary-Knoten des Clusters ausfällt. Sollte es dennoch passieren, ist der secondary-Knoten da, um nahtlos einzuspringen, das Dateisystem zu prüfen, einzuhängen und mit der Arbeit fortzufahren, ohne dass auch nur ein einzelnes Bit an Daten verloren ging.

Um diese Aufgabe zu bewerkstelligen, ist es nötig, eine weitere Eigenschaft zu nutzen, die unter FreeBSD verfügbar ist, welche ein automatisches Failover auf der IP-Schicht ermöglicht: CARP. CARP steht für Common Address Redundancy Protocol und erlaubt es mehreren Rechnern im gleichen Netzsegment, die gleiche IP-Adresse zu verwenden. Setzen Sie CARP auf beiden Knoten des Clusters anhand der Dokumentation in Abschnitt 32.12, „CARP - Common Address Redundancy Protocol“ auf. Nachdem dieser Schritt abgeschlossen ist, sollte jeder Knoten seine eigene carp0-Schnittstelle mit der geteilten IP-Adresse 172.16.0.254 besitzen. Selbstverständlich muss der primäre HAST-Knoten des Clusters der CARP-Masterknoten sein.

Der HAST-Pool, welcher im vorherigen Abschnitt erstellt wurde, ist nun bereit für den Export über das Netzwerk auf den anderen Rechner. Dies kann durch den Export über NFS, Samba etc. erreicht werden, indem die geteilte IP-Addresse 172.16.0.254 verwendet wird. Das einzige ungelöste Problem ist der automatische Failover, sollte der primäre Knoten einmal ausfallen.

Falls die CARP-Schnittstelle aktiviert oder deaktiviert wird, generiert das FreeBSD-Betriebssystem ein devd(8)-Ereignis, was es ermöglicht, Zustandsänderungen auf den CARP-Schnittstellen zu überwachen. Eine Zustandsänderung auf der CARP-Schnittstelle ist ein Indiz dafür, dass einer der Knoten gerade ausgefallen oder wieder verfügbar ist. In diesem Fall ist es möglich, ein Skript zu starten, welches den Failover automatisch durchführt.

Um diese Zustandsänderungen auf der CARP-Schnittstelle abzufangen, müssen die folgenden Zeilen in der Datei /etc/devd.conf auf jedem Knoten eingefügt werden:

notify 30 {
	match "system" "IFNET";
	match "subsystem" "carp0";
	match "type" "LINK_UP";
	action "/usr/local/sbin/carp-hast-switch master";
};

notify 30 {
	match "system" "IFNET";
	match "subsystem" "carp0";
	match "type" "LINK_DOWN";
	action "/usr/local/sbin/carp-hast-switch slave";
};

Um diese neue Konfiguration zu aktivieren, starten Sie devd(8) auf beiden Knoten neu, um die neue Konfiguration wirksam werden zu lassen:

# /etc/rc.d/devd restart

Für den Fall, dass die carp0-Schnittstelle aktiviert oder deaktiviert wird (sich also der Status der Schnittstelle ändert), erzeugt das System eine Meldung, was es dem devd(8)-Subsystem ermöglicht, ein beliebiges Skript zu starten, in diesem Fall also /usr/local/sbin/carp-hast-switch. Dies ist das Skript, dass den automatischen Failover durchführt. Für genauere Informationen zu der obigen devd(8)-Konfiguration, lesen Sie die devd.conf(5)-Manualpage.

Ein Beispiel für ein solches Skript könnte wie folgt aussehen:

#!/bin/sh

# Original script by Freddie Cash <fjwcash@gmail.com>
# Modified by Michael W. Lucas <mwlucas@BlackHelicopters.org>
# and Viktor Petersson <vpetersson@wireload.net>

# The names of the HAST resources, as listed in /etc/hast.conf
resources="test"

# delay in mounting HAST resource after becoming master
# make your best guess
delay=3

# logging
log="local0.debug"
name="carp-hast"

# end of user configurable stuff

case "$1" in
	master)
		logger -p $log -t $name "Switching to primary provider for ${resources}."
		sleep ${delay}

		# Wait for any "hastd secondary" processes to stop
		for disk in ${resources}; do
			while $( pgrep -lf "hastd: ${disk} \(secondary\)" > /dev/null 2>&1 ); do
				sleep 1
			done

			# Switch role for each disk
			hastctl role primary ${disk}
			if [ $? -ne 0 ]; then
				logger -p $log -t $name "Unable to change role to primary for resource ${disk}."
				exit 1
			fi
		done

		# Wait for the /dev/hast/* devices to appear
		for disk in ${resources}; do
			for I in $( jot 60 ); do
				[ -c "/dev/hast/${disk}" ] && break
				sleep 0.5
			done

			if [ ! -c "/dev/hast/${disk}" ]; then
				logger -p $log -t $name "GEOM provider /dev/hast/${disk} did not appear."
				exit 1
			fi
		done

		logger -p $log -t $name "Role for HAST resources ${resources} switched to primary."


		logger -p $log -t $name "Mounting disks."
		for disk in ${resources}; do
			mkdir -p /hast/${disk}
			fsck -p -y -t ufs /dev/hast/${disk}
			mount /dev/hast/${disk} /hast/${disk}
		done

	;;

	slave)
		logger -p $log -t $name "Switching to secondary provider for ${resources}."

		# Switch roles for the HAST resources
		for disk in ${resources}; do
			if ! mount | grep -q "^/dev/hast/${disk} on "
			then
			else
				umount -f /hast/${disk}
			fi
			sleep $delay
			hastctl role secondary ${disk} 2>&1
			if [ $? -ne 0 ]; then
				logger -p $log -t $name "Unable to switch role to secondary for resource ${disk}."
				exit 1
			fi
			logger -p $log -t $name "Role switched to secondary for resource ${disk}."
		done
	;;
esac

Im Kern führt das Skript die folgenden Aktionen durch, sobald ein Knoten zum master / primary wird:

  • Es ernennt den HAST-Pool als den primären für einen gegebenen Knoten.

  • Es prüft das Dateisystem, dass auf dem HAST-Pool erstellt wurde.

  • Es hängt die Pools an die richtige Stelle im System ein.

Wenn ein Knoten zum backup / secondary ernannt wird:

  • Hängt es den HAST-Pool aus dem Dateisystem aus.

  • Degradiert es den HAST-Pool zum sekundären.

Achtung:

Bitte beachten Sie, dass dieses Skript nur ein Beispiel für eine mögliche Lösung darstellt. Es behandelt nicht alle möglichen Szenarien, die auftreten können und sollte erweitert bzw. abgeändert werden, so dass z.B. benötigte Dienste gestartet oder gestoppt werden usw.

Tipp:

Für dieses Beispiel wurde ein Standard-UFS Dateisystem verwendet. Um die Zeit für die Wiederherstellung zu verringern, kann ein UFS mit Journal oder ein ZFS-Dateisystem benutzt werden.

Weitere detaillierte Informationen mit zusätzlichen Beispielen können auf der HAST Wiki-Seite abgerufen werden.

19.18.5. Fehlerbehebung

19.18.5.1. Allgemeine Tipps zur Fehlerbehebung

HAST sollte generell ohne Probleme funktionieren. Jedoch kann es, wie bei jeder anderen Software auch, zu gewissen Zeiten sein, dass sie sich nicht so verhält wie angegeben. Die Quelle dieser Probleme kann unterschiedlich sein, jedoch sollte als Faustregel gewährleistet werden, dass die Zeit für beide Knoten im Cluster synchron läuft.

Die Anzahl an Debugging-Meldungen von hastd(8) sollte erhöht werden, wenn Fehler von HAST bereinigt werden. Dies kann durch das Starten des hastd(8)-Dienstes mit der Option -d erreicht werden. Wichtig zu wissen ist, dass diese Option mehrfach angegeben werden kann, um die Anzahl an Meldungen weiter zu erhöhen. Sie können viele nützliche Informationen auf diese Art bekommen. Sie sollten ebenfalls die Verwendung der Option -F in Erwägung ziehen, die den hastd(8)-Dienst in den Vordergrund bringt.

19.18.5.2. Auflösung des Split-brain-Zustands

Die Konsequenz aus der Situation, wenn beide Knoten des Clusters nicht in der Lage sind, miteinander zu kommunizieren und dadurch beide als primäre Knoten fungieren, wird als split-brain bezeichnet. Dies ist ein gefährlicher Zustand, weil es beiden Knoten erlaubt ist, Änderungen an den Daten vorzunehmen, die miteinander nicht in Einklang gebracht werden können. Diese Situation sollte vom Systemadministrator händisch bereinigt werden.

Um diese Situation zu beheben, muss der Administrator entscheiden, welcher Knoten die wichtigsten Änderungen von beiden besitzt (oder diese manuell miteinander vermischen) und anschliessend den HAST-Knoten die volle Synchronisation mit jenem Knoten durchführen zu lassen, welcher die beschädigten Daten besitzt. Um dies zu tun, geben Sie die folgenden Befehle auf dem Knoten ein, der neu synchronisiert werden soll:

# hastctl role init <resource>
# hastctl create <resource>
# hastctl role secondary <resource>

Wenn Sie Fragen zu FreeBSD haben, schicken Sie eine E-Mail an <de-bsd-questions@de.FreeBSD.org>.

Wenn Sie Fragen zu dieser Dokumentation haben, schicken Sie eine E-Mail an <de-bsd-translators@de.FreeBSD.org>.