208 208.fi

Hochverfügbarkeit durch eine verteilte Architektur

Einige praktische Einschränkungen der Dienstduplizierung


Hochverfügbarkeit bezeichnet Dienste, die den Betrieb mit einer hohen Wahrscheinlichkeit gewährleisten. Wenn der Dienst auf mehrere unabhängige Geräte ausgerollt wird, sollte ein einzelnes Gerät auf weniger Anfragen reagieren. Dies sorgt nicht nur für bessere Leistung oder Latenz, sondern auch für eine bessere Fehlertoleranz. Wenn einer unserer Nameserver langsamer oder blockiert wird, würde der Dienst dank eines anderen Nameservers weiterlaufen.

Theoretisch spielt die Ursache des Ausfalls eines Geräts keine Rolle. Die häufigste Störung unserer Nameserver ist wohl das regelmäßige Aktualisieren von Service-Containern und Inhalten, wodurch ein Server nach dem anderen für kurze Zeit deaktiviert wird. Da die Funktionalität des aktualisierten Geräts vor der Aktualisierung des nächsten überprüft wird, kann maximal ein Gerät zerstört werden. Falls nötig, kann es auf die Lage vor der Aktualisierung zurückgesetzt werden. Während dieser Zeit halten die anderen Server den Dienst verfügbar. Bei uns geschieht all dies automatisch in unserem auf GitLab basierenden ERP-System.

Die Mathematik der Verfügbarkeit

Die Verfügbarkeit eines Dienstes kann als die Wahrscheinlichkeit beschrieben werden, dass der Dienst zu einem beliebigen Zeitpunkt verfügbar ist. Wenn der Dienst auf n Geräten dupliziert wird, die unabhängig voneinander ausfallen, sodass die Wahrscheinlichkeit, dass ein Gerät zu einem beliebigen Zeitpunkt unverfügbar ist, p beträgt, ist die Wahrscheinlichkeit, dass der gesamte Dienst ausfällt, also dass kein Gerät reagiert, pⁿ.

Zum Beispiel, wenn man auf eine Verfügbarkeit von 99,9999 % zielt, also pⁿ=10⁻⁶, darf der Dienst maximal 86,4 Millisekunden pro Tag (86.400 Sekunden) ausfallen.

Bei einem verdoppelten System wird dieses Verfügbarkeitsziel erreicht, wenn die Ausfallwahrscheinlichkeit in einem einzelnen Gerät p=10⁻³ beträgt, d. h. das Gerät ist maximal 86,4 Sekunden pro Tag nicht verfügbar. Dies reicht für das Ziel aus, wenn der schlimmste Fehler eine Unterbrechung der Netzwerkverbindung von weniger als einer Minute oder ein geplantes Systemupdate nicht mehr als einmal täglich ist.

In einem Cluster von drei Systemen wird das Verfügbarkeitsziel erreicht, wenn p=10⁻², d. h. Ausfälle für ein einzelnes unabhängiges Gerät nicht länger als 14 Minuten und 24 Sekunden pro Tag dauern.

Wie sieht es mit größeren Ausfällen aus, deren Behebung Stunden oder Tage benötigt? Angenommen, dass höchstens ein oder zwei solche unwahrscheinliche Ausfälle gleichzeitig auftreten können, kann einfach eine entsprechende Anzahl unabhängiger Ersatzgeräte hinzugefügt werden.

Größere Ausfälle, wie der Verlust der Backbone-Netzwerkverbindung oder ein Brand in einem Rechenzentrum, werden durch den Einsatz eines Ersatzservers aus einem anderen Rechenzentrum behoben. Das erfordert menschliche Arbeit. Im Falle eines solchen Ausfalls liegt die Verfügbarkeit eines einzelnen Geräts höchstens bei p=10⁻¹ (2,4 Stunden pro Tag), und das Verfügbarkeitsziel würde tatsächlich den Einsatz von mindestens n=6 Servern in unabhängigen Rechenzentren erfordern.

Einfache Dezentralisierung mit zentraler Kontrolle

Ein Dienst lässt sich leicht duplizieren, wenn er auf selten veränderten Inhalten basiert. Zum Beispiel werden die Software und Zertifikate für unsere Nameserver alle drei Tage aktualisiert. Außerdem können sich die Inhalte beim Bedarf ändern, meist seltener.

Ebenso kann man einen Validierungsdienst für Zertifikate oder einen Webserver duplizieren, der statische Inhalte bereitstellt. Aber wie sieht es mit Diensten aus, deren Inhalte sich während der Nutzung ändern?

Replikation von Änderungen während der Nutzung

Im E-Mail-Dienst ändern sich die Inhalte (Benutzer-Nachrichtenordner) während der Nutzung. Allerdings basieren SMTP-Server auf Nachrichtenwarteschlangen, die im Grunde redundant gestaltet sind. Eingehende Nachrichten können auf mehrere Server dupliziert werden, noch bevor der Absender die SMTP-Verbindung schließen darf. Jede Nachricht hat eine eindeutige Kennung Message-Id. Typischerweise sind Postfächer persönlich, und ein einzelnes Postfach wird nicht gleichzeitig auf mehreren verschiedenen Geräten bearbeitet. Nachrichtenordner, die auf verschiedene Server dupliziert werden, könnten durch Sperren und Protokolleinträge aktualisiert werden, also durch die Implementierung eines einfachen verteilten Datenbanksystems. Die Verwaltung ist recht einfach, wenn Änderungen an einem einzelnen Ordner von höchstens einem Server oder Terminalgerät gleichzeitig erlaubt sind.

In einem Online-Marktplatz oder Buchungssystem können Nutzer gleichzeitig die geteilten Inhalte ansehen und bearbeiten. Wenn es nur eine begrenzte Anzahl von viralen Produkten, Hobbypässen oder Sitzplätzen gibt, sollten alle Nutzer die aktuelle Situation sehen und ihre Wahl reservieren können, bis die Transaktion einschließlich eines möglichen Bezahlvorgangs bestätigt oder storniert ist. Das Gehirn einer solchen Anwendung ist oft ein Datenbanksystem, das Transaktionen und Sperren abwickelt.

Eine Anwendung, die eine zentralisierte Datenbank nutzt, kann dezentralisiert werden, indem das Datenbanksystem auf ein dezentrales umgestellt wird. Eine gut aufgebaute Anwendung kann schon mit zurückgerollten Transaktionen zurechtkommen, wenn zum Beispiel ein Deadlock auftaucht oder die Wartezeit auf eine Sperre überschritten wird.

Verteilte Systeme mit mehreren beschreibbaren Knoten

Es ist noch relativ einfach, ein zentrales System aufzubauen und zu verwalten, wo alle Änderungen durch eine primären Server vorgenommen werden, der dann sie auf Replikate überträgt, die den primären Server entlasten können, indem sie Lesevorgänge übernehmen.

Was nicht einfach ist, ist ein Failover zu verwirklichen, also das Befördern eines Replikats zum primären Server. Was wäre wenn der alte primäre Server teilweise erreichbar bleibt und einige Benutzer ihn weiterhin nutzen, während andere auf einen neuen Server umgeschaltet haben?

Ein verteiltes System darf nie in eine Lage eines geteiltes Gehirns gelangen, wo die Anwender voneinander getrennte primäre Server nutzen und sich einbilden, dass alle die gleiche Ansicht teilen. Wenn ein verteiltes Datenbanksystem erwartet, dass jede Operation von allen Servern bestätigt wird, bestimmen die langsamsten Verbindungen die Geschwindigkeit. Gute Leistung erfordert das Puffern von Änderungen und Mehrheitsentscheidungen sowie eine schnelle Reaktion auf Störungen.

Ein wirklich verteiltes System, wo die Dienstknoten des Clusters ihre Rolle schnell anpassen können, setzt einen verteilten Wahlalgorithmus voraus, wodurch der Koordinator bzw. primäre Knoten festgestellt wird. Darüber hinaus werden eine effiziente verteilte Sperrverwaltung und ein Wiederherstellungsmechanismus benötigt. Wenn ein ehemaliger Koordinator seine Verbindung zum Cluster wiederherstellt und herausfindet, dass mittlerweile ein neuer Koordinator gewählt wurde, muss er entweder seine lokalen Änderungen zurückrollen oder replizieren, nachdem er die neuesten Änderungen von den anderen übernommen hat. Koordinatorwahlen können häufig stattfinden, und Störungen sind auch während der Wahl möglich.

In einem verteilten System können Ereignisse in sehr vielen unterschiedlichen Reihenfolgen stattfinden. Eigentlich ist eine strenge Totalordnung der Ereignisse nicht garantiert, denn verschiedene Knoten, jeder mit einer eigenen Uhr, kann die Ereignisse in unterschiedlichen Reihenfolgen beobachten. Außerdem werden Aktualisierungen komplizierter, denn während einer Aktualisierung werden unterschiedliche Softwareversionen gleichzeitig im Einsatz sein.

Die Entwicklung von robusten verteilten Systemen benötigt formale Methoden, wie Korrektheisbeweise und Verifizierung durch Model Checking und Erreichbarkeitsanalyse. Um die Explosion des Zustandsraums im Griff zu halten, muss eine passende Stufe der Abstraktion gefunden werden. In einer Doktorarbeit war einer von uns damit beschäftigt.

Zur Zeit schätzen wir die Aufwand jeglicher Replizierung einzusetzen größer als den dadurch erhältlichen Nutzen. Wir würden lieber etwas mehr Mühe bei einem Ausfall machen als das Risiko eingehen, dass ein Failover-Mechanismus einen Fehler verursacht. Vorfälle sind einfacher zu behandeln, wenn die Welt hält. Die sichtbare Welt dreht sich natürlich weiter, in voll isolierten Produktionsumgebungen nach unserer verschachtelten Sicherheitsarchitektur.

Um Ausfälle auf einem einzelnen Gerät vorherzusagen und zu verhindern, entwickelt unsere Zuverlässigkeitsüberwachung Vorschläge für Maßnahmen.

CC BY 4.0