Blog

Distributed Tracing: Hype oder wirklicher Nutzen?

19 Apr 2023

Header

Moderne Softwarearchitekturen, die auf Microservices- und Serverless-Architekturen basieren, bringen Vorteile für die Anwendungsentwicklung. Verteilte Entwicklerteams können ihre einzelnen Services einfacher verwalten, überwachen und betreiben. Der Nachteil ist, sie können leicht das „große Ganze“ aus den Augen verlieren. Gibt es Probleme in einer Transaktion, die auf mehrere Microservices, serverlose Funktionen und Teams verteilt ist, ist es nahezu unmöglich, den Service, der für das Problem verantwortlich ist, von den betroffenen Services zu unterscheiden. Distributed Tracing soll hier unterstützen und das übergreifende Systemverhalten überwachen und sichtbar machen.

Dabei erfasst Distributed Tracing die Pfade, die ein Request (von einer Anwendung oder einem Endnutzer) durch eine verteilte Anwendungslandschaft wie Microservices oder Serverless-Funktionen nimmt [1]. Distributed Tracing bietet damit eine Ende-zu-Ende-Sicht auf den Request sowie die Zusammenhänge verschiedener Services. Es handelt sich also um eine Diagnosetechnik, die aufzeigt, wie sich eine Menge von Services verhält, um einzelne Requests zu bearbeiten.

Die Terminologie dahinter

Bevor wir darüber reden können, wie Distributed Tracing funktioniert, müssen die grundlegenden Begriffe geklärt werden. Hierzu ist es sinnvoll, die Definitionen der OpenTelemetry heranzuziehen. OpenTelemetry bietet einen Open-Source-Standard sowie einen Satz von Technologien, mit denen Traces aus ihren Cloud-nativen Anwendungen und ihrer Infrastruktur erfasst sowie exportiert werden können [2].

  • Request: Ein Request (auch als Transaktion bekannt) ist die Art und Weise, wie Applikationen in einer verteilten Systemlandschaft kommunizieren. Dabei kann jeder Service eine andere Technologie zur Verarbeitung des Requests nutzen, z. B. HTTP oder MQTT.

  • Trace: Eine Trace repräsentiert den Ende-zu-Ende-Fluss eines Requests durch die verschiedenen Services. Jede Trace besteht aus mehreren Spans.

  • Span: Beim Distributed Tracing zeigt eine Span eine einzelne Arbeitseinheit an, die in einem Trace ausgeführt wird, z. B. einen API-Aufruf oder eine Datenbankabfrage. Jeder Service im Fluss eines bestimmten Requests durch das verteilte System trägt eine Span mit einem zeitlichen Kontext bei. Dabei kann man zwischen zwei verschiedenen Span-Arten unterscheiden:

    • Root Span: Die Root Span (auch als Parent Span bezeichnet) ist die erste Span in einer Trace.

    • Child Span: Alle folgenden Spans werden als Child Spans bezeichnet.

  • Instrumentation: In Microservices-Umgebungen bezieht sich die Instrumentierung normalerweise auf den Code, der einem Service hinzugefügt wird, damit Daten gesammelt werden können. Moderne Tracing-Tools unterstützen normalerweise die Instrumentierung in mehreren Sprachen und Frameworks. So kann man für Java z. B. die Instrumentierung mit Spring Cloud Sleuth umsetzen. Dabei werden die Standardkonfigurationen (automatisiertes Aufsetzen von Spans, Traces usw.) durch das Framework bereits übernommen, ohne dass man selbst etwas im Code ändern muss. Dadurch können verschiedene Distributed Tracing Tools wie z. B. Jaeger [3] oder Zipkin [4] diese Daten sammeln und visualisieren.

  • Sampling: Tracing-Daten werden oft in großen Mengen produziert, sie sind nicht nur „teuer“ zu sammeln und zu speichern, sondern es ist auch „teuer“, sie zu übertragen. Um ein Gleichgewicht zwischen dem Monitoring und diesen Kosten herzustellen, wird Sampling eingesetzt. Es ist also der Prozess, bei dem entschieden wird, ob eine Span verarbeitet und exportiert wird oder nicht. Hier gibt es zwei Ansätze:

    • Head-based Sampling: Die Sampling-Entscheidung wird ganz am Anfang gesetzt, wenn die Trace beginnt.

    • Tail-based Sampling: Die Sampling-Entscheidung wird für die jeweilige Trace erst am Ende des Prozesses gesetzt.

  • Trace Context: Der Trace Context wird verwendet, um die Anfrage über Services hinweg zu verfolgen. Das ist vergleichbar mit dem Versandetikett auf einem Paket. Der Trace Context enthält Daten wie z. B. die Trace ID, die Span ID oder verschiedene Flags wie z. B. die Sampling Flag, die anzeigt, ob die Span verarbeitet werden soll oder nicht. Bei jedem Request wird daher der Trace Context in die Metadaten des Transportprotokolls eingehängt. Wie in Tabelle 1 zu sehen ist, haben die verschiedenen Anbieter unterschiedliche Formate für den Trace Context. Hier sollte man jedoch in seiner Implementierung auf den W3C-TraceContext setzen. Er versucht im Dschungel der verschiedenen Trace Contexts eine Standardisierung zu etablieren, die auch immer mehr Toolanbieter adaptieren.

  • Exporter: Die Komponente, die die Daten einer Trace bündelt und an das Ziel-Backend oder einen Endpunkt im benötigten Format exportiert. Dadurch können Distributed Tracing Tools wie Jaeger oder Zipkin die Daten sammeln, verarbeiten und visualisieren.

Wie das Ganze funktioniert

Die Datensammlung des Distributed Tracing beginnt ab dem Zeitpunkt, an dem ein Request einen Service erreicht, z. B., wenn ein Benutzer ein Formular der Website absendet. Durch die jeweilige Instrumentation wird dann die Erstellung einer einmaligen Trace ID getriggert sowie einer Span, in diesem Fall der Parent Span. Diese erste Span ID ist dann gleich die Trace ID. Bei jedem weiteren Service-Aufruf bleibt die Trace ID gleich und die Span ID ändert sich. Jede weitere Span ist eine Child Span. Dieses Vorgehen erlaubt es, einen Baumgraphen aus den Daten zu erstellen, der zeigt, wie viel Zeit der Request in jedem Service verbracht hat, wie in Abbildung 1 beispielhaft zu sehen ist.

Trace-Context-Lösung Trace-Context-Beispiel
Jaeger uber-trace-id:{trace-id}:{span-id}:{parent-span-id}:{flags}
Zipkin X-B3-TraceId:{trace-id}

X-B3-SpanId:{span-id}

X-B3-ParentSpanId:{parent-span-id}

X-B3-Sampled:{sampleFlag}

W3C-TraceContext traceparent:{version}-{trace-id}-{parent-id}-{trace-flags}

Tabelle 1: Trace-Context-Beispiele verschiedener Anbieter

 

Abb. 1: Beispiel für einen Baumgraphen
Abb. 1: Beispiel für einen Baumgraphen

Der jeweilige Exporter sendet dann die Daten an den Tracing-Server. Neben dem Span Context können weitere Daten mit der Trace mitgeschickt werden. Dazu zählen z. B. Tags oder Logs, die der Trace wichtige Mehrinformationen zur Analyse liefern können, wie Abbildung 2 [6] es exemplarisch zeigt.

Abb. 2: Beispiel für Trace-Daten
Abb. 2: Beispiel für Trace-Daten

Schließlich können dann die Spans in einem Tracing-Tool, etwa mittels eines Flammengraphen, visualisiert werden. Dabei ist die Parent Span die oberste, drunter sind alle Child Spans zu sehen, die ihrer zeitlichen Reihenfolge entsprechend auftreten. Ein Beispiel hierfür zeigt Abbildung 3. Dadurch ist ersichtlich, welcher Service wann und wie lange den Request gehandhabt hat. In unserem Beispiel fällt auf, dass Service D am meisten Zeit „gekostet“ hat. Hier wäre eventuell ein tieferer Blick lohnenswert, um Optimierungen abzuleiten.

Abb. 3: Beispiel für einen Flammengraphen
Abb. 3: Beispiel für einen Flammengraphen

Der Nutzen von Distributed Tracing

Wenn man Distributed Tracing einsetzt, sollte das selbstverständlich einen Nutzen stiften. Dabei sind die folgenden wichtigsten Vorteile zu nennen, die sich durch den Einsatz von Distributed Tracing ergeben:

  • Minimierung der MTTD (Mean Time to Detect) und der MTTR (Mean Time to Recover): Distributed Tracing hilft, die Zeit zu verkürzen, in der ein Problem identifiziert wird, sowie die Zeit, bis es behoben ist. Zeit ist Geld und daher gilt hier: je schneller, desto besser.

  • Verständnis der Servicebeziehungen: Durch Distributed Tracing kann ein Verständnis dafür geschaffen werden, welche Services miteinander in Verbindung stehen. Dadurch ergibt sich ein Bild für die Ursache-Wirkung-Beziehung zwischen den Services.

  • Messen bestimmter Benutzeraktionen: Sendet z. B. ein Benutzer ein bestimmtes Formular ab, so kann diese Aktion über alle Services hinweg verfolgt werden. Engpässe, Fehler etc. können so bestimmten Prozessen zugeordnet werden.

  • Verbesserung von Zusammenarbeit und Produktivität: Ein Request kann mehrere Services durchlaufen, für deren Entwicklung mehrere Teams verantwortlich sind. Distributed Tracing kann effizient aufdecken, wo der Fehler entstanden ist und welches Team das Problem beheben muss.

  • Pflege von SLAs (Service Level Agreements): Die meisten Unternehmen haben SLAs, also Verträge mit dem Kunden über bestimmte Leistungsziele der Services. Distributed Tracing kann diverse Performancedaten aggregieren und hilft bei der Beurteilung, ob SLAs eingehalten werden.

Fazit

Im Allgemeinen ist Distributed Tracing der beste Weg für DevOps-, Betriebs-, Software- und SRE-(Site-Reliability-Engineering-)Teams, um schnell Antworten auf bestimmte Fragen in Umgebungen mit verteilter Software zu erhalten. Sobald eine Anfrage eine Handvoll Microservices umfasst, ist es unerlässlich, zu sehen, wie all die verschiedenen Services zusammenarbeiten. Trace-Daten geben eine Antwort darauf, was in der gesamten Anwendung passiert. Wenn es nur isolierte Sichten auf die einzelnen Services gäbe, gäbe es keine Möglichkeit, den Fluss zwischen einzelnen Services zu rekonstruieren. Distributed Tracing ist ein rein technisches Instrument, das man nutzen sollte, wenn man Antworten auf die folgenden Fragen braucht:

  • Welche Services sind im Request eingebunden und wie lange?

  • Was ist die Ursache von Fehlern in einem verteilten System?

  • Wo gibt es Performanceengpässe?

Durch Distributed Tracing ist man in der Lage, schnell Missstände zu erkennen. Darüber hinaus hilft es, nicht erst zu reagieren, wenn schon etwas passiert ist. Man sollte proaktiv nach Problemen suchen, um sie zu beseitigen, bevor größere Schwierigkeiten entstehen. Daher ist ganz klar, dass es sich hier nicht nur um einen Hype handelt, sondern um ein hilfreiches Instrument, um das „große Ganze“ im Services-Dschungel überblicken zu können.

Dabei sollte man beachten, dass Distributed Tracing nur ein Teil des gesamten Monitorings ist. Es gibt definitiv Aufschluss über die Services und ihre Beziehung zueinander. Es ist aber unerlässlich, neben Distributed Tracing auch die anderen zwei Säulen der Observability zu beachten: Distributed Metrics und Distributed Logging. Nur so kann neben der technischen Sicht auch eine Business-Sicht aufgezogen werden. Erst alle drei zusammen geben einen optimalen Überblick über das Geschehen einer verteilten Anwendung.

hobolic_robert_sw.tif_fmt1.jpg
Robert Hobolic ist seit 10 Jahren bei der Capgemini Deutschland GmbH in Stuttgart in verschiedenen Software-Engineering- und Architektenrollen unterwegs. Derzeit ist er Technischer Chef-Architekt mit Fokus auf Microservice-Architektur, Cloud und DevOps.