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.