Trace All the Things
Bei der Planung von Infrastrukturen und Architekturen finden Logging und Monitoring meist zu Beginn Beachtung. Das Thema Tracing wird dabei oft vernachlässigt, bis es zum ersten unerklärbaren Problem kommt. Meine Mission besteht darin, das zu verhindern.
Warum ist Tracing wichtig und wozu brauche ich das überhaupt?
Tracing ist eine Methode in IT und DevOps Teams, um Applikationen zu überwachen. Und noch wichtiger, um sie zu messen. Besonders interessant wird Tracing bei Applikationen, die aus Microservices bestehen.
Die Fehlersuche und das Messen der Performance unserer Anwendungen auf Request-Ebene wird immer schwieriger, je mehr Services letzten Endes involviert sind. Es ist umso wichtiger, dass wir vollständig nachvollziehen können, wie sich unsere Systeme verhalten und warum sie tun, was auch immer sie tun.
Daher denken wir bereits bei der ersten Planung von Microservice-Architekturen über Dinge wie Logging oder Monitoring nach. Monitoringsysteme wie beispielsweise Prometheus helfen uns, das Systemverhalten besser zu verstehen, während Loggingsysteme wie Greylog oder Elasticstack uns beim Auffinden und der Analyse von Fehlern unterstützen. Diese Daten werden idealerweise an einer zentralen Stelle wie Elasticsearch abgelegt und über User Interfaces wie Kibana einfach zugänglich gemacht.
Request Tracing sollte die logische Konsequenz von Logging und Monitoring sein
Tracing hilft uns, genau herauszufinden, wo Fehler auftreten und welche Komponente unserer Architektur, sogar welcher spezifische Teil der Komponente suboptimal performt.
Und trotz allem schenken wir Tracing in der Regel nicht die Aufmerksamkeit in der ersten Planung von Systemen, die es verdient. Tracing wird vielmehr implementiert, wenn der erste “unerklärbare” Fehlerreport erzeugt wird. Zum Beispiel ruft ein Kunde an und fragt, wieso sein Request von gestern so lange gebraucht hat. Ohne ein verlässliches Tracingsystem wird es uns schwerfallen, auf solche Fragen valide Antworten zu geben.
Deshalb sind geeignete Logging, Monitoring und Tracing Stacks so wichtig. Diese drei Stacks bilden zusammen die Basis, um unsere Systeme zu analysieren, zu betreiben und letztlich auch zu optimieren – nur so können wir alles, was geschieht, wirklich verstehen.
Wenn Tracing so wichtig ist, wieso nutzen wir es nicht jetzt schon alle?
Die Antwort darauf ist tatsächlich relativ simpel. Tracing in verteilten Infrastrukturen und Architekturen bringt die eine oder andere Herausforderung mit sich. Es gibt ein paar Schwierigkeiten mit dem Setup eines Tracingsystems.
Unser Tracingsystem muss den Tracingkontext sowohl innerhalb als auch zwischen unseren Prozessen angemessen abbilden. Das ist nicht einfach. Um das zu erreichen, muss so ziemlich jeder Teil unserer Applikation angefasst werden.
Fun Fact: Microservices bedeuten, dass es EINIGE Orte gibt, an denen wir aktiv werden müssen.
Und vielleicht arbeiten Sie nicht alleine mit dem System und es gibt viele unterschiedliche Leute, die für unterschiedliche Teile der Applikation verantwortlich sind. Vielleicht arbeiten diese Personen nicht einmal im selben Unternehmen. Vielleicht sind in der Applikation Standardkomponenten eingebaut, die Sie gar nicht selbst liebevoll in Handarbeit erstellt haben, z.B. Traefik oder nginx als Software Load Balancer. Und genau hier beginnt der Spaß.
Was wir also brauchen, ist ein Standard, der von jedem genutzt werden kann, ohne dass sich der Arbeitsalltag signifikant verkompliziert. An dieser Stelle kommt der OpenTelemetry-Standard ins Spiel. Dieser Standard erlaubt es allen, Tracing in ihrem eigenen Code zu implementieren, ohne sich an einen bestimmten Anbieter zu binden.
Wie funktioniert Tracing?
Die Tracingkomponente des OpenTelemetry-Standards besteht aus zwei Konzepten.
Erst einmal haben wir „Spans“. Sie messen die Zeit, die ein einzelner Teil unserer Workload zur Ausführung benötigt. So sehen wir im Beispiel oben die lange rosafarbene Span. Das könnte die Zeit sein, die unser Software Load Balancer vom Erhalt eines Request bis zum Versand der Response benötigt hat.
Diese Information allein ist nicht viel wert. Der erste Schritt besteht darin, dass durch die Erstellung dieser ersten Span eine sogenannte “Trace ID” erzeugt wird.
Unser erster tatsächlicher Service, der an diesem Request beteiligt war, wird durch den orangefarbenen Bereich dargestellt. Wichtig ist, dass die Trace ID an diesen Service übergeben wird.
Nun erstellt dieser Service seine eigene Span. Die Trace ID wird nicht verändert. So kann man später die beiden Events miteinander verknüpfen und verstehen, was passiert ist.
Die Workload unseres Services ist in zwei Schritte unterteilt, die jeweils durch eine eigene Span dargestellt werden. Wenn unser Service fertig ist, ruft er einen anderen Service auf (dunkelblau) und übergibt auch hier die Trace ID.
Die Summe all dieser Spans, die an diesem einen Request beteiligt sind, wird “Trace” genannt.
Implementierung
Jede der Komponenten in unserer Systemlandschaft wird vollständig isoliert behandelt. Es spielt keine Rolle, welche Art von Anwendung wir betreiben, solange wir den OpenTelemetry-Standard implementieren. Es gibt Libraries für fast jede Art von Anwendung, die wir betreiben möchten, wie Springboot, JEE, Go etc.
Die letztendliche Tracingtechnologie, die wir in unserem System verwenden werden, kann durch eine einfache Konfigurationsänderung umgestellt werden. Beispiele sind Zipkin oder Jaeger. Es besteht keine Notwendigkeit, irgendetwas in unseren Anwendungen zu ändern, sobald OpenTelemetry eingerichtet ist.
Auftritt Jaeger
Jaeger ist natürlich mit dem bereits gezeigtem OpenTelemetry-Datenmodell und seinen Libraries kompatibel.
Jaeger selbst ist in Go geschrieben und bietet die ideale Basis für ein Setup in Kubernetes. Es ist leichtgewichtig und extrem schnell. Wir haben die Möglichkeit, einen Datenspeicher unserer Wahl einzurichten, zum Beispiel Elasticsearch, Cassandra oder sogar Kafka. Schauen wir uns kurz an, wie sich Jaeger in unsere Systeme integriert.
Jaeger besteht aus mehreren verschiedenen Teilen, die in unserem System eingesetzt werden sollen. Zum Ausprobieren können wir das „All-in-one“ Docker Image nutzen, um ein Gefühl für Jaeger zu bekommen.
Beim Deployment von Jaeger in die „reale Welt“ implementieren wir zunächst die OpenTelemetry-Client-Bibliotheken in unseren Anwendungscode.
Auf Infrastrukturebene sehen wir den „jaeger-agent“ als die Komponente, die unsere Traces zum System transportiert. Diese jaeger-agents können in jedem Container als „Sidecar“ oder als DaemonSet im Cluster eingesetzt werden. Agents können über mehrere Instanzen verfügen, müssen es aber nicht. Um möglichst kurze Netzwerkstrecken zu haben, empfiehlt es sich, mehrere Agents zu starten.
Diese Agents berichten an einen jaeger-collector, der die Traces an den Datenspeicher unserer Wahl sendet.
Die jaeger-query dient der Suche nach unseren Traces. Die grafische Oberfläche wird jaeger-ui genannt. Im Prinzip ist die jaeger-ui das Kibana des Tracing.
Fazit
Wir haben gesehen, dass es eine einfache Integration von Request Tracing in Microservice-Architekturen mit dem OpenTelemetry-Standard und Jaeger als Downstream-Technologie gibt. Und mit der One-Image-Lösung kann man alles mal ausprobieren und selbst loslegen.
Da Tracing in den meisten Systemen fehlt, hoffe ich, hiermit ein wenig Awareness geschaffen zu haben. Was wir immer im Hinterkopf haben sollten: Ohne Tracing haben wir immer Blind Spots in unseren Systemen. Das ist nicht unser Anspruch.
Sascha Rauch
Neueste Artikel von Sascha Rauch (alle ansehen)
- DevOps Engineers: Zwischen Erwartungen und Realität - 17. September 2024
- Trace All The Things - 12. Januar 2023