Chaos-Engineering und Fehlerinjektion – der Schlüssel zu höherer Resilienz

Veröffentlicht am 27 Juli, 2020

Chief Technology Officer, Microsoft Azure

„Als ich die Blogreihe ’Advancing Reliability’ mit meinem Posting letzten Juli startete, stellte ich gleichzeitig mehrere laufende Initiativen zur Verbesserung der Plattformverfügbarkeit vor. Diese Initiativen sind Teil unserer Verpflichtung, ein vertrauenswürdiges Portfolio von Clouddiensten anzubieten. Einer meiner Schwerpunkte damals war die Fehlerinjektion. Diese Methode setzen wir zunehmend ein, um die Ausfallsicherheit von Systemen zu validieren. Heute habe ich den zuständigen Principal Program Manager Chris Ashton gebeten, diese umfassenden Chaos Engineering-Konzepte etwas genauer zu beleuchten. Anhand von Azure-Beispielen soll erläutert werden, wie wir die Konzepte zusammen mit Stresstests und synthetischen Workloads umsetzen, um die Resilienz von Anwendungen und Diensten zu verbessern.“ – Mark Russinovich, CTO, Azure


 

Die Entwicklung umfangreicher, verteilter Anwendungen war noch nie einfacher, aber es gibt einen Haken. Ja, die Infrastruktur wird dank Ihrer Public Cloud in Minutenschnelle bereitgestellt, es gibt viele Sprachoptionen zur Auswahl, umfassenden Open-Source-Code und eine Fülle von Komponenten und Diensten auf dem Markt, auf denen man aufbauen kann. Ja, es gibt gute Referenzquellen, die Ihnen dabei helfen, Ihre Lösungsarchitektur und Ihr Design besser zu verstehen, wie z. B. das Azure Well-Architected Framework und andere Ressourcen im Azure Architecture Center. Aber während die Anwendungsentwicklung einfacher wird, steigt das Risiko von Abhängigkeitsunterbrechungen. Nicht kontrollierbare Ausfälle – so selten sie auch sein mögen – können jederzeit auftreten, Ihre Abhängigkeiten könnten Probleme verursachen oder Ihre wichtigsten Dienste/Systeme könnten zu langsam reagieren. Kleinere Störungen in einem Bereich können größere Ausmaße annehmen oder lang anhaltende Auswirkungen in einem anderen Bereich haben. Diese Dienstunterbrechungen können die Entwicklerproduktivität beeinträchtigen, das Vertrauen der Kunden schwächen, Umsatzeinbußen verursachen und sich sogar auf das Geschäftsergebnis eines Unternehmens auswirken.

Moderne Anwendungen und die Cloudplattformen, auf denen sie entwickelt werden, müssen mit Blick auf Ausfälle konzipiert und laufend überprüft werden. Entwickler müssen bekannte und unbekannte Ausfallbedingungen berücksichtigen, Anwendungen und Dienste müssen redundant ausgelegt sein, und Algorithmen erfordern Wiederholungs- und Back-off-Mechanismen. Die Systeme müssen widerstandsfähig gegenüber den Szenarien und Bedingungen sein, die durch seltene, aber unvermeidliche Produktionsausfälle und -unterbrechungen verursacht werden. Mit diesem Beitrag möchten wir Sie dazu anregen, darüber nachzudenken, wie sich typische Fehlerbedingungen am besten validieren lassen. Außerdem zeigen wir anhand von Beispielen, wie wir bei Microsoft unsere eigenen Systeme validieren.

Ausfallsicherheit

Resilienz ist eine Fähigkeit, durch die Systeme bei disruptiven Ereignissen kontrolliert heruntergefahren und anschließend wiederhergestellt werden können. Die Überprüfung der Resilienz einer Anwendung, eines Diensts oder einer Plattform ist genauso wichtig wie die Entwicklung ausfallsicherer Lösungen. Es ist einfach und oft naheliegend, die Zuverlässigkeit einzelner Komponenten isoliert zu validieren und daraus zu schließen, dass das gesamte System genauso zuverlässig sein wird. Aber das könnte ein Fehler sein. Resilienz ist eine Eigenschaft eines ganzen Systems, nicht nur seiner Komponenten. Um zu verstehen, ob ein System wirklich resilient ist, muss man die Resilienz des ganzen Systems in der betreffenden Umgebung messen und verstehen. Aber wie geht man dabei vor und wo fängt man an?

Fehlerinjektion und Chaos-Engineering

Chaos-Engineering ist eine Methode, durch die ein System realen Ausfällen und Abhängigkeitsunterbrechungen ausgesetzt wird, mit denen es in einer produktiven Umgebung konfrontiert sein wird. Fehlerinjektion ist die gezielte Einschleusung eines Fehlers in ein System, um dessen Stabilität und Fehlerbehandlung zu untersuchen.

Indem Systemarchitekten konsequent auf Fehlerinjektion und Chaos-Engineering setzen, können sie vertrauenswürdige Lösungen entwickeln. Und Entwickler haben den Vorteil, dass sie die Resilienz ihrer Anwendungen messen, verstehen und optimieren können. Genauso können die Site Reliability Engineers (SREs) und tatsächlich jeder, der diese Verantwortung an Fachteams delegiert, die Einhaltung der Service-Level-Ziele sicherstellen und den Systemzustand in der produktiven Umgebung überwachen. Auch Betriebsteams können neue Hardware und Rechenzentren validieren, bevor diese für die Nutzung durch Kunden freigegeben werden. Die Einbindung von Chaos-Techniken in die Releaseüberprüfung fördert das Vertrauen aller Mitarbeiter – einschließlich der Führungsebene – in die vom Unternehmen entwickelten Systeme.

Führen Sie während des gesamten Entwicklungsprozesses frühe und häufige Tests durch, wie Sie es hoffentlich bereits tun. Während Sie sich auf den produktiven Einsatz Ihrer Anwendung oder Ihres Diensts vorbereiten, folgen Sie den normalen Testverfahren, indem Sie Komponenten-, Funktions-, Stress- und Integrationstests hinzufügen und durchführen. Wo es sinnvoll ist, verwenden Sie Tests für bestimmte Fehlerfälle. Verwenden Sie darüber hinaus die Fehlerinjektion, um die Fehlerbehandlung und das Verhalten des Algorithmus zu bestätigen. Um eine noch größere Wirkung zu erzielen – und hier kommt das Chaos-Engineering ins Spiel – können End-to-End-Workloads (wie Stresstests, Leistungsbenchmarks oder synthetische Workloads) durch Fehlerinjektion ergänzt werden. Beginnen Sie mit einer vorproduktiven Testumgebung, bevor Sie Experimente in der produktiven Umgebung durchführen. Veranschaulichen Sie sich außerdem, wie sich Ihre Lösung in einer sicheren Umgebung mit einem synthetischen Workload verhält, bevor sich Probleme auf den echten Kundendatenverkehr auswirken könnten.

Ein sinnvoller Einsatz der Fehlerinjektion in einem Validierungsprozess kann einen oder mehrere der folgenden Punkte umfassen:

  • Ad-hoc-Überprüfung neuer Features in einer Testumgebung:
    Ein Entwickler könnte einen virtuellen Testcomputer (VM) einrichten und neuen Code isoliert ausführen. Während der Durchführung bestehender Funktions- oder Stresstests könnten Fehler injiziert werden, um den Netzwerkzugriff auf Remoteabhängigkeiten (wie z. B. SQL Server) zu blockieren. So kann überprüft werden, ob das Szenario durch den neuen Code korrekt umgesetzt wird.
  • Automatisierte Fehlerinjektion in einer CI/CD-Pipeline, einschließlich Bereitstellungs- oder Resilienzgates:
    Bestehende End-to-End-Szenariotests (wie z. B. Integrations- oder Stresstests) können durch die Fehlerinjektion ergänzt werden. Fügen Sie einfach nach der normalen Ausführung einen neuen Schritt ein, um die Ausführung fortzusetzen oder um sie mit einigen Fehlern erneut zu starten. Durch das Hinzufügen von Fehlern können Probleme gefunden werden, die normalerweise durch die Tests nicht entdeckt würden. Oder die Entdeckung von Problemen wird beschleunigt, was letztendlich zu Treffern führen kann.
  • Validierung von Vorfallskorrekturen und Regressionstests:
    Die Fehlerinjektion kann in Verbindung mit einem Workload oder einer manuellen Ausführung verwendet werden. So lassen sich die gleichen Bedingungen herstellen, durch die ein Vorfall verursacht wurde. Dies macht es möglich, die Validierung einer bestimmten Vorfallskorrektur oder Regressionstests für ein Vorfallsszenario zu veranlassen.
  • BCDR-Drills in einer vorproduktiven Umgebung:
    Fehlerbedingungen, die ein Datenbankfailover verursachen oder die Speicher offline schalten, eignen sich für BCDR-Drills. Auf diese Weise wird sichergestellt, dass sich Systeme bei solchen Bedingungen angemessen verhalten und dass während der Failovertests keine Daten verloren gehen.
  • Game-Days in produktiven Umgebungen:
    Ein „Game-Day“ ist eine koordinierte Simulation eines Ausfalls oder Vorfalls, um zu überprüfen, ob ein Ereignis von Systemen angemessen behandelt wird. Dies umfasst in der Regel die Überprüfung der Überwachungssysteme sowie der menschlichen Prozesse, die während eines Vorfalls durchgespielt werden. Die zuständigen Teams können Fehlerinjektionstools einsetzen, um Fehler, die ein hypothetisches Szenario darstellen, kontrolliert zu orchestrieren.

Typische Releasepipeline

Diese Abbildung zeigt eine typische Releasepipeline und die Möglichkeiten zur Integration der Fehlerinjektion:

Chaos-Engineering in der Releasepipeline

 

Eine Investition im Bereich Fehlerinjektion wird erfolgreicher sein, wenn sie auf wenigen grundlegenden Komponenten aufbaut:

  • Koordinierte Bereitstellungspipeline
  • Automatisierte ARM-Bereitstellungen
  • Synthetische Runner und synthetische End-to-End-Workloads
  • Überwachungs-, Warnungs- und Livesite-Dashboards

Wenn diese Voraussetzungen gegeben sind, kann die Fehlerinjektion ganz ohne oder mit geringem zusätzlichem Aufwand in den Bereitstellungsprozess integriert werden. So lässt sich der Codeflow auf dem Weg zur produktiven Umgebung leichter steuern.

Lokale Rack-Stromausfälle und Geräteausfälle wurden bei der Ursachenanalyse früherer Vorfälle als vereinzelte Fehlerquellen ermittelt. Bis ein Servicetechniker erkennt, dass ein Dienst durch eines dieser Ereignisse in der produktiven Umgebung beeinträchtigt wird und somit als nicht resilient gilt, hat er einen langen, mühevollen und kostspieligen Weg vor sich. Es gibt mehrere Möglichkeiten, die Fehlerinjektion zu nutzen, um die Resilienz gegenüber diesen Ausfällen in der gesamten Releasepipeline in einer kontrollierten Umgebung und einem kontrollierten Zeitrahmen zu validieren. Dies bietet dem Codeautor auch die Möglichkeit, die festgestellten Probleme zu untersuchen. Ein Entwickler, der Code ändert oder neuen Code hinzufügt, kann eine Testumgebung einrichten, den Code bereitstellen und Ad-hoc-Experimente mithilfe von Funktionstests und Fehlertools durchführen, die das Offlineschalten von Abhängigkeiten simulieren, z. B. das Herunterfahren von VMs, das Blockieren des Dienstzugriffs oder die einfache Änderung von Berechtigungen. In einer Stagingumgebung kann die Injektion ähnlicher Fehler zusätzlich zu automatisierten End-to-End- und Integrationstests oder anderen synthetischen Workloads eingesetzt werden. Testergebnisse und Telemetrie können dann verwendet werden, um die Auswirkungen der Fehler zu ermitteln und mit der Ausgangsleistung zu vergleichen, um den Codeflow ggf. zu blockieren.

In einer vorproduktiven oder Canary-Umgebung können automatisierte Runner Fehlerbedingungen ausgesetzt werden, die wiederum den Zugriff auf Abhängigkeiten blockieren oder diese offline schalten. Mithilfe von Überwachungs-, Warnungs- und Livesite-Dashboards kann dann überprüft werden, ob die Ausfälle erkannt wurden und ob das System reagiert und das Problem beseitigt hat. Das ist das Prinzip der Resilienz. In der gleichen Umgebung können SREs oder Betriebsteams auch BCDR-Drills (Business Continuity/Disaster Recovery) durchführen. Dabei verwenden sie Fehlerinjektionen, um Speicher oder Datenbanken offline zu schalten und Systemmetriken erneut auf Resilienz und Datenintegrität zu überwachen. Die gleichen Canary-Aktivitäten können auch in der produktiven Umgebung mit echtem Kundendatenverkehr durchgeführt werden. Dies führt jedoch mit höherer Wahrscheinlichkeit zu Auswirkungen auf die Kunden, sodass dieser Schritt nur empfohlen wird, wenn die Fehlerinjektion an früherer Stelle in der Pipeline eingesetzt wurde. Die Umsetzung dieser Verfahren und die Integration der Fehlerinjektion in eine Bereitstellungspipeline ermöglicht eine systematische und kontrollierte Resilienzprüfung. Diese ermöglicht es den Teams, Probleme zu entschärfen und die Anwendungszuverlässigkeit zu verbessern, ohne die Endkunden zu beeinträchtigen.

Fehlerinjektion bei Microsoft

Bei Microsoft beziehen einige Teams die Fehlerinjektion frühzeitig in ihre Validierungspipeline und automatisierten Testdurchläufe ein. Die verschiedenen Fachteams führen Stresstests, Leistungsbenchmarks oder synthetische Workloads wie gewohnt in ihren automatisierten Validierungsgates aus, und es wird eine Baseline festgelegt. Dann wird der Workload erneut ausgeführt, dieses Mal jedoch mit Fehlern wie CPU-Überlastung, Datenträger-E/A-Jitter oder Netzwerklatenz. Workloadergebnisse werden überwacht, die Telemetrie und Speicherabbilder werden überprüft, und Service Level Indicators (SLI) werden mit Service Level Objectives (SLOs) verglichen, um die Auswirkungen abzuschätzen. Wenn die Ergebnisse als Fehler eingestuft werden, kann es sein, dass der Code nicht in die nächste Stufe der Pipeline fließt.

Andere Microsoft-Teams verwenden die Fehlerinjektion in regelmäßigen BCDR-Drills (Business Continuity, Disaster Recovery) und für Game-Days. Einige Teams führen monatliche, vierteljährliche oder halbjährliche BCDR-Drills durch und setzen Fehlerinjektionen ein, um Katastrophenbedingungen auszulösen und sowohl den Wiederherstellungsprozess als auch die Warnungs-, Überwachungs- und Livesiteprozesse zu überprüfen. Dies geschieht oft in einer vorproduktiven Canary-Umgebung, noch bevor die Methode in der produktiven Umgebung mit echtem Kundenverkehr eingesetzt wird. Einige Teams führen auch Game-Days durch, an denen sie sich ein hypothetisches Szenario ausdenken, z. B. die Replikation eines früheren Vorfalls. Außerdem verwenden sie Fehlerinjektionen, um die Orchestrierung zu erleichtern. Fehler können sich in diesem Fall noch schädlicher auswirken, z. B. können sie VMs abstürzen lassen, den Netzwerkzugriff deaktivieren, ein Datenbankfailover verursachen oder das Herunterfahren eines vollständigen Rechenzentrums simulieren. Auch hier werden die normalen Livesite-Überwachungs- und -Warnungsfunktionen verwendet, sodass Ihre DevOps- und Incident-Management-Prozesse ebenfalls validiert werden. Um allen Beteiligten entgegenzukommen, werden diese Aktivitäten in der Regel während der Geschäftszeiten durchgeführt und nicht über Nacht oder am Wochenende.

Unsere Betriebsteams verwenden die Fehlerinjektion auch zur Validierung neuer Hardware, bevor diese den Kunden überlassen wird. Drills werden in Zusammenhang mit Stromausfällen in einem Rack oder Rechenzentrum ausgeführt. So kann beobachtet werden, ob sich Überwachungs- und Sicherungssysteme erwartungsgemäß verhalten.

Bei Microsoft verwenden wir die Prinzipien des Chaos-Engineerings und Fehlerinjektionsmethoden, um die Resilienz und das Vertrauen in die von uns angebotenen Produkte zu erhöhen. Mithilfe dieser Methoden überprüfen wir die Anwendungen, die wir den Kunden bereitstellen, und die Dienste, die wir den Entwicklern zur Verfügung stellen. Dies dient dazu, die zugrunde liegende Azure-Plattform zu validieren, um neue Hardware vor der endgültigen Bereitstellung zu testen. Einzeln und zusammen tragen die Maßnahmen zur allgemeinen Zuverlässigkeit der Azure-Plattform bei – und zur Verbesserung unserer gesamten Servicequalität.

Unbeabsichtigte Folgen

Denken Sie daran, dass die Fehlerinjektion ein sehr wirkungsvolles Tool ist und mit Vorsicht eingesetzt werden sollte. Es sollten Sicherheitsvorkehrungen getroffen werden, um sicherzustellen, dass sich Fehler, die in einer vorproduktiven oder Testumgebung eingeschleust werden, nicht negativ auf die produktive Umgebung auswirken. Der Ausbreitungsradius eines Fehlerszenarios sollte eingedämmt werden, um die Auswirkungen auf andere Komponenten und auf die Endkunden zu minimieren. Die Möglichkeit, Fehler zu injizieren, sollte nur eingeschränkt zugänglich sein, um Risiken zu minimieren und um das Eindringen von Hackern in böswilliger Absicht zu verhindern. Die Fehlerinjektion kann in der produktiven Umgebung eingesetzt werden, muss aber sorgfältig geplant werden. Führen Sie zuerst Tests in der vorproduktiven Umgebung durch, begrenzen Sie den Ausbreitungsradius, und sehen Sie eine Ausfallsicherung vor, damit das Experiment bei Bedarf abrupt beendet werden kann. Der Atomunfall von Tschernobyl im Jahr 1986 ist ein eklatantes Beispiel für eine misslungene Fehlerinjektion. Stellen Sie sicher, dass Ihr System vor unbeabsichtigten Folgen geschützt ist.

Chaos as a Service?

Wie von Mark Russinovich in diesem früheren Blogbeitrag erwähnt, besteht unser Ziel darin, Kunden und Partnern native Fault Injection-Dienste zur Verfügung zu stellen. So können die Kunden ihre eigenen Anwendungen und Dienste auf die gleiche Weise überprüfen. Dies ist eine spannende und sehr wirkungsvolle Methode, um die Zuverlässigkeit von Clouddiensten zu verbessern und die Auswirkungen seltener, aber unvermeidlicher Störungen zu minimieren. Es gibt viele Teams, die viele interessante Dinge ausprobieren. Wir untersuchen, wie wir all diese unterschiedlichen Tools und Fehler am besten zusammenbringen können, um unseren Alltag einfacher zu machen – für die internen Entwickler von Azure-Diensten, für integrierte Azure-Dienste wie Microsoft 365, Microsoft Teams und Dynamics und schließlich für unsere Kunden und Partner, die ihre eigenen Anwendungen und Lösungen mit den gleichen Tools herausfordern (und letztendlich die Resilienz ihrer eigenen Anwendungen und Lösungen verbessern) können.