Next Level APM – Microservice-Architekturen sinnvoll überwachen
Verteilte Anwendungen mit Microservices werden immer populärer. Vorteile wie die von anderen Microservices unabhängige Entwicklung, Verteilung und Skalierung, einfache Ersetzbarkeit und gute Übersichtlichkeit dank Single Responsibility-Prinzip sowie die Verwendung von jeweils für die Aufgabe passenden Technologien bescheren Microservices eine wachsende Verbreitung. Allerdings führen diese Vorteile auch zu einer steigenden Komplexität der Anwendungen, die die Hersteller von Monitoringlösungen vor ganz neue Herausforderungen stellen.
Gartner unterteilt die Anforderungen an solche APM-Lösungen in fünf Dimensionen [1],
- End-User Experience Monitoring (EUEM), also das Erfassen von Metriken vom Endanwender bis zum anderen Ende des Systems,
- Application Topology Discovery and Visualization, also das Entdecken und Darstellen von Software- und Hardware-Komponenten, die bei der Ausführung der Anwendung relevant sind,
- User-defined Transaction Profiling, also das Nachverfolgen von vom Benutzer einer Anwendung angeforderten Transaktionen durch die Anwendung,
- Application Component Deep Dive, also das Erfassen von relevanten Metriken der einzelnen Komponenten in einer Anwendung, sowie
- IT Operations Analytics (ITOA), also die Kombination oder Anwendung verschiedener Modelle zur Datenanalyse. [2].
Diese fünf Dimensionen haben sich auch mit dem Aufkommen von Microservice-Architekturen nicht verändert. Microservice-Architekturen stellen viele etablierte Monitoringlösungen allerdings vor einige Probleme. Denn wo der Monolith im Sinne der Systemtheorie als lediglich "kompliziertes System" verstanden werden kann, sind verteilte Systeme per Definition "komplex" [3]. Aus- und Vorhersagen über solche Systeme sind schwieriger, da die einzelnen unabhängigen Services miteinander in Wechselwirkung stehen. Deren beobachtbaren Effekte müssen erst miteinander in Bezug gesetzt werden, um ein zusammenhängendes Bild von der Anwendung zu erhalten. Hier scheitern viele etablierte Werkzeuge noch. Den Schluss auf das große Ganze muss daher ein Mensch übernehmen. Genau das wird aber durch Eigenschaften wie viele kleine (und teilweise auch ephemerale) Container mit verschiedensten Technologien massiv erschwert.
Wo der SysOps beim Monolithen "nur" einige wenige große Systeme überwachen musste, hat er es bei Microservices schnell mit Dutzenden, wenn nicht Hunderten von kleinen heterogenen Systemen zu tun. Und er muss nun nicht nur mehrere Systeme überwachen, sondern auch über das Wissen für den gesamten polyglotten Stack verfügen, um den Informationsfluss zwischen diesen System zu verstehen. Eine Aufgabe, die mit steigender Anzahl von Microservices praktisch unmöglich wird. So konstatiert der Streaming-Dienst Netflix daher auch "humans cannot continuously monitor the status of all of our systems" [4].
Die Datenflut in verteilten Systemen wächst mit der Skalierung der zu überwachenden Dienste. Um dieser Datenflut Herr zu werden, müssen die erfassten Daten von einem oder auch mehreren Monitoringwerkzeugen in Wissen umgewandelt bzw. reduziert werden, das dann vom Menschen beurteilt werden kann.
Service-Qualität überwachen
Nun stellt sich die Frage, was denn sinnvolle Werte sind, die ein Mensch im Kontext von APM überwachen sollte. Dafür muss man zunächst verstehen, was denn eigentlich eine Anwendung ist. Nehmen wir an, wir haben einen Webshop. Dieser Webshop erlaubt uns, eine Produktsuche durchzuführen, indem wir Anfragen an eine URL schicken. Dieser Einstiegspunkt ist ein logischer Dienst in unserer Anwendung. Ein anderer Dienst steuert unseren Warenkorb und wieder ein anderer Dienst den Bezahlvorgang. Die Summe aller Einstiegspunkte und aller dafür erforderlichen physikalischen Komponenten und der Informationsfluss darin ist unsere Anwendung.
Im Sinne von Gartner‘s Application Topology Discovery erkennt eine gute Monitoringlösung diese Dienste und Komponenten automatisch. Lösungen, die dies nicht können, erlauben zumindest deren händische Instrumentierung (z. B. über ein SDK), um sie zu überwachen. Sind die Dienste einmal überwacht, brauchen wir sinnvolle Kennzahlen, die darüber Aufschluss geben, ob unsere Kunden diese verschiedenen Dienste auch nutzen können. Für verteilte System definiert Google hierzu die vier "golden signals" latency, traffic, errors und saturation[5]. Mit diesen Metriken hat man zumindest schon einmal einen guten Überblick über die Anwendung: "If you measure these four golden signals and page a human when one signal is problematic (or, in case of saturation, nearly problematic), your service will be at least decently covered by monitoring."
Eine sinnvolle Ergänzung dazu ist, die Anzahl der Instanzen zu messen, die einen Dienst zur Verfügung stellen [6]. Andere Kennzahlen, wie z. B. der Apdex [7], haben sich nicht bewährt, da sie irreführend sein können [8]. Letzten Endes sind diese Metriken jedoch nur die Spitze des Eisberges, denn wenn die gewünschte Service-Qualität nicht erfüllt wird, wissen wir noch nicht, warum.
Den Informationsfluss beobachten
Stellt sich heraus, dass die Service-Qualität eines Dienstes, z. B. für unsere Produktsuche, nicht ausreichend ist, wollen wir also wissen, warum. Das Mittel der Wahl in verteilten Anwendungen ist Distributed Tracing, also das Verfolgen von Anfragen durch unsere Anwendung hindurch. Distributed Tracing erlaubt uns, den Informationsfluss zu beobachten und so eventuelle Probleme zu lokalisieren. Bei Gartner ist dies das User-defined Transaction Profiling.
In einem Monolithen schaut man sich hierzu einfach den Callstack eines Aufrufs an. In verteilten Anwendungen sind die Callstacks aber über verschiedene Systeme verstreut. Distributed Tracing verbindet sie wieder, indem der initiale Aufruf bei seinem Eintritt in die Anwendung einen eindeutigen Identifikator erhält, dem dann in allen weiteren Stationen des Aufrufs die jeweiligen Callstacks des Subsystems zugeordnet werden. Das Resultat könnte dann z. B. wie folgt aussehen:
Hat man die verschiedenen Callstacks erst einmal wieder zusammengeführt, lässt sich wie bei einem monolithischem Callstack einfach ermitteln, an welcher Stelle ein Aufruf langsam oder fehlerhaft war. Eine recht simple und leichtgewichtige Implementierung von Distributed Tracing stammt von Google und ist im Dapper Paper [9] beschrieben. Auf Dapper basiert auch die Implementierung von Zipkin [10], aus dem wiederum das OpenTracing-Projekt [11] entstanden ist, das einen offenen Standard für Tracing-Werkzeuge etablieren will.
Die physikalische Ebene
Wie bereits oben ausgeführt, besteht eine Anwendung unter den logischen Diensten aus physikalischen Komponenten, deren Zusammenspiel Einfluss auf die Leistung unserer Anwendung haben können. Lässt sich anhand von Traces nicht schlüssig ermitteln, wieso unsere Service-Qualität unzureichend ist, ist es Zeit für den Application Component Deep Dive.
Wir tauchen also ein in die Komponenten, auf denen unser logischer Dienst ausgeführt wird. Um das Beispiel mit der Produktsuche fortzuführen, nehmen wir an, unser Dienst ist als Spring Boot-Anwendung in der Cloud implementiert. Das Spring Boot läuft auf einer Java Virtual Machine (JVM). Eine JVM ist ein Prozess, die auf einem Host läuft. Dieser Prozess läuft in einem Docker-Container. Der Container läuft auf einem Host. Der Host läuft in einer AWS-Zone.
Erhält unser logischer Dienst nun eine Anfrage, verbindet sich der Dienst zu einem ElasticSearch Cluster. Ein ElasticSearch-Knoten in diesem Cluster ist ebenfalls eine Java-Anwendung. Diese Anwendung läuft also auch auf einer JVM. Die JVM ist wieder ein Prozess. Eventuell läuft auch dieser Prozess in einem Container. Der Container läuft auf einem Host. Zwei solcher Knoten laufen auf dem gleichen Host wie unser Dienst, zwei weitere Knoten auf einer separaten AWS-Instanz. Eine Monitoringlösung könnte dieses Setup nun wie folgt visualisieren:
All diese Verbindungen ergeben zusammen einen Laufzeitkontext. Die verschiedenen Metriken all dieser Komponenten und ihrer Abhängigkeiten sowie der darin stattfindende Datenfluss beeinflussen maßgeblich die Service-Qualität unserer Anwendung. Für den oben gezeigten Graph würden hier also eine Vielzahl unterschiedlicher Metriken verschiedenster Systeme erfasst, u. a. aus der JVM Kennzahlen zur Garbage Collection und über die verschiedenen Memory Spaces, für den ElasticSearch-Knoten Informationen über den Index. Für den Host wollen wir die CPU und Speicherauslastung wissen, usw. – hier wird auch deutlich, wieso es für einen Menschen kaum möglich ist, die gesamte Anwendung händisch zu überwachen. Wir haben nun bereits nur für unsere Produktsuche 30 Komponenten dauerhaft zu überwachen. Wir sind hier auf die Hilfe von smarten Algorithmen angewiesen.
Änderungen erkennen
Erschwerend kommt hinzu, dass der Laufzeitkontext in der Regel nicht statisch ist. Anwendungen verändern sich. Es werden neue Bibliotheken auf den Server gespielt, neuer Code ausgerollt, neue Hardware eingebaut, neue Dienste konfiguriert oder Servicekonfigurationen angepasst, etc. und meist sind es genau diese Änderungen, die dann zu einer Beeinträchtigung der Service-Qualität führen, weil z. B. ein Speicherlimit zu gering (oder zu hoch) gesetzt wurde oder weil das Update auf eine neue Bibliothek einen Bug beinhaltet, usw.
Erkennt eine Monitoringlösung solche Änderung, kann sie uns dabei unterstützen, einen oder auch mehrere Root Causes zu identifizieren und uns so wertvolle Zeit bei der Fehlerbehebung sparen.
Algorithmen gegen das Rauschen
Die fünfte Dimension von APM betrifft jedwede Auswertung der erfassten Daten mit Hilfe von Algorithmen. Ziel hierbei ist es, das Rauschen zu reduzieren, bzw. die erfassten Daten in sinnvolle Informationen oder Aktionen aufzubereiten. Ein Beispiel hierfür wären Notifizierungen im Fehlerfall.
Nicht alle Probleme müssen zwingend auch sofort gemeldet werden. Der Ausfall eines Payment Gateway kostet Umsatz. Das will ich selbstverständlich sofort wissen – auch mitten in der Nacht. Aber eine einzelne voll ausgelastete CPU in einem Cassandra Cluster ist kein solches Problem, wenn der Cluster als Ganzes dadurch nicht relevant beeinträchtigt ist. Werkzeuge mit fixen Regeln und Schwellenwerten sind hier nur bedingt hilfreich. "Wenn die Festplatte zu 80 Prozent voll ist, dann benachrichtige mich" ist eine schlechte Regel, wenn dadurch kein Service beeinträchtigt ist. "Wenn die Festplatte in 30 Minuten voll ist, dann benachrichtige mich" ist schon besser, weil hierbei zumindest die tatsächlich gerade stattfindenden Schreiboperationen mit einbezogen werden und so ein Trend ermittelt wird. Aber auch ein Trend ist fehleranfällig. Wäre die Festplatte nämlich nur deshalb in 30 Minuten voll, weil gerade ein Backup erstellt wird, dass im Anschluss eh auf den Backupserver gespielt und lokal wieder gelöscht wird, ist eine Benachrichtigung wieder unnötig. Moderne Monitoringlösungen erkennen diesen Kontext, z. B. indem sie auch die Periodizität solcher Ereignisse erkennen und nur über "Ausreißer" (Outlier Detection) benachrichtigen.
Ein anderes Beispiel für intelligente Datenaufbereitung ist die Erkennung von kausalen Effekten. Gerade bei Werkzeugen mit fixen Schwellenwerten führen einzelne Probleme oder Änderungen schnell zu Kettenreaktionen. Eine CPU läuft plötzlich unter Volllast. Der SysOps bekommt eine E-Mail. Nun laufen aber auch andere Prozesse langsamer. Und schon füllt sich das Postfach mit weiteren E-Mails, die das eigentliche Problem verdecken und so wertvolle Zeit bei der Fehleranalyse kosten. In Microservice-Architekturen bekommt diese Art der Analyse noch einmal eine ganz eigene Brisanz, da sich die kausalen Effekte über die verteilten Systeme verstreut manifestieren. Eine gute Monitoringlösung erkennt diese Zusammenhänge und clustert diese kausalen Symptome zu einem einzelnen Ereignis.
Diese zwei Beispiele seien hier nur exemplarisch erwähnt. Die rapide Verbreitung von Machine Learning erlaubt selbstverständlich noch andere Analysen der erfassten Daten, wie z. B. die gezielte Analyse der Daten einzelner Technologien. So könnte mich meine Monitoringlösung z. B. darüber informieren, wenn ich eine veraltete Version einer Software einsetze oder über Datenbankabfragen, die keine Indizes verwenden. Kurz gesagt: alles was hilft, den reibungslosen Betrieb unserer Anwendung zu gewährleisten oder sogar noch zu optimieren.
- Gartner: Anforderungen an APM-Lösungen
- Informatik Aktuell – Ulrich Zeh: IT-Vorsorge: Proaktive Unterstützung für Endanwender mit der richtigen ITOA-Lösung
- Wikipedia: Komplexes System
- Netflix
- Beyer, B. and Jones, C. and Petoff, J. and Murphy, N.R. (2016), Site Reliability Engineering – How Google Runs Production Systems, O‘Reilly Media Inc, 60f,
- Instana Blog – Mirko Novakovic: Introducing the Logical View
- Wikipedia: Apdex
- DZone Blog – Jim Hirschauer: Apdex is Fatally Flawed
- Paper: Dapper, Google's Large-Scale Distributed Systems Tracing Infrastructure
- Zipkin
- The OpenTracing Project