In einem Umfeld, in dem IT-Systeme ebenso verteilt sind wie Anwendungen, Software und Mitarbeitende, braucht es Klarheit darüber, was in diesem Geflecht separater Interaktionen vor sich geht. Genau das macht dezentrales Tracing möglich.
Dezentrales Tracing ist ein Verfahren zur Erfassung der Anforderung in Anwendungen und dazu, welchen Weg diese Anforderung von Benutzern und Frontend-Geräten bis zu Backend-Services und -Datenbanken nehmen.
Anforderungen und Transaktionen lassen sich damit aus jeder beliebigen Anwendung erfassen, die durch Monitoring abgedeckt ist. Dies liefert wichtige Informationen, die zur Verbesserung der Verfügbarkeit, zur Problem- und Incident-Behebung, zur fortlaufenden Optimierung und damit letztlich auch zu einem herausragenden Benutzer- und Kundenerlebnis beitragen.
In diesem Artikel schauen wir uns das dezentrale Tracing sowie die Technologien, die seine Umsetzung in eurem Unternehmen möglich machen, genauer an.
Das Management moderner Softwareumgebungen steht und fällt mit den drei „Säulen der Observability“. Mitunter werden Metriken, Logdaten und Traces mit Events sogar noch um eine weitere Säule ergänzt (MELT-Framework). Jede Säule stellt eine Datenquelle dar, die enorm wichtige Einblicke in Anwendungen und die Infrastruktur liefert, auf der sie gehostet wird. Diese Methode wird häufig als Application Performance Monitoring (APM) bezeichnet, einem Gebiet des IT-Monitorings.
Vielen ITOps- und SRE-Teams (Site Reliability Engineering) dürften die beiden Säulen Logdaten und Metriken wohlbekannt sein, ist ihre Analyse doch schon seit Jahrzehnten gängige Praxis, um Folgendes zu erreichen:
Dagegen sind Traces und mit ihnen die dritte Säule womöglich manchen weniger ein Begriff.
Tracing ist per se kein neues Konzept. Eine Trace bezeichnet eine Sammlung von Transaktionen, auch Spans genannt, die eine eindeutige User- oder API-Transaktion repräsentieren und von einer Anwendung und den ihr zugehörigen Services verarbeitet werden.
Tracing beginnt ab dem Moment, an dem ein Benutzer mit einer Anwendung interagiert. Sendet ein Benutzer eine Anforderung ab, etwa das Hinzufügen eines Artikels zum Warenkorb, wird dieser eine eindeutige Trace-ID zugewiesen. Diese Trace repräsentiert also eine einzelne User-Interaktion. Auf dem Weg der Anforderung durch das Host-System wird jede darauf ausgeführte Operation (oder Span) mit einigen Kennzeichnungen versehen.
Die Trace setzt sich aus einer Sammlung von Spans zusammen. Diese stellen wiederum jeweils eine einzelne Operation dar, die Folgendes umfasst:
Jeder Span stellt ein Segment des Pfads der Anforderung dar. Dementsprechend umfasst jeder Span wichtige Informationen im Zusammenhang mit dem Service, der die Operation ausgeführt hat. Dazu gehören:
Entwicklungs- und Management-Teams mit Fokus auf monolithischen Anwendungen nutzen Traces schon seit Langem, um Aufschluss über verschiedene Faktoren der App-Performance zu erhalten:
Beim Tracing geht es letztlich immer darum, Klarheit über Transaktionen zu erhalten. Was sich jedoch verändert, ist die Art seiner Umsetzung. Denn Tracing funktioniert nicht bei Anwendungen, die auf dezentralen Softwarearchitekturen aufbauen, wie beispielsweise Microservices.
Nachfolgend zunächst ein Blick auf klassisches Tracing, seine Funktionsweise und warum es in der heutigen Zeit wenig zielführend ist.
In den Anwendungen früherer Zeiten, die als Monolithen ausgeführt wurden, war Tracing zwar möglich, es war aber weniger wichtig, die zugrunde liegenden Vorgänge nachzuvollziehen, da eine Anforderung während ihrer Verarbeitung durch die Anwendung weniger bewegliche Elemente durchlaufen musste. Es gab also schlicht weniger Traces, die man hätte erfassen können.
Die damaligen Tracing-Tools basierten auf probabilistischen Stichprobenverfahren. Dadurch wurde jedoch nur ein kleiner, willkürlich ausgewählter Teil der Gesamtmenge aller Transaktionen erfasst. Probabilistische Stichproben liefern einen gewissen Einblick in das Geschehen. Da es aber eben nur Momentaufnahmen vereinzelter Transaktionen sind, decken sie nicht die Gesamtheit ab und können somit auch kein Gesamtbild liefern. So könnten IT- und SRE-Teams durch Tracing mittels Stichprobenverfahren auch allerhöchstens folgende Erkenntnisse gewinnen:
Aufschluss über detaillierte Performance-Trends könnte dieser Ansatz jedoch nicht liefern. Ebenso wenig ist er skalierbar genug, um die Tausenden dezentralen Services innerhalb von containerisierten Umgebungen zu quantifizieren, die hochdynamischen Veränderungen unterliegen:
Es lässt sich also schnell erkennen, dass dieser Ansatz der kurzlebigen Beschaffenheit dezentraler Systeme nicht gewachsen ist, kann dieser Umstand doch weitere Warnmeldungen ohne direkten Zusammenhang nach sich ziehen und so die Behebung eines Problems erschweren. Ein Beispiel wäre, dass ein EC2-Node ausfällt und durch einen anderen ersetzt wird: Wenn sich dies nur auf eine Benutzeranforderung auswirkt, muss dann überhaupt benachrichtigt werden? Entsprechend würden derartige Probleme dem Team so lange verborgen bleiben, bis sie sich zu ernsthaften Störungen auswachsen.
In einer statischen, monolithischen Umgebung wäre das nicht weiter problematisch. Wahrscheinlicher ist aber, dass eure Umgebung deutlich komplexer ist und sich laufend verändert.
Mit einer einzelnen Benutzertransaktion gehen Interaktionen mit Dutzenden oder auch Hunderten von Microservices einher, von denen jeder Daten aus Backend-Datenspeichern anfordert, die wiederum miteinander über APIs oder mit anderen Teilen der Infrastruktur interagieren. Oder einfach ausgedrückt: Eine Software steuert eine andere an.
Microservices sind so angelegt, dass sie voneinander unabhängig skalieren. So ist es auch nichts Ungewöhnliches, dass mehrere Instanzen eines einzelnen Services auf mehreren Servern, an unterschiedlichen Standorten und in verschiedenen Umgebungen gleichzeitig ausgeführt werden. Dabei kann sich jeder Microservice zudem auf Stacks mit mehreren Ebenen verteilen, die sich aus folgenden Komponenten zusammensetzen:
Klassisches Monitoring, bei dem nur Stichproben oder ein einzelnes Codeelement betrachtet wird, reicht hier schlicht nicht aus. Um die Anforderungen innerhalb eines Verbunds aus Microservices nachvollziehen zu können, gilt es, zusätzliche Daten zu erfassen. Darunter etwa:
Da sich die Traces auf verschiedene Services verteilen, spricht man bei der Kennzeichnung von Spans und deren Korrelation von dezentralem Tracing.
Am besten stellt man sich hierzu ein Online-Videospiel mit Millionen von Spielern vor: Das Spiel muss den Standort jedes Endbenutzers erfassen, dazu jede Interaktion zwischen den Spielern, jeden Gegenstand, den sie im Spiel aufnehmen, sowie eine Vielzahl anderer Daten, die im Spielverlauf generiert werden. Mit herkömmlichen Tracing-Methoden wäre eine flüssige Ausführung des Spiels undenkbar – gewährleisten lässt sich dies erst durch dezentrales Tracing.
Dezentrales Tracing erfasst, wie eine Anforderung (bzw. Transaktion) verschiedene Services innerhalb einer Microservice-Architektur durchläuft. So wird der Ursprung (also die benutzerzentrierte Frontend-Anwendung) einer Anforderung auf ihrem Weg zu anderen Services nachvollziehbar.
Veranschaulichen lässt sich dezentrales Tracing am Beispiel eines Verbunds von Microservices in einer modernen Anwendung:
In dieser Umgebung würde eine dezentrale Trace der Benutzeranforderung zuerst Informationen zum Anforderungsstatus am ersten Frontend-Service erfassen – also die Daten, die benutzerseitig eingegeben wurden, und wie lange der Service benötigt, um diese Daten an andere Services weiterzuleiten.
Der nächste Berührungspunkt in der Trace würde die Backend-Services betreffen, die die Eingaben akzeptieren und die erforderliche Datenverarbeitung durchführen, z. B. ETL oder inzwischen immer häufiger auch ELT. Im Anschluss daran übertragen die Backend-Services die verarbeiteten Daten an den Datenbank-Service, wo sie gespeichert werden.
Nehmen wir eine einfache Client-Server-Anwendung:
Aus Sicht des Clients ist eine einzige Aktion aufgetreten. Der Client hat eine Anforderung gesendet und eine Antwort erhalten. Wir erfassen jedoch sämtliche Server-Anforderungen, die als Ergebnis dieser Client-Anforderung generiert werden, in einem Span. Der Client führt im Kontext der Anwendung verschiedene Transaktionen mit dem Server aus, daher werden mehr Spans generiert. Diese können wir innerhalb eines Trace-Kontextes miteinander korrelieren.
Der Trace-Kontext ist gewissermaßen der Kitt, der die Spans zusammenhält, wie sich an dieser Aufschlüsselung erkennen lässt:
Zu beachten gilt, dass der Trace-Kontext derselbe bleibt. Auf diese Weise werden alle Spans miteinander verknüpft, und die Infrastruktur wird darüber informiert, dass jeder Span der gleichen Transaktion zugehörig ist.
Tools für dezentrales Tracing korrelieren zunächst die Daten aus allen Spans, um sie anschließend als Visualisierungen zu formatieren, die auf zwei Arten bereitgestellt werden können:
Um Traces erfassen zu können, müssen die Anwendungen zunächst instrumentiert werden. Zur Instrumentierung von Anwendungen bedarf es Frameworks wie OpenTelemetry, mittels derer sich Traces generieren und die App-Performance messen lässt. Diese Informationen sind äußerst nützlich, da sie die Ursache von Engstellen leicht erkennbar machen, statt lange nach ihnen suchen zu müssen.
Dabei gilt: Solange ihr ein anbieterunabhängiges Framework wie OpenTelemetry nutzt, müsst ihr dies zudem nur einmal vornehmen.
Im Anschluss an die Instrumentierung der Anwendung gilt es, Telemetriedaten mithilfe eines Collectors zu erfassen.
Hierfür ideal geeignet ist der Splunk OpenTelemetry Collector, mit dem sich die Erfassung und Verarbeitung von App-Telemetrie ebenso wie deren Export in ein Analysetool wie Splunk APM einheitlich lösen lässt. Dies wiederum macht Folgendes möglich:
Wollt ihr wissen, welcher Microservice letztlich die Fehler in der Anwendung verursacht hat? Mit Splunk erkennt ihr nicht nur, dass es paymentService war, ihr könnt sogar noch tiefer ins Detail gehen und feststellen, welche Version die Ursache war.
(Wie sich Anwendungen automatisch instrumentieren und die Trace-Daten mit Splunk APM analysieren lassen, zeigen wir hier am Beispiel einer einfachen Java-App.)
Der wichtigste Vorteil von dezentralem Tracing besteht darin, Benutzertransaktionen mit konkretem Kontext von einer zentralen Stelle aus durchleuchten zu können. Die Komplexität dahinter spielt so keine Rolle mehr. Ein ganzheitlicher Ansatz entsteht, der sich in vielerlei Hinsicht bemerkbar macht:
Da SRE- und IT-Teams den Anforderungsstatus einschließlich zugehöriger Performance-Faktoren für die einzelnen Services im Blick haben, können sie die Ursache von Leistungsproblemen präzise identifizieren. Hierzu können sie jedes nur erdenkliche Detail erfassen und so die Reaktionsfähigkeit jedes einzelnen Services ermitteln. Dadurch erhalten sie Klarheit über Probleme wie die Folgenden:
Aufgrund der Fragmentierung von Microservice-Architekturen gestaltet sich das Monitoring der Anwendungsperformance rund um die Ermittlung und Behebung von Problemen zeitaufwändiger und kostenintensiver als bei monolithischen Anwendungen.
Zudem sind Daten zu Fehlern in Microservices nicht immer klar nachvollziehbar. So sind Entwicklungsteams häufig mit Fehlermeldungen und obskuren Statuscodes konfrontiert, aus denen es Probleme zu entschlüsseln gilt. Dezentrales Tracing liefert ihnen dagegen ein Gesamtbild verteilter Systeme mit folgenden Vorteilen:
All dies schlägt sich letztlich in einer verkürzten Mean Time to Recovery/Repair (MTTR) nieder.
In Microservice-Umgebungen liegt jeder Prozess in der Regel in der Verantwortung eines bestimmten Teams. Dies kann es jedoch erschweren, Fehler auszumachen und zu bestimmen, wer für deren Behebung verantwortlich ist. Dezentrales Tracing hilft hier in vielerlei Hinsicht:
Tools für dezentrales Tracing können mit vielen verschiedenen Anwendungen und Programmiersprachen verwendet werden, sodass Entwickler sie in praktisch jedes System einbinden und Daten in einer Tracing-Anwendung anzeigen können.
Beim Code-Tracing interpretiert ein Programmierer die Ergebnisse jeder Codezeile einer Anwendung und zeichnet ihre Auswirkung per Hand auf (nicht mit einem Debugger, der den Prozess automatisiert), um die Programmausführung nachzuverfolgen.
Das manuelle Tracing kleiner Code-Blöcke kann effizienter sein, da der Programmierer nicht das gesamte Programm ausführen muss, um die Auswirkungen kleiner Änderungen zu prüfen.
Daten-Tracing trägt dazu bei, die Genauigkeit und Datenqualität kritischer Datenelemente zu überprüfen, sie zu ihren Quellsystemen zurückzuverfolgen und sie mit statistischen Methoden zu überwachen und zu verwalten. Normalerweise besteht die beste Methode zur Überprüfung der Genauigkeit darin, Operationen bis zu ihrem Ursprung zurückzuverfolgen und mithilfe von Quelldaten zu validieren – dies hat sich in der Vergangenheit jedoch bei umfangreichen operativen Prozessen als nicht kosteneffizient erwiesen. Stattdessen kann die statistische Prozesskontrolle (Statistical Process Control, SPC) für Priorisierung, Tracing, Monitoring und Kontrolle von kritischen Datenelementen genutzt werden.
Unter einer Programm-Trace versteht man einen Index der während der Ausführung einer Anwendung ausgeführten Anweisungen und referenzierten Daten. Zu den Informationen in einer Programm-Trace gehören u. a. der Programmname, die Sprache und die ausgeführte Quellanweisung. Programm-Traces werden beim Debugging einer Anwendung verwendet.
Bei Anwendungen, die sich aus verschiedenen Programmiersprachen und verteilten Microservices zusammensetzen und von ebenso global verteilten Entwicklern geschrieben wurden, ist es ungemein hilfreich, ein offenes, herstellerunabhängiges Framework an der Hand zu haben, mit dem sich die Anwendungen instrumentieren lassen.
OpenTelemetry ermöglicht dies bei einer Vielzahl von Programmiersprachen automatisch; nur bei einigen muss die Instrumentierung manuell erfolgen.
Jaeger und Zipkin sind zwei beliebte Open Source-Tools für das Anforderungs-Tracing mit ähnlichen Komponenten: Collector, Datenspeicher, Abfrage-API und Web-UI.
Die Funktionsweise gestaltet sich bei beiden wie folgt: Ausgehende Anforderungen werden zusammen mit der Anwendung nachverfolgt. Der Collector zeichnet die Daten auf, korreliert sie zwischen verschiedenen Traces und überträgt sie an eine Datenbank, aus der sie über die UI abgefragt und analysiert werden können.
Jaeger und Zipkin unterscheiden sich in ihrer Architektur und den unterstützten Programmiersprachen: Jaeger ist in Go implementiert, Zipkin in Java. Zipkin unterstützt praktisch jede Programmiersprache mit dedizierten Bibliotheken für Java, Javascript, C, C++, C#, Python, Go, Scala und anderen. Bei Jaeger ist die Liste der unterstützten Sprachen kürzer: C#, Java, Node.js, Python und Go.
Kafka ist eine verteilte Streaming-Plattform mit hohem Durchsatz und geringer Latenz für Echtzeit-Datenfeeds, die häufig in Microservice-Architekturen eingesetzt wird. Verwendet wird sie wie folgt:
Kafka nutzt „Topics“ – ein Kategorie- oder Feed-Name, unter dem Datensätze veröffentlicht werden – zum Abstrahieren von Datensatzströmen. Für jedes Topic legt Kafka ein partitioniertes Log an, bei dem es sich um eine geordnete, laufend erweiterte Abfolge von Datensätzen handelt, das als externes Commit-Log für ein verteiltes System dienen kann.
Microservices sind für Cloud-basierte Apps heute Standard. Mit der zunehmenden Komplexität dieser Systeme bietet Distributed Tracing von Anforderungen einen enormen Vorteil gegenüber dem älteren Suchansatz (der sprichwörtlichen Suche nach der Nadel im Heuhaufen) beim Aufspüren von Problemen, die Services stören könnten.
Wenn ihr für ein Microservice-basiertes System verantwortlich seid, wird euch die Einführung dieses leistungsfähigen Tools in eurem Unternehmen die Arbeit enorm erleichtern. Umso mehr gilt dies bei Umsetzung mit Splunk Observability. Hier könnt ihr die Lösung testen und in Aktion erleben, wie ihr damit eine Echtzeit-Ansicht eurer Tracing-Telemetrie erhaltet und auf diese Weise Probleme deutlich schneller behebt.
Ihr habt einen Fehler entdeckt oder eine Anregung? Bitte lasst es uns wissen und schreibt eine E-Mail an ssg-blogs@splunk.com.
Dieser Beitrag spiegelt nicht zwingend die Position, Strategie oder Meinung von Splunk wider.
Weltweit führende Unternehmen verlassen sich auf Splunk, ein Cisco-Unternehmen, um mit unserer Plattform für einheitliche Sicherheit und Observability, die auf branchenführender KI basiert, kontinuierlich ihre digitale Resilienz zu stärken.
Unsere Kunden vertrauen auf die preisgekrönten Security- und Observability-Lösungen von Splunk, um ihre komplexen digitalen Umgebungen ungeachtet ihrer Größenordnung zuverlässig zu sichern und verbessern.