Qualität von Websystemen - Teil 12

Zurück an den Anfang: Wiederherstellbarkeit von Websystemen

Daniel Takai

©istock/Devenorr

Die Recoverability bezeichnet die Wiederherstellbarkeit eines Websystems. Wie man diese automatisiert, um in möglichst kurzer Zeit wieder ein verfügbares System zu erhalten, erfahren Sie in diesem Artikel. Die Geschwindigkeit der Wiederherstellung bestimmt im Wesentlichen die Verfügbarkeit, da ein System nun mal nicht verfügbar ist, solange es wiederhergestellt wird.

Früher unterschied man noch zwischen Erneuerbarkeit und Wiederherstellbarkeit. Beim physischen Versagen von Komponenten war es wichtig, dass Ersatzteile schnell bei der Hand waren. Im eigenen Rechenzentrum sind die Maschinen teuer und werden teilweise individuell gepflegt, um Investitionen zu schützen. In der Cloud ist diese Denkweise ein Anachronismus. Hier ist die Wiederherstellbarkeit identisch mit der Erneuerbarkeit eines Systems, da hier jeweils eine gänzlich neue virtuelle Maschine per API bereitgestellt wird. Um die Hardware muss man sich nicht mehr kümmern. Die Bereitstellung von frischen, neuen virtuellen Maschinen mit wohldefinierter Konfiguration über solche APIs ist die Basis effizienter Wiederherstellbarkeit und damit das Thema dieses Artikels.

Abb. 1: Recoverability im Zusammenhang mit der Widerstandsfähigkeit

Abb. 1: Recoverability im Zusammenhang mit der Widerstandsfähigkeit

Abbildung 1 zeigt das Qualitätsmerkmal im Zusammenspiel mit der Widerstandsfähigkeit (Resiliency), der Sicherheit sowie der Skalierbarkeit. Die Wiederherstellbarkeit ist einer der drei wesentlichen Bausteine, die Widerstandsfähigkeit möglich macht. Da Wiederherstellbarkeit in Cloud-Umgebungen identisch mit der Erneuerbarkeit ist, beeinflusst sie zudem die dynamische Skalierbarkeit eines Systems. In der Cloud können wir genau dann dynamisch skalieren, wenn wir neue virtuelle Maschinen automatisch provisionieren können. Schließlich beeinflusst die Integrität von Daten nach einer Wiederherstellung auch die Sicherheit. Einige Autoren, wie Thomas Limoncelli [1], nennen die erwähnten Konzepte schlicht Operational Requirements, aber nicht im Sinne von Anforderungen, sondern im Sinne von Notwendigkeiten. Tatsächlich ist ohne den konsequenten Einsatz dieser Methoden ein effizienter und qualitativ hochwertiger Betrieb schwierig. Abbildung 2 zeigt die Konzepte rund um die Wiederherstellbarkeit, von denen wir einige in diesem Artikel beschreiben.

Abb. 2: Konzepte der Wiederherstellbarkeit

Abb. 2: Konzepte der Wiederherstellbarkeit

Services automatisieren

Effiziente Wiederherstellbarkeit kann durch die Abschaffung von Schufterei (Elimination of Toil) und die Einführung von Service Automation gelingen. Die Durchführung von repetitiven, komplizierten Arbeitsabläufen durch Menschen ist fehleranfällig. Ein Vorteil der Automatisierung ist, dass Prozesse jedes Mal gleich ausgeführt werden und deswegen keine Fehler passieren können. Repetitive, komplizierte Arbeitsabläufe sind außerdem für den Menschen unangenehm, da ihm bewusst ist, dass Fehler passieren können. Weil im Betrieb die Auswirkungen von Fehlern groß sein können, verursacht dies Stress, und Stress fördert kein angenehmes Arbeitsklima. Im Umkehrschluss bedeutet dies, dass die Abschaffung von Schufterei im Servicesupport und Engineering [2] sowohl die Qualität erhöht als auch die Arbeit angenehmer macht.

Ein Server, der manuell gepflegt wird, sammelt über Monate und Jahre so genannten Cruft (Müll) an. Cruft sind spezielle Konfigurationen und Softwarepakete, die im Laufe der Zeit individuell eingespielt wurden, beispielsweise für Bugfixes oder Patches. Cruft führt zu Mehraufwänden bei der Administration, da ein solches System schwieriger zu konfigurieren ist und mehr Dokumentation benötigt. Ein Server, der nicht automatisch konfiguriert wird, d. h., dessen Konfiguration nicht in einem Konfigurationsmanagementsystem verwaltet wird, heißt SnowflakeServer.

Die Einführung einer vollautomatischen Systemkonfiguration ist aufwendig, komplex sowie riskant und sollte aus diesen Gründen strategisch im Rahmen der Service Strategy geplant werden. Die Prämisse hierfür ist, dass alle durchgeführten Prozesse einen Value Stream darstellen [1], dessen Flow Rate die Effizienz, d. h. die Kosten bestimmen. Die Investition in Automatisierung ist deswegen eine Investition in Effizienz und Qualität und steigert den Erfolg. Wie immer bei komplexen Unterfangen, bietet sich dabei eine Vorgehensweise in Iterationen an, um das Risiko möglichst gering zu halten. Die folgenden Schritte können eine Organisation iterativ an das gewünschte Ergebnis heranführen.

Zu Beginn sollten alle vom Betrieb durchgeführten Prozesse einheitlich dokumentiert werden. Jede Dokumentation sollte bestimmten Qualitätskriterien genügen, damit sie nützlich sein kann: Sie sollte in logische Schritte zerlegt, nachvollziehbar und wiederholbar sein. Es bietet sich zudem an, für jeden Prozess einen Eigentümer festzulegen, der bei Rückfragen zur Verfügung steht. Bietet die Organisation verschiedene Services an (z. B. ein Versionskontrollsystem), so sind die Prozeduren mit den Services in Beziehung zu setzen (z. B. Update des Versionskontrollsystems). Um dies zu erreichen, benötigt man eine Dokumentationsplattform für die Prozeduren und muss sicherstellen, dass auch alle Kollegen damit arbeiten. Schlussendlich möchte man beurteilen können, welche Prozeduren am häufigsten ausgeführt werden, d. h. in welchen am meisten Geschäftswert steckt. Dies kann man beispielsweise durch eine Verknüpfung des Ticketmanagements mit der Prozessdokumentation erreichen, bei der man die Anzahl der Tickets auswerten kann.

Ist die Dokumentation der Arbeitsabläufe auf einem guten Weg, beginnen wir mit der Erstellung von Skripten zur Automatisierung, zunächst von einzelnen Schritten. Hier sind viele Detailfragen zu klären. Von der Netzwerktopologie über die Versionierung der Skripte bis hin zur Auswahl von Skriptsprachen reicht das Spektrum, damit von jedem Arbeitsplatz aus funktionierende Skripte entwickelt und angestoßen werden können.

Funktionieren unsere Skripte, können wir sie zu ganzen Prozeduren zusammenfassen. Eine automatisierte Prozedur heißt auch Runbook oder Playbook. Hierfür gibt es eigene Softwarelösungen, die sich auf die Ausführung von Runbooks spezialisiert haben, ein eigenes API anbieten und diese Aufgaben auch zeitgesteuert ausführen können. Notabene ist eine solche Software wiederum ein Service, der Kosten und Ressourcen im Betrieb benötigt, dafür aber die erwähnten Qualitäts- und Effizienzgewinne realisieren kann. Eine funktionierende Automationsarchitektur erlaubt uns außerdem die Entwicklung von Skripten für die Wiederherstellung ganzer Services.

Abbildung 3 zeigt die Bausteine einer Automationsarchitektur, in der exemplarisch Stackstorm als zentrale Plattform eingesetzt wird. Üblicherweise führt Stackstorm Skripte auf den Services via SSH aus. Die Bausteinsicht zeigt aber auch eine mögliche Integration entfernter Services über ServiceMix. Prozesse werden entweder per Chat vom Servicepersonal oder über ServiceMix von anderen Systemen angestoßen. Die Kommunikation im Beispiel geschieht via Slackstorm. Die Praxisprozesse per Chat zu steuern heißt ChatOps.

Abb. 3: Eine mögliche Automationsarchitektur

Abb. 3: Eine mögliche Automationsarchitektur

Image ist alles

Nachdem wir nun unterstützende Strategien zur Automatisierung von Services besprochen haben, kommen wir nun zu den Services selbst, genauer den Maschinen, auf denen unsere Services betrieben werden sollen. Die Fähigkeit zur automatischen Bereitstellung virtueller Maschinen ist Voraussetzung der effizienten Wiederherstellbarkeit. Grundstein hierfür ist wiederum ein funktionierendes Configuration-Management-System, beispielsweise Puppet, Ansible oder Chef. Indem wir die vollständige Konfiguration eines Service in einem anderen System verwalten, können wir die Konfiguration unabhängig sichern und haben sie auch noch verfügbar, wenn der Service selbst nicht mehr verfügbar ist. Eine solche Konfiguration heißt von Produkt zu Produkt verschieden. Bei Puppet heißen sie Module, Chef verwaltet Rezepte. Der Begriff Recipe (Rezept) ist heute als Name für eine Konfiguration im Betrieb geläufig. Es erinnert an leckeres Essen, passt aber auch gut zum Konzept, denn ein Rezept besteht aus Zutaten. Die Möglichkeit, verschiedene Zutaten zu einem funktionierenden Ganzen zusammenfügen zu können, beherrschen alle drei genannten Technologien. Selbstverständlich werden die Rezepte heute wie normale Softwareentwicklungsartefakte versioniert.

Bei der Entwicklung eines Websystems ist es wichtig, dass die Rezepte zwischen Entwicklung und Betrieb abgestimmt werden, damit bei der lokalen Entwicklung derselbe Softwarestack zum Einsatz kommt, wie in der Produktionsumgebung. Bei einigen Technologien, wie PHP, ist dies schwierig, da ein Entwickler auf verschiedenen Projekten mit unterschiedlichen PHP-Versionen arbeiten muss. Aus diesem Grund ist der Einsatz von Virtualisierung auf dem lokalen Rechner heute nicht mehr unüblich. Eine verbreitete Technologie ist Vagrant. Da dies jedoch hohe Anforderungen an die Hardware stellt und auch auf schnellen Maschinen durch den Overhead längere Turnaroundzeiten in der Entwicklung bedeutet, setzt sich hier die Containerisierung immer mehr durch, beispielsweise mit Docker. Die Prinzipien des externen Konfigurationsmanagements sind hier jedoch dieselben.

Eine Prämisse zur Garantie des Erfolgs dieses Vorgehens ist das Paradigma, dass die Konfiguration einer Maschine nie modifiziert werden darf. Die Konfiguration kann nur geändert werden, wenn eine neue Maschine provisioniert wird. Im laufenden Betrieb dürfen keine lokalen Konfigurationen gemacht werden. Dies bedingt unter anderem auch, dass der Service selbst seinen Zustand externalisieren muss, damit Neuprovisionierungen gelingen können. Erst wenn man diesen Schritt geschafft hat, lassen sich Services vollständig automatisch wiederherstellen. Dieses Paradigma heißt Immutable Infrastructure. Da Konfigurationsänderungen bei Websystemen häufig vorkommen, werden auch häufig neue Maschinen bereitgestellt und dadurch getestet. Somit erzielt die Methode eine hohe funktionale Qualität der Betriebsabläufe. Sie sorgt zudem für Integrität in der Konfiguration und verhindert die Entstehung von SnowflakeServern.

Abbildung 4 zeigt den Produktions- und Konsumationsablauf von Vagrant-Boxen für eine Entwicklergemeinde anhand eines UML-Kommunikationsdiagramms. Die Abbildung zeigt das Vorgehen in folgender Reihenfolge:

  • 1. Jenkins findet Änderungen an der Systemkonfiguration im Git
    • 1.1 Jenkins triggert den buildplan zur Neukonstruktion
    • 1.2 Der buildplan stößt Packer an, der VirtualBox startet
    • 1.3 In der VirtualBox startet Puppet
    • 1.4 Puppet telefoniert nach Hause und bittet um die aktuelle Konfiguration
    • 1.5 Puppet lädt die Konfiguration aus Git und konfiguriert die Box
    • 1.6 Packer hält die VirtualBox an
    • 1.7 Die Box wird auf Nexus mit Version gespeichert
  • 2. Der Entwickler möchte mit der aktuellen Konfiguration arbeiten
    • 2.1 Vagrant lädt die gewünschte Box
    • 2.2 Vagrant startet die Box

Dieses Vorgehen funktioniert sehr gut, benötigt aber erhebliche Ressourcen. Zudem ist es meist nicht praktikabel, lokal mehr als eine Vagrant Box laufen zu lassen. Ich muss also hier dafür sorgen, dass alle benötigten Softwarepakete der Entwicklung in ein und derselben Box laufen. Docker hat hier große Vorteile, da ich die benötigten Pakete lokal flexibel mischen kann.

Abb. 4: Vagrant-Boxen bauen mit Packer

Abb. 4: Vagrant-Boxen bauen mit Packer

Die Herstellung von Images nach Rezepten ist ein komplexer Prozess mit vielen Fehlerquellen. Insbesondere die Vermischung verschiedener Zutaten zu einem großen Ganzen kann Probleme bereiten. Puppet beispielsweise erlaubt sogar Vererbungen, ähnlich wie bei der objektorientierten Entwicklung. Leider gibt es hier aber keinen Compiler, der die richtige Konfiguration automatisch prüfen kann. Bei größeren Betriebsorganisationen mit vielen Servern kann die Komplexität der Rezepte deswegen schnell eine Grenze überschreiten, die Menschen noch beherrschen können. Kleinste Änderungen an einem Rezept können hier katastrophale Auswirkungen haben. Um dies zu testen, gibt es ein Werkzeug namens Serverspec, das regelmäßig den Konfigurationsstand verteilter Maschinen überprüfen kann und im Falle einer Fehlkonfiguration Alarm schlägt. Serverspec ist unabhängig von Puppet, Chef oder auch Docker und kann alle möglichen Konfigurationen prüfen. Voraussetzung ist hierfür der Mehraufwand, da die Konfigurationen effektiv doppelt gepflegt werden müssen. Der große Vorteil ist jedoch die Widerstandsfähigkeit gegen Fehler beim Refactoring der Rezepte.

Redundanzen planen

Ein übliches Mittel, um ein System wiederherstellen zu können, ist die Schaffung von Redundanzen der beteiligten Komponenten. Anders als bei der Skalierbarkeit, bei der Last auf mehrere identische Komponenten über einen Load Balancer verteilt wird, werden hier so genannte Spares (Ersatzteile) eingesetzt, die dann zum Zuge kommen, wenn eine Komponente ausfällt. Spares kommen immer dann zum Einsatz, wenn sich eine Komponente nicht skalieren lässt, beispielsweise weil shared nothing nicht möglich ist, oder die Umgebung nicht virtualisiert ist. Man unterscheidet drei verschiedene Redundanzszenarien:

  • Eine aktiv redundante Komponente, die Hot Spare genannt wird, erhält dieselben Eingaben wie die Hauptkomponente, sodass alle Komponenten einen synchronen Zustand haben. Dadurch kann ein Hot Spare bei Ausfall den Betrieb in wenigen Millisekunden übernehmen [3]. Der verbreitetste Anwendungsfall ist ein Server mit einem Back-up-Server, der kurz 1+1-Redundanz genannt wird (one plus one).
  • Bei einer passiven redundanten Komponente, die Warm Spare genannt wird, erhält nur die aktive Komponente Eingaben. Außerdem versorgt die aktive Komponente die passiven Komponenten mit Zustandsinformationen, in der Regel zu wohldefinierten Zeitpunkten (Checkpoints). Im Gegensatz zu aktiven Komponenten verbrauchen passiv redundante Systeme weniger Strom, da nicht alle Eingaben verarbeitet werden müssen. Allerdings muss sich die Komponente auch um die Zustandssychronisation kümmern, weswegen die Komplexität der Komponente zunimmt. Da es sich um einen System Level Concern handelt, gilt eine solche Architektur als nicht konzeptionell integer.
  • Der dritte und letzte Fall von Redundanz ist der so genannte Cold Spare. Hier wird der Ersatzserver erst bei einem Ausfall gestartet und der Zustand synchronisiert. In einem solchen Fall ist die Recovery-Zeit in der Regel sehr lang. Cold-Spare-Architekturen sind in Ordnung, wenn die Verfügbarkeit nicht so wichtig ist, die Zuverlässigkeit aber schon [4].

Moderne Cloud-basierte Websysteme persistieren ihren Zustand über entsprechende Storage-Dienste, wie ein AWS Mongo DB Cluster oder Amazon Elastic Block Storage, und man spricht nur noch von Redundanz auf dem Storage Layer. Bei den Knoten weiter vorne reden wir von Skalierbarkeit. Trotzdem haben wir das Konzept der Redundanz aufgenommen, weil wir heute immer noch mit älteren Systemen zu tun haben, die eingebunden werden wollen.

Back-up, Restore und Rollback sicherstellen

Ein wesentlicher Aspekt der Wiederherstellbarkeit ist eine funktionierende Datensicherung. Als Architekt möchte ich dafür Sorge tragen, dass die Daten meines Service sicher gespeichert werden und sich im Notfall auch wieder herstellen lassen. Nach einer Wiederherstellung sollte zudem Datenintegrität gewährleistet sein.

Jeder Service muss es ermöglichen, dass seine Daten im laufenden Betrieb gesichert werden können [1]. Bei älteren Systemen kann es sein, dass der Service angehalten werden muss, damit eine Datensicherung durchgeführt werden kann. Da dies die Verfügbarkeit um die Zeit senkt, die benötigt wird, um die Datensicherung abzuwickeln, gilt diese Praxis heute als nicht mehr akzeptabel. Insbesondere bei Systemen mit vielen Inhalten kann eine Sicherung mehrere Stunden in Anspruch nehmen.

Abbildung 4 zeigt ein einfaches System als Beispiel. Alle Daten des Websystems liegen entweder in der Datenbank oder, im Fall von binären Dateien wie Bildern, im Dateisystem. Schreibende Zugriffe auf das CMS schreiben dann sowohl in die Datenbank als auch das Dateisystem. Als Architekt möchte ich sicherstellen, dass beide Komponenten zum selben Zeitpunkt gesichert werden, damit bei einer Wiederherstellung ein konsistenter Datenbestand eingespielt werden kann. Damit dies möglich ist, sollten also sowohl Datenbank als auch Dateisystem eine ähnliche Back-up-Strategie haben, damit sich wenigstens annähernd exakte Systemzustände wiederherstellen lassen. Eine perfekte Synchronisation ist theoretisch möglich, aber praktisch teuer zu implementieren.

Abb. 5: Integrität beim Back-up sicherstellen

Abb. 5: Integrität beim Back-up sicherstellen

Die Back-up-Strategie sollte neben den Uhrzeiten der Sicherung auch eine Retention Policy enthalten, die vorgibt, wie lange Datensicherungen aufbewahrt werden. Geben Sie hier in Absprache mit dem Fach vor, wie lange alte Datenbestände aufgehoben werden sollen. Üblicherweise ist das Back-up eines Websystems von vor einem Jahr nutzlos, ein Back-up von vor einem Monat jedoch gut zu gebrauchen. Eine gängige Retention Policy ist die Aufbewahrung der Back-ups von vor eins, zwei, drei und fünf Werktagen, sowie von vor zwei, drei und vier Wochen. Um den benötigten Speicherplatz zu minimieren, sollte wann immer möglich eine inkrementelle Back-up-Strategie zum Einsatz kommen. Hier werden jeweils nur die Daten gesichert, die sich geändert haben. Setzt man die oben beschriebene Retention Policy um, könnte das Back-up von vor ein, zwei oder drei Werktagen ein inkrementelles Back-up und das von vor fünf Werktagen, zwei Wochen und vier Wochen ein volles Back-up sein. Dies verhindert dann einen Totalausfall, da die Daten dreimal vollständig vorliegen.

Ein weiterer Teil der Back-up-Strategie sollte die physische Aufbewahrung der Daten umfassen. Keinesfalls sollten die Back-up-Daten am selben physischen Ort aufbewahrt werden, da sonst im Falle einer Katastrophe sowohl System als auch Back-up verschwinden würden. Die Lagerung an einem anderen Ort nennt man auch Offsite Backup. Google hatte in 2012 einen schweren Fehler in einem Lösch-Daemon ihres Musikdiensts, der hunderttausende Musikdateien der Benutzer entfernt hat [4]. Die Datensicherung findet bei Google auf Tape statt. Die Bänder werden in einem Warenhaus gelagert, das sich weit weg vom eigentlichen Rechenzentrum befindet. Um die Dateien wiederherzustellen, musste eine Software entwickelt werden, um die 5 337 Tapes zu identifizieren, welche die fehlenden 1,5 PB an Daten enthielten. Diese wurden dann mit mehreren Lastwagen angeliefert und von Hand in die Tape Libraries eingespannt, um die Daten zurück zu spielen. Die gesamte Operation dauerte sieben Tage.

Den Ernstfall üben: DiRT Drill

Das Google-Beispiel verdeutlicht die möglichen Probleme der Wiederherstellung. Google gelang die Operation nur deswegen so problemlos, weil sie diesen Eventualfall gut vorbereitet und geübt hatten. Frei nach dem Motto „Hoffnung ist keine Strategie“, war jedes Detail im Vorfeld vom Team geplant worden. So wusste das Team beispielsweise, dass die Verwendung der Tape-Roboter viel langsamer war als das manuelle Befüllen der Tape Libraries. Im Ernstfall wurden die Roboter also ausgeschaltet, um schneller wieder an die Daten zu kommen. Bei einem so genannten Disaster and Recovery Testing Drill (DiRT Drill) werden verschiedene Szenarien des Systemversagens vom Team geübt. Ziel der Übung ist es zum einen, nachzuweisen, dass sich das System überhaupt wieder herstellen lässt, aber zum anderen auch etwaige Schwachstellen zu finden, die eine Wiederherstellung erschweren könnten. Durch die gemeinsame Analyse und die Übung erhöht das Team die Widerstandsfähigkeit des Systems.

Qualitätsszenarien

Die folgenden Szenarien können zur Kommunikation im Team eingesetzt werden, um Diskussionen rund um die Verbesserung der Wiederherstellbarkeit zu stützen:

  • Im Produktivbetrieb soll sich ein neuer Node.js-Server für das E-Commerce-System bei unserem Cloud-Provider binnen drei Minuten getestet herstellen lassen.
  • Wenn ein DevOps Engineer Änderungen an den Rezepten der Produktionsumgebung vornimmt, wird diese neue Konfiguration innerhalb von fünf Minuten von Serverspec automatisch getestet.
  • Kommt es zu Datenverlust der Produktionsumgebung, kann der Datenbestand von vor einem, zwei, drei und fünf Werktagen, sowie von vor einer, zwei und vier Wochen innerhalb eines Arbeitstages vollständig widerhergestellt werden.

Fazit

Die Automatisierung der Wiederherstellbarkeit verlangt nach Investitionswillen und akkurater Planung. Während kleine Teams auch ohne vollständige Deployment-Ketten auskommen können, sind diese bei größeren Betriebsorganisationen Kern ihrer Konkurrenzfähigkeit, da sonst keine hohe Verfügbarkeit geleistet werden kann.

Geschrieben von
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: