Chaos Engineering: Für kontrollierte Unordnung sorgen

Seite 2: Die Grundlagen des Testens

Inhaltsverzeichnis

Beim Entwickeln von Software sichert man die Implementierung durch Tests in unterschiedlichen Ausprägungen ab. Dabei verweisen Entwickler gerne auf die Testpyramide, die veranschaulicht, in welchem Umfang man welche Testart schreiben sollte. Die Testpyramide veranschaulicht jedoch auch ein Dilemma: Je weiter man sich mit einem Testszenario nach oben bewegt, desto mehr Aufwand, Zeit und Kosten entstehen.

Die Testpyramide (Abb. 1)

Beim Erstellen von Unit-Tests schreiben Entwickler Testfälle, um das erwartete Verhalten zu überprüfen. Die zu testende Komponente ist entweder von allen Abhängigkeiten befreit, oder man hat ihr Verhalten mit Mocks fest im Griff. Die Tests können die Fehlerfreiheit nicht garantieren. Sollten Entwickler des Moduls in der Implementierung der Komponente einen Denkfehler gehabt haben, tritt der Fehler in den Tests auf, unabhängig davon, ob die Entwickler erst die Tests und dann den Code implementiert haben. Eine mögliche Abhilfe schafft das sogenannte Extreme Programming, bei dem sich Entwickler beim Schreiben der Tests und der Implementierung der Funktionen kontinuierlich abwechseln.

Ein Unit-Test im Detail (Abb. 2)

Um Entwicklern und Verantwortlichen mehr Freizeit gönnen zu können, folgen nach den Unit-Tests noch Integrationstests, die das Zusammenspiel einzelner Komponenten überprüfen. Integrations-Tests laufen im Idealfall nach den erfolgreich getesteten Unit-Tests automatisiert durch und testen voneinander abhängige Komponenten.

Integrationstest im Überblick (Abb. 3)

Durch eine hohe Testabdeckung und Automatisierung erreicht man einen stabilen Zustand der Anwendung. Aber wer kennt nicht das ungute Gefühl auf dem Weg zur Produktion? Nur unter realen Bedingungen kann man erkennen, wie sich die einzelnen Bausteine der Gesamtarchitektur verhalten. Das ungute Gefühl hat der Einsatz zeitgemäßer Microservices-Architekturen verstärkt, die Komplexität und lose Kopplung hat zugenommen. Es muss mit viel Bedacht daran gearbeitet werden, die lose Kopplung aufrechtzuerhalten, um nicht in einem verteilten Monolithen zu landen.

In Zeiten von Microservices und deren loser Kopplung erreicht man schnell eine Softwarearchitektur, die sich unter dem Begriff Distributed System zusammenfassen lässt. Die entwickelten Systeme sind einzeln für sich schnell zu begreifen, ihre Aufgabe ist einfach zu verstehen und sie lassen sich schnell deployen und skalieren. Das führt in den meisten Fällen zu einer Architektur, die wie folgt aussieht:

Eine typische Softwarearchitektur beim Einsatz von Microservices (Abb. 4)

Architekturdiagramme vermitteln eine klare und abstrakte Sicht auf die entwickelte Software. Beim Betrieb der Architektur treten jedoch Szenarien auf, die sich nicht aus einem Architekturdiagramm ableiten lassen. Darunter liegende Schichten sind nicht zu erkennen, auch die eingesetzte Hardware ist nicht zu fassen. Es sind unzählige Komponenten notwendig, um eine performante und robuste Anwendung zu betreiben. In der Produktion erwarten Entwickler etwas, was dem folgenden Architekturdiagramm nahekommt.

Softwarearchitektur in alltäglichen Projekten (Abb. 5)

Der Load Balancer kennt nicht alle Instanzen des Gateways oder kann sie im Netzwerk dank einer Firewall-Regel nicht erreichen. Die Service Discovery kann sich nicht synchronisieren und liefert unterschiedliche Ergebnisse. Die eine oder andere Applikation ist tot, doch die Service Discovery bekommt den Ausfall nicht mit. Die Last wird wegen fehlender Instanzen nicht verteilt und führt zu einer erhöhten Last auf einzelne Knoten. Das kommt manchen vielleicht vertraut vor. Die meisten Entwickler wissen, was es heißt, sich mit defekter Hardware, fehlerhafter Virtualisierung, falsch konfigurierten Firewalls und langwierigen Abstimmungen innerhalb des Unternehmens auseinandersetzen zu müssen.

Der Ansatz zeigt, wo Probleme liegen können. Ein Blick auf eine äußerst komplexe Architekur wie die von Netflix hilft, das Ausmaß einer zeitgemäßen Microservices-Architektur noch besser begreifen zu können.

"Resiliency through Failure" (A. Tseitlin, QCon NY, 2013) (Abb. 6)

Umso beeindruckender ist, wie gut die Architektur von Netflix funktioniert und auf mögliche Fehler reagieren kann. Wenn man sich Vorträge des Netflix-Teams anschaut, stößt man früher oder später über die Aussage: "Keiner weiß, wie und warum das funktioniert." Diese Erkenntnis hat bei Netflix das Thema Chaos Engineering ins Leben gerufen.

Um das Verhalten der komplexen und hochverfügbaren Systeme besser zu verstehen, setzt Netflix Chaos Engineering ein, um den Entwicklern, Operations und anderen Beteiligten die Sicherheit und das Vertrauen in die von ihnen entwickelte und betrieben Software zu vermitteln. Ein Ausfall in Produktion führt bei allen Beteiligten zu einem klaren Verhalten: Sie reagieren zielgerichtet und fokussieren sich auf die vom Ausfall betroffene Komponente, ohne sich die Zeit nehmen zu müssen, das große Ganze im Blick zu haben.

Die für alle unangenehmen Ausfälle treten in Produktion vereinzelt und unkontrolliert auf. Durch Chaos Engineering ist es möglich, die Ausfälle kontinuierlich und kontrolliert stattfinden zu lassen. Die Beteiligten haben die notwendige Zeit und Mittel, den Ausfall und das Verhalten zu analysieren.