Qualität von Webanwendungen - Teil 11: Wir sind immer für Sie da

5 Methoden, wie Sie Websysteme hochverfügbar machen

Nicolas Bär, Daniel Takai

@shutterstock/MaximP

Websysteme sind komplex und anfällig gegen unterschiedliche Störungen. Dennoch ist Hochverfügbarkeit ein häufig gewünschtes Qualitätsmerkmal, insbesondere im E-Commerce. Eine hohe Verfügbarkeit bedeutet jedoch auch hohe Kosten im Betrieb und der Produktion eines Websystems. In diesem Artikel beschreiben wir, wie sich die Verfügbarkeit definieren und berechnen lässt, und einige Methoden, um sie zu verbessern.

Verfügbarkeit gehört zum Qualitätsmerkmal der Widerstandsfähigkeit eines Systems (Resiliency). Tatsächlich wird sie in vielen Fällen als Synonym betrachtet [1]. In Kombination mit der Diskussion um Prüfbarkeit (Monitoring) und Wiederherstellbarkeit (Recoverability) ist es dem Webarchitekten möglich, verschiedene Maßnahmen zu definieren, die eine höhere Uptime eines funktionierenden Systems ermöglichen. Für uns bedeutet Resiliency, dass möglichst viele Störungen eines Systems bekannt und wir dagegen gewappnet sind. Die Konzepte rund um die Verfügbarkeit sowie das Zusammenspiel mit der Resiliency zeigt Abbildung 1.

Abb. 1: Konzepte rund um die Verfügbarkeit

Abb. 1: Konzepte rund um die Verfügbarkeit

Es gibt verschiedene Gründe, warum eine Komponente in unserem System versagen kann. Nebenläufigkeitsprobleme kommen häufig vor, denn Deadlocks, Race Conditions, Livelocks und Starvations sind schwierig mit Testfällen nachzustellen. Generell hat das Timing-Verhalten in verteilten Systemen einen Einfluss auf das Verhalten seiner Komponenten. Denken Sie nur an einen überlasteten Service, der nur wenige KB pro Minute liefert, und extrapolieren Sie dies auf das Verhalten Ihrer Anwendung.

Auch Designfehler können problematisch sein: Der falsche Entwurf eines Systems kann ein System zum Absturz bringen oder es so langsam machen, dass es sich schlicht nicht für die angedachte Aufgabe einsetzen lässt. Designfehler in der Architektur sind leicht zu finden, aber sehr schwierig – da kostspielig – zu beheben, denn die Behebung erfordert ein Redesign des Systems. Denken Sie an eine Spaghettiarchitektur, die nicht mehr beherrschbar ist, und führen Sie dann nachträglich eine Middleware ein. Die Kosten hierfür sind höher, als wenn die Middleware von Anfang an mit dabei gewesen wäre. Designfehler entstehen durch Fehler in der Erhebung und Analyse der gewünschten Qualitätsmerkmale und deren Abbildung auf die Architektur eines Systems.

Auch Überlast kann ein Softwaresystem zum Absturz bringen. Wenn die benötigte Kapazität die verfügbare Kapazität sprengt, wird sich das System erratisch verhalten. Langsame Antwortzeiten sind dann nur ein Symptom der Überlast. Eine bekannte Ursache für Überlast sind Denial-of-Service-Angriffe.

Sneaks [2] sind Fehler, die in bestimmten Zuständen eines verteilten Systems zu Problemen führen. Nicht alle Zustände eines Systems lassen sich aufgrund der großen Anzahl möglicher Kombinationen testen. Zudem bestehen verteilte Systeme aus verschiedenen Komponenten, die aus praktischen Gründen nicht in ihrer Produktivkonfiguration vorab getestet werden können. Eine interessante Frage ist, welche Maßnahmen der Prüfbarkeit oder des Monitorings getroffen werden müssen, um Sneaks zur Laufzeit erkennen zu können.

Varianz in der Laufzeitumgebung kann ein System kompromittieren. Sie entsteht beispielsweise durch das Versagen oder den Verschleiß physischer Komponenten unseres Systems. In einer Cloud haben wir keine Kontrolle über die unmittelbare Laufzeitumgebung und wir leiden am Noisy-Neighbor-Syndrom. Auch können wir nicht verhindern, dass unser Cloud-Provider mehr Kapazität verkauft, als verfügbar ist. Wir müssen damit rechnen, dass unsere Komponenten ihr Antwortverhalten plötzlich ändern. Eine Änderung am Laufzeitverhalten kann wiederum Nebenläufigkeitsprobleme zutage fördern, die auf der Testumgebung nicht auftreten. Uns ist mal ein Deadlock auf Produktion untergekommen, der nur auftrat, wenn das Logging eingeschaltet war.

Auch funktionale Fehler können ein System kompromittieren. Dabei müssen sie nicht unbedingt von einem Entwickler gemacht worden sein, es kann auch sein, dass falsche oder widersprüchliche Anforderungen zu solchen Fehlern führen. Bei Websystemen mit geringen Budgets für die Benutzerschnittstelle kann auch eine Fehlbedienung durch den Benutzer einen Absturz verursachen.

Methode 1: Verfügbarkeit definieren

Verfügbarkeit ist die Fähigkeit eines Systems, Fehler zu verbergen oder zu reparieren, sodass die kumulative Dauer eines Ausfalls nicht einen benötigten Wert über einem Zeitintervall übersteigt [3]. Es geht also darum, Ausfallszeiten möglichst zu minimieren. Der Grund für einen Ausfall (Outage) ist eine Störung (Fault). Störungen können vermieden, toleriert, behoben oder vorhergesagt werden. Je besser ein System in der Behandlung von Störungen wird, desto widerstandsfähiger wird es. Die Unterscheidung zwischen Ausfall und Störung erlaubt es uns, automatische Reparaturen sinnvoll ins Feld zu führen. Wenn Code eine Störung enthält, wir diese Störung aber automatisch erkennen und reparieren können, dann entsteht kein Ausfall.

Die Verfügbarkeit wird häufig als Prozentzahl angegeben, zum Beispiel 99,5 Prozent, die angibt, wie wahrscheinlich es zu einem gewissen Zeitpunkt ist, dass das System noch funktioniert. Tabelle 1 gibt einen Überblick über die verschiedenen Klassen von Verfügbarkeit. Hochverfügbarkeit meint üblicherweise five Nines, also 99,999 Prozent. Die Verfügbarkeit bestimmt sich aus der folgenden Formel [2]: V = MTBF / MTBF + MTTR.

Dabei bezeichnet MTBF die Mean Time Between Failures und MTTR steht für Mean Time To Repair. Fällt ein System beispielsweise monatlich aus (alle 720 h) und die Widerherstellung benötigt vier Stunden, so ergibt sich für die Verfügbarkeit V = 720 / 720 + 4 = 99,45 %.

Logischerweise gehen alle Ausfälle eines Systems in die tatsächliche Verfügbarkeit ein, wobei man generell zwischen geplanten und ungeplanten Ausfällen unterscheidet. Da die Stakeholder eines Systems oft eine sehr hohe Verfügbarkeit fordern und diese auch gerne vertraglich festhalten möchten, sollte man genau definieren, welche Ausfälle in die Messung der Verfügbarkeit einfließen. Hierfür lassen sich Service Level Indicators definieren (SLI) und mit etwas Erfahrung Service Level Targets (SLT), also gewünschtes Minimum und Maximum eines SLIs. Zu einem SLI gehört eine exakte Definition der Metriken, der Messfrequenz sowie der Perspektive, aus der gemessen wird. In einem Service Level Agreement (SLA) können SLIs und SLTs vertraglich festgelegt werden. Oft enthält ein SLA auch Strafen, falls die SLTs nicht eingehalten werden, z. B. zehn Prozent Discount bei einer Unterschreitung von fünf bis zehn Prozent. Da während der Entwicklung eines Systems noch keine Daten über den Produktivbetrieb vorliegen, kann die vertragliche Verankerung von vernünftigen SLAs im Vorfeld schwierig sein. Deswegen sollten SLAs, die Strafen enthalten, besser nur auf der Grundlage von handfesten Daten geschlossen werden.

Denken Sie auch daran, dass die Messungen und Zusammenstellung der Indikatoren für den Nachweis der Einhaltung aufwendig sein können. Ist eine hohe Verfügbarkeit in Argentinien gefordert, so muss auch ein Indikator in Argentinien gemessen werden, d. h. Sie müssen dort einen Server für die Messung installieren. Wir haben bei uns hierfür auf allen Kontinenten der Erde einen Server installiert, der jedes von uns betriebene System misst. Damit erhalten wir wertvolle Daten über Performance und Verfügbarkeit und können bestimmte SLIs preiswert nachweisen. Bei Einsatz eines Content Delivery Networks (CDN) kann bei Messung von Edge und Origin Server sogar der Nutzen desselben bewiesen werden.

Generell sollte bei vertraglicher Fixierung die geplante Ausfallzeit keinen Einfluss auf die Verfügbarkeitsberechnung haben, da diese Ausfallzeiten zum Beispiel durch Deployments fremdgesteuert sind und Sie keinen Einfluss darauf haben. Denken Sie auch daran, dass Sie beim Hosting in fremden Rechenzentren oder in der Cloud oft keine Verfügbarkeitsvereinbarung gegenüber dem Betreiber geltend machen können und kalkulieren Sie das Risiko entsprechend. Die Prognose der Verfügbarkeit ist viel schwieriger als ihre nachträgliche Berechnung. Dies gilt auch für externe Dienste, die integriert werden sollen.

Verfügbarkeit Ausfallzeiten pro Quartal Ausfallzeiten pro Jahr
99 % 21,6 Stunden 3,65 Tage
99,5 % 10,8 Stunden 1,83 Tage
99,9 % 2,16 Stunden 8,76 Stunden
99,99 % 12,96 Minuten 52,6 Minuten
99,999 % 1,3 Minuten 5,26 Minuten

Tabelle 1: Verfügbarkeit nach Google [4]

Methode 2: Maintenance Page

Die Hauptursache für Ausfälle sind in vielen Fällen Deployments. Solange das System aktualisiert wird, kann es nicht benutzt werden. Anstatt Requests eines Benutzers in einen hässlichen Time-out laufen zu lassen, schaltet man hierfür eine so genannte Maintenance Page (Wartungsseite) auf, die den Benutzer freundlich und konform zur Corporate Identity auf diesen Umstand aufmerksam macht. An das Design und den Prozess zur Aktivierung dieser Seite sollte man stets denken, es sei denn, Sie planen unterbrechungsfreien Betrieb auch bei Deployments, z. B. via Continuous Delivery [5] .

Methode 3: Reliability Mathematics

Um die Widerstandsfähigkeit eines Systems quantifizieren zu können, benötigen wir Wahrscheinlichkeitsrechnung und Statistik [2]. Wir können nicht wissen, wann ein System versagt, aber wir können versuchen, die Wahrscheinlichkeit davon zu bestimmen. Sammeln wir Daten über tatsächliche Ausfälle von Systemkomponenten, so können wir daraus eine Statistik erstellen, die unsere Annahmen über die Wahrscheinlichkeit stützt. Insbesondere können wir statistische Methoden verwenden, um die Varianz einer Umgebung zu bestimmen. In Cloud-Umgebungen kann die Varianz der Umgebung unserer Maschinen hoch sein (Noisy-Neighbor-Problem). Die Culling-Methode, bei der eine große Anzahl virtueller Maschinen gestartet und gemessen wird und dann alle bis auf die schnellste Maschine wieder entsorgt werden, ist ein Beispiel für den Einsatz statistischer Methoden zur Verbesserung der Qualität eines Systems.

Die Wahrscheinlichkeit, dass ein Ereignis eintritt, bezeichnen wir mit P(A). Sind zwei Ereignisse unabhängig, so beträgt die Wahrscheinlichkeit, dass beide gleichzeitig eintreten, nach der Produktregel P(AB) = P(A) * P(B).

Angenommen, wir haben ein System mit zwei aktiven Knoten, die Seiten ausliefern: Knoten A und B. Die Verfügbarkeit eines Knotens legen wir für dieses Beispiel arbiträr fest: P(A) = 95 % = 0,95 = P(B). Wie groß ist nun die Wahrscheinlichkeit, dass das gesamte System noch am Leben ist? Das Komplement der Wahrscheinlichkeit eines Ereignisses wird folgendermaßen notiert: P(A) = 5% = 0.05. Die Wahrscheinlichkeit, dass beide gleichzeitig ausfallen ist demnach P(AB) = P(A)P(B) = 0.05 ∙ 0.05 = 0,0025 = 0,25%.

Wenn wir also zwei Knoten mit 95 Prozent Verfügbarkeit verwenden, steigt die Verfügbarkeit des gesamten Systems auf 97,5 Prozent. Nicht schlecht! Allerdings benötigen wir noch einen Load Balancer (L), der eingehende Anfragen auf beide Knoten verteilt. Unser Load Balancer hat eine Verfügbarkeit von 99 Prozent. Wie hoch ist nun die Gesamtverfügbarkeit? Wir nennen den Verbund von Knoten A und B im Folgenden K. Die Wahrscheinlichkeit, dass nun entweder L oder K ausfallen, berechnet sich so: P(L+K) = P(L) + P(K) – P(LK) = 0.01 + 0.25 – (0.01 ∙ 0.25) = 2.575%

Die Gesamtverfügbarkeit wäre demnach 97,425 Prozent (Abb. 2). Dadurch, dass wir eine weitere Schicht hinzufügen, sinkt also die Verfügbarkeit. In einem Schichtensystem von Komponenten senkt jede Schicht die Verfügbarkeit des gesamten Systems, da keine Schicht eine Verfügbarkeit von 100 Prozent oder mehr haben kann.

Abb. 2: Rechenbeispiel für Verfügbarkeit

Abb. 2: Rechenbeispiel für Verfügbarkeit

Methode 4: Komponentenverfügbarkeit verbessern

Die Verfügbarkeit einer Komponente bestimmt sich aus der physischen Laufzeitumgebung in Kombination mit der Qualität der Software. Physische Komponenten versagen auf gänzlich andere Weise als Software. Strom kann ausfallen oder der Serverschrank umkippen, es gibt Erdbeben und Wasserrohrbrüche. Kurz, die Möglichkeiten sind endlos. Aber sicher ist, dass Hardware versagt. Und ein einzelnes physisches System kann nie eine hundertprozentige Verfügbarkeit haben. Die Verfügbarkeit eines physischen Systems kann durch den Einkauf teurer Hardware erhöht werden, die eine längere und verlässlichere Lebensdauer hat.

Bezüglich der Software sprechen wir der Einfachheit halber vom gesamten Softwarestack einer Maschine. Wir gehen davon aus, dass dieser Stack in Gänze getestet wurde. Software fällt aufgrund von Fehlern in der Entwicklung aus und Vorhersagen von Verfügbarkeit basieren auf der Menge von gefundenen Fehlern [2]. John Musa hat das Musa-Modell [6] entworfen, bei dem aus den bei einem Testlauf gefundenen Fehlern bis zu einem Testabbruch die notwendige Zeit für weitere Tests zur Erreichung einer bestimmten Verfügbarkeit abgeleitet werden kann. Wir finden das hochinteressant, aber praktisch schwierig umzusetzen, da das Modell von richtigen Eingaben abhängt und einen bestimmten formalen Prozess im Testing voraussetzt.

Trotzdem gibt uns Musa einen wichtigen Hinweis, nämlich, dass die Verfügbarkeit von funktionalen Fehlern im System abhängig ist. Angenommen, wir haben unsere Defects nach Blocker, Critical, Major und Minor kategorisiert. Ein kritischer Fehler erfordert in unserem System ein neues Release. Für ein neues Release benötigen wir eine Stunde Downtime. Wir finden jede Woche zwei kritische Fehler, also haben wir zwei Stunden Downtime pro Woche. Nun können wir über die Variablen Zeit pro Release und Anzahl kritischer Fehler die Verfügbarkeit steuern. Für zwei Stunden Ausfallzeit ergibt sich eine Verfügbarkeit von 98,81 Prozent für die Software. In Kombination mit der Hardwareverfügbarkeit von 99,999 Prozent kommen wir dann auf P(S+H)=P(S)+P(H)–P(SH)=1.19+0.001–(1.19∙0.001)=98.81019%.

Offenbar hat die Hardware also nur einen sehr geringen Einfluss auf die Verfügbarkeit, weswegen wir sie auch ignorieren können und uns nur auf die Qualität unserer Software konzentrieren können. Wie wir gesehen haben, können wir nun die Anzahl kritischer Fehler reduzieren oder die Deployment-Zeit verkürzen. Um dies zu erreichen, wählen wir einen iterativen Verbesserungsprozess, um schrittweise die Perfektionierung unseres Produkts zu erreichen [7]. Dies folgt der Kaizen-Philosophie, die in Japan nach dem zweiten Weltkrieg eine Qualitätsrevolution begleitete. Wir analysieren also im Team unseren Prozess und die Qualität und kommen so zu stetigen kleinen Verbesserungen, die am Ende zu weniger Fehlern führen.

Methode 5: Load Balancing Strategies

Load Balancer sind essenzielle Komponenten einer hoch verfügbaren Umgebung. Sie stellen sicher, dass die Last von eingehenden Anfragen auf verschiedene Systeme verteilt wird und dass Anfragen nicht an überlastete Nodes weitergeleitet werden. Die Nodes innerhalb eines Load Balancers werden Pool genannt. Für jeden Node im Pool muss stetig geprüft werden, ob die Kapazität ausgeschöpft ist. Dazu eignet es sich, eine spezifische Health-Check-Seite in der Webapplikation zu bauen, die einen Systemcheck durchführt. Wenn der Health Check negativ ausfällt, kann der Node aus dem Pool entfernt werden.

Es können verschiedene Strategien angewendet werden, um die Last auf die Nodes im Pool zu verteilen. Round Robin ist eine verbreitete Methode, die auch bei DNS eingesetzt wird. Anfragen werden zirkulierend an Nodes im Pool verteilt. Die Kapazität der einzelnen Nodes wird dabei nicht berücksichtigt, und Anfragen können unfair verteilt werden. Weighted Round Robin adressiert dieses Problem und gibt den Nodes im Pool eine Gewichtung. Nodes mit mehr Kapazität (durch bessere Hardware) werden höher gewertet. Die Anfragen werden dann anhand der Gewichtung an die Nodes im Pool verteilt. Least Connections zählt die offenen Verbindungen zu den Nodes und leitet eingehende Anfragen an den Node mit den wenigsten Verbindungen weiter. Ähnlich der Round-Robin-Methode kann Weighted Least Connections die Kapazität der einzelnen Nodes berücksichtigen. Least Response Time misst die durchschnittliche Antwortzeit der Nodes im Pool und leitet neue Anfragen an den Node mit der besten Zeit und den wenigsten offenen Verbindungen. Diese Methode berücksichtigt die Latenz zum Zielsystem. Entscheidend für die Wahl der Load-Balancing-Methode sind vor allem zwei Faktoren: die Kapazität der einzelnen Systeme und die Latenz zwischen den Systemen.

Wenn eine Webapplikation Session-relevante Daten im Memory von einem Node speichert, z. B. den Warenkorb eines Benutzers, muss der Load Balancer sicherstellen, dass alle Requests von diesem Benutzer an den gleichen Node weitergeleitet werden. Dies wird Sticky Sessions genannt und als Session-Cookie implementiert. Sticky Sessions sind mit Nachteilen verbunden. Einerseits kann eine unfaire Lastverteilung entstehen, da die Load-Balancing-Methode für alle Anfragen von einem Benutzer umgangen wird. Andererseits gehen Daten verloren, wenn ein Node aus dem Pool entfernt wird. Queue Draining kann verwendet werden, um einen Node langsam aus dem Pool zu entfernen. Es werden keine neuen Sessions mehr auf dem Node erstellt, und die bestehenden Verbindungen laufen langsam aus. Wenn ein Node aus dem Pool entfernt wurde, um z. B. ein Canary Deployment durchzuführen, sollte der Node langsam wieder in den Pool aufgenommen werden. Das heißt, die Last auf dem Node sollte iterativ vergrößert werden (Slow Ramp-up), da ein Big Bang oft zu Ausfällen führen kann, da z. B. ein Cache noch nicht warm ist.

Qualitätsszenarien

Um Maßnahmen zugunsten der Verfügbarkeit kommunizieren zu können, bieten sich folgende Qualitätsszenarien an. Qualitätsszenarien zur Verfügbarkeit drehen sich meistens um die Entdeckung von Störungen sowie gewünschte Ausfallzeiten des Systems. Ein paar Beispiele:

  • Eine Störung in einem Modul soll zur Laufzeit durch einen Eintrag im Log der Komponente gemeldet werden, damit der Logmonitor diese Störung an den Service-Operator melden kann.
  • Wird auf der Produktionsumgebung die Software aktualisiert, so kann der Service-Operator vorher auf dem Load Balancer manuell eine Wartungsseite schalten, die den Designvorgaben der Kommunikationsabteilung entspricht.
  • Ausfälle während der normalen Geschäftszeiten zwischen 7 und 20 Uhr von montags bis samstags sind unerwünscht, weswegen geplante Ausfälle außerhalb dieser Zeitfenster stattfinden sollten.

Fazit

Wir haben gesehen, dass sich Verfügbarkeit theoretisch berechnen lässt. Jedoch sind solche Prognosen schwierig, denn Softwaresysteme sind komplex und lassen sich nicht auf wenige Variablen reduzieren. Je nachdem, wie kritisch ein System ist, sollte mehr in die Sicherstellung von Verfügbarkeit investiert werden. Ist eine Unternehmung auf ihren Webshop angewiesen, so ist sie gut beraten, wenigstens einen zweiten Knoten für die Shopkomponente zu finanzieren. Dies erhöht auch die Kapazität und kann die Performance des Shops verbessern, und das steht in direkter Korrelation zum Erfolg des Systems.

Verwandte Themen:

Geschrieben von
Nicolas Bär
Nicolas Bär
Nicolas Bär arbeitet als IT-Architekt bei der Unic AG in Zürich. Als Performanceenthusiast optimiert er Webapplikationen und engagiert sich in seiner Freizeit im Open-Source-Umfeld.
Daniel Takai
Daniel Takai
Daniel Takai ist Enterprise-Architekt, arbeitet in der Schweiz und ist auf die digitale Transformation spezialisiert. Er berichtet regelmäßig im Java Magazin über seine Erfahrungen und Erkenntnisse. Im Frühling 2016 erscheint sein Buch über Methoden der Entwicklung von Cloud-basierten und serviceorientierten Websystemen.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: