Erhältlich ab: August 2016
In den letzten Jahren tauchten zahlreiche Artikel und Blogbeiträge auf, die unserer Meinung nach eine gefährlich Botschaft verbreiten: Wir sind agil und brauchen daher keine Architekten! Wir wollen dagegenhalten: mit dem Agilitekten.
Ein gut geführtes Restaurant kann als Metapher für ein Softwareentwicklungsteam dienen. Dort sind die Zuständigkeiten für die Arbeit im Team klar verteilt. Der Saucier macht die Soßen, der Patissier ist für Feingebäck und Süßspeisen verantwortlich, die Oberkellnerin kümmert sich um die Begrüßung und die Vergabe der Sitzplätze, der Sommelier konzentriert sich auf den Wein, und der Küchenjunge spült das Geschirr. Wenn man die Arbeitsmuster eine Weile beobachtet, wird man feststellen, wie sich jede Person auf den Arbeitsbereich konzentriert, für den sie zuständig ist. Die Kellnerin kommt mit einer Bestellung an, heftet sie an die Pinnwand und verschwindet wieder, um einen Korb mit Brot an den Tisch zu bringen. Der Annoncier liest die Bestellung durch und ruft den Spezialisten die Namen für jedes Gericht zu. Der Poissonier brät den Heilbutt an, der Saucier bereitet die Soße zu, der Legumier bereitet das Gemüse und die Garnierung mit Brunnenkresse vor und der für die Präsentation der Gerichte zuständige Koch richtet die Speise an. Sobald alles fertig ist, kontrolliert der Chefkoch das Ergebnis. Dann wird der Kellner herangerufen und serviert die Gerichte am Tisch. Jeder Einzelne weiß, was er zu tun hat, wann er es zu tun hat und – mindestens ebenso wichtig – was er von den Kollegen erwarten kann. Die Atmosphäre ist lebendig, geschäftig und entschlossen. Wenn Sie diesem Muster in einem Softwareprojekt begegnen, spüren Sie förmlich die Energie von konzentrierter Spannung und Leistungsbereitschaft in der Luft: den Flow.
Was hier wirklich vor sich geht, ist Folgendes: Die Leute kommen mit ihrer Verantwortung und ihrem Wissen zur vollen Entfaltung. Die UI-Spezialisten wissen, was von ihnen erwartet wird. Die Tester wissen, wann eine Version wirklich auslieferungsfähig ist. Die Datenbankentwickler wissen, wo ihre Aufgaben beginnen und enden. Jeder Einzelne im Team ist zuversichtlich in Bezug auf das, was er zu tun hat, und darauf, dass er weiß, wann er seinen Beitrag geleistet hat. Und der Agilitekt weiß um seine Zuständigkeit für die Koordinierung von Aufgaben, die Kommunikation der Architektur an verschiedene Stakeholder, die Mitwirkung bei der Erarbeitung querschnittlicher Konzepte, die Moderation an Schnittstellen, das Herbeiführen von Entscheidungen sowie die Sicherstellung konzeptioneller Integrität.
Manche Projekte basieren darauf, dass alle für alles verantwortlich sind: „Wir sind ein Team. Wir ziehen alle an einem Strang. Wenn irgendetwas getan werden muss, steht es in der Verantwortung jedes Einzelnen, dafür zu sorgen, dass es getan wird.“ Kaum überraschend, dass das nur selten gut geht. Stellen Sie sich vor, das Team in einem Restaurant würde so arbeiten. Während der Koch die Soufflés backt, würde er sich um die Reservierungen Sorgen machen, der Kellner würde noch etwas Salz in die Suppe werfen, der Oberkellner würde das übrige Geschirr waschen, aus Sorge, es könnte nicht genügend für Tisch 22 vorhanden sein – jeder würde sich um alles kümmern – oder in alles einmischen – und nichts besonders gut machen.
Agilitekten tragen die Verantwortung für die Qualität der Lösung. Das bedeutet, dass sie mit Kollegen gemeinsam an Aufgaben arbeiten, bei Bedarf um Unterstützung bitten oder andere relevante Quellen als Hilfe heranziehen. Entscheidend ist, dass nur eine Person die Verantwortung für die Gesamtaufgabe tragen kann. Der Agilitekt hat durch seine Rolle eingewilligt, die Verantwortung für Funktionalität und Qualität der Lösung zu übernehmen. Als Agilitekt wissen Sie, dass Sie es sind, der für den Erfolg der Lösung den Kopf hinhalten muss.
Kompetenz zieht Ansehen nach sich [1]. Autorität und Ansehen haben einen natürlichen Drang zur Kompetenz hin und sammeln sich bei ihr. In einem multifunktionalen Team (gibt es überhaupt noch andere?) bringen die verschiedenen Teammitglieder unterschiedliche Kompetenzen auf unterschiedlichen Gebieten ein. Die Autorität, Entscheidungen treffen zu dürfen, sollte sich dieser Kompetenz anschließen.
Geistige Arbeit unterscheidet sich grundlegend von Produktionstätigkeiten. In der Produktion wird den Arbeitern ein gemeinsames und in der Regel einfaches Ziel vorgegeben: „Ausstoß der maximal möglichen Produktion von Reiswaffeln innerhalb der nächsten acht Stunden.“ Und sie verfügen über geeignete Fähigkeiten, um diese Arbeit zu erledigen. Der Produktionsleiter ist üblicherweise ein Meister auf diesem Gebiet und verfügt über profundes Wissen über die Fertigungsstraße und ihre Funktionsweise. Wenn Entscheidungen getroffen werden müssen, ist dies die Aufgabe des Produktionsleiters.
Im Gegensatz dazu sind bei einer geistigen Arbeit wie Architekturentwicklung die Fähigkeiten der Beteiligten vielfältig, ebenso wie Fachkenntnisse in den benötigten Disziplinen. Bestimmte Entscheidungen fallen in das natürliche Fachgebiet eines oder mehrerer Teammitglieder. Wenn eine Entscheidung vollständig innerhalb des Teams getroffen werden kann, sollte sie dort von den jeweiligen Spezialisten getroffen werden. Falls eine Entscheidung auch Personen außerhalb des Teams betrifft, müssen die Mitglieder des Teams, in deren natürliches Fachgebiet die betroffene Entscheidung fällt, auf jeden Fall an der endgültigen Entscheidungsfindung beteiligt werden.
Stefan Toth zeigt in [2] verschiedene Interpretationen der Architektenrolle (Abb. 1), die wir alle in der Praxis mehr oder weniger verbreitet wiederfinden.
Den klassischen Architekten mit Vorgaben von oben und ohne Beachtung von Feedback aus dem Team finden wir genauso gefährlich wie das Team ohne expliziten Verantwortlichen für die Architektur. Obwohl natürlich Ausnahmen die Regel bestätigen und auch diese Organisationsformen manchmal funktionieren können.
Wenn das Team eher jung und unerfahren ist, übernimmt der Agilitekt (oder Architektur-Owner) mehr Koodinierungs- und Entscheidungsverantwortung. Im Idealfall hat man als Agilitekt hoffentlich Architekturagenten, die in ihrem jeweiligen Aufgabenbereich eigenständig größere Teile entscheiden und verantworten können. Dann bleibt für den Agiliteken aber immer noch die Koordinierung und Moderation zwischen den Teammitgliedern, um insgesamt Konsistenz, Ausgewogenheit und Angemessenheit zu erreichen.
Im allgemeinen Sprachgebrauch hat das Wort Autorität unterschiedliche Bedeutungen. Zum einen kann eine Person aufgrund ihres außerordentlichen Fachwissens auf einem bestimmten Gebiet eine natürliche Autorität sein. Zum anderen kann eine Person aufgrund ihrer Position Autorität haben. Es ist durchaus möglich, eine natürliche Autorität zu sein, ohne Autorität zu haben. Ebenso ist es möglich, eine Autorität zu haben, ohne eine Autorität zu sein. Im Idealfall ist ein Agilitekt keine Autorität, sondern besitzt natürliche Autorität und wird deshalb von den Teammitgliedern respektiert.
Im agilen Team zu arbeiten, macht Spaß. Die Fähigkeiten aller Teammitglieder zu nutzen, Aufgaben gemäß den Fähigkeiten zu übernehmen und für deren Erledigung auch die Verantwortung zu übernehmen, ist eine gute Idee. Lösungsvorschläge erarbeiten, sich gegenseitig Feedback zu geben und gemeinsam Entscheidungen treffen, ist gut. Aber einer muss die Gesamtverantwortung für die Lösung übernehmen: der Agilitekt. Und Gesamtverantwortung ist unteilbar.
Peter Hruschka (System Guild) und Dr. Gernot Starke (innoQ-Fellow) haben vor einigen Jahren www.arc42.de gegründet, das freie Portal für Softwarearchitekten. Sie sind Gründungsmitglieder des International Software Architecture Qualification Board (www.iSAQB.org) sowie Autoren mehrerer Bücher rund um Softwarearchitektur und Entwicklungsprozesse.
[1] DeMarco, Tom; Hruschka, Peter et al.: „Adrenalin Junkies & Formular-Zombies“, Carl Hanser Verlag, 2007
[2] Toth, Stefan: „Vorgehensmuster für Softwarearchitektur: Kombinierbare Praktiken in Zeiten von Agile und Lean“, Carl Hanser Verlag, 2014
Die klassischen Antworten auf Komplexität, Kontrolle, Regulierung und Mikromanagement, sind schon längst überholt. Aber gerade in letzter Zeit zeigt sich, dass sie endgültig zum alten Eisen gehören. Denn mit den Sprüngen an Komplexität, die Softwareentwicklung gerade macht, können starre Gebilde nicht mehr mithalten. Entweder fliegt Entwicklern und Management die Software um die Ohren oder versinkt in Stagnation.
Der andere Weg ist loszulassen und Freiheit zuzulassen. Unter dieser Prämisse steht die Erkenntnis, dass es heute keinen allwissenden Architekturguru mehr geben kann. Kein Mensch kann solch komplexe Gebilde, wie sie die Softwareentwicklung schafft, komplett überblicken. Komplexität muss gespalten werden, auf Teams und Menschen verteilt. Deswegen ergibt die Kombination aus agiler Softwareentwicklung und Microservices so viel Sinn. Es sind Puzzleteile, die richtig zusammengesetzt ein Bild ergeben.
Wer selbst über sein Tun entscheidet und nicht einem Befehl von oben folgt, muss für seine Entscheidungen auch gerade stehen. Aus großer Macht folgt große Verantwortung. Verantwortung für das eigene Tun zu übernehmen, führt außerdem dazu, dass es keinen Dienst nach Vorschrift mehr gibt. Das schöne Wort Ownership fällt in diesem Kontext oft und beschreibt dieses verantwortungsvolle Besitzgefühl. Daraus entsteht Motivation, die sich in besserer Arbeit niederschlägt. Doch dieser Ansatz darf nicht ins Negative verkehrt werden. Aus Verantwortung darf keine Kultur erwachsen, die sofort einen Sündenbock sucht, wenn mal etwas schief geht. Denn aus Fehlern kann und soll man lernen. Das geht nicht, wenn aus Angst, sofort als Buhmann dazustehen, Fehler unter den Teppich gekehrt werden.
Außerdem dürfen Unternehmen und Manager nicht in die Falle tappen, Entwicklern mit Mikromanagement die Freiheit zu nehmen, die sie ihnen gerade gegeben haben. Wer Teams Entscheidungsfreiheit verspricht und ihnen dann doch alles vorschreibt, hat schon verloren. Für Richtlinien bei der Softwarearchitektur gilt: So viele wie nötig, so wenige wie möglich. Außerdem sollten Regeln es den Entwicklern nicht unnötig schwer machen, gute Software zu bauen. Denn eines ist sicher: Wenn Umwege aufgezwungen werden, suchen Menschen nach Abkürzungen. Wo kein Weg ist, entsteht eben ein Trampelpfad. Die Hauptstraße in Richtung erfolgreicher Softwareprojekte und -produkte – und erfolgreicher Entwicklerinnen und Entwickler – zeigen Ihnen unsere Autoren Stefan Zörner, Stefan Toth und Christian Schneider in unserer Titelstory zum Thema Softwarearchitektur. Vielleicht ist Ihr Team oder Ihr Unternehmen ja bereits auf dem Weg unterwegs, den die Autoren beschreiben. Falls nicht, sind ihre Texte der ideale Wegweiser für Sie.
In diesem Sinne: Werden Sie zum Architekten! Falls Sie es nicht bereits sind.
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.
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.
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 [3].
Die Einführung einer vollautomatischen Systemkonfiguration ist aufwendig, komplex sowie riskant und sollte aus diesen Gründen strategisch im Rahmen der Service Strategy [4] 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.
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 [4], Ansible [5] oder Chef [6]. 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:
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
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.
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 [7], 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.
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 [8]. 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 Zustandssynchronisation 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 [9].
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.
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.
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 [9]. 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.
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.
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 wiederhergestellt werden.
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.
Daniel Takai ist Technologiemanager bei der Unic AG in Bern. Er ist dort für die Entwicklungsprozesse, Technologieentwicklung und Softwarearchitekturen verantwortlich.
[1] Limoncelli, Thomas A.; Chalup, Strata R.; Hogan, Christina J.: „The Practice of Cloud System Administration: Designing and Operating Large Distributed Systems“, Addison-Wesley Professional, 2014
[2] The Stationery Office: „ITIL Service Operation“, 2011
[3] SnowflakeServer: http://martinfowler.com/bliki/SnowflakeServer.html
[4] Puppet: https://puppet.com/
[5] Ansible: https://www.ansible.com/
[6] Chef: https://www.chef.io/
[7] Serverspec: http://serverspec.org/
[8] Bass, Len; Kazman, Rick; Clements, Paul: „Software Architecture in Practice“, Addison-Wesley, 2012
[9] Beyer, Betsy; Jones, Chris; Petoff, Jennifer; Murphy, Niall R.: „Site Reliability Engineering“, O’Reilly, 2016