Mit Distributed Tracing, dem leistungsstarken Diagnosetool für Hybrid- wie Microservices-Umgebungen, können Sie Performance-Probleme von einem zentralen Ort aus untersuchen. In einem Distributed Trace werden Datensätze zu Events in unterschiedlichen Komponenten eines verteilten Systems konsolidiert. 

Dieser Artikel befasst sich mit folgenden Themen:

  • Was ist Distributed Tracing und wie wird es eingesetzt
  • Struktur eines Distributed Trace einschließlich Spans und Transaktionen, mit Beispielen in New Relic
  • Wie Trace-Kontext zwischen verschiedenen Services übertragen wird sowie Infos zum Formatstandard W3C Trace Context
  • Headbasiertes und tailbasiertes Sampling: Pro und Kontra

Was ist Distributed Tracing?

In einer Distributed Trace werden Datensätze zu Events in unterschiedlichen Komponenten eines verteilten Systems konsolidiert.  Diese Events werden durch eine einzelne Operation ausgelöst, z. B. durch Klicken auf eine Web-Schaltfläche, und sie können sich über mehrere Prozesse, Netzwerke und Sicherheitssysteme erstrecken. Sehen wir uns zuerst die einzelnen Begriffe an:

  • Distributed, also „Verteilt“, bezieht sich auf verteilte Systeme. Solche Systeme bestehen aus voneinander unabhängigen Komponenten, die über Anfragen kommunizieren und so eine Anwendung bilden. 
  • Tracing bezieht sich auf die Traces, die den gesamten Pfad einer Anfrage von Service zu Service nachverfolgen. 

Distributed Tracing ist wesentlicher Bestandteil zentraler Plattformen für das Application Performance Monitoring (APM). In Kombination mit anderen Observability-Tools wie Metriken, Logs und Alerts liefert es umfassende Einblicke in den Health- und Performance-Status Ihres gesamten Anwendungs-Stacks. Aus dem Distributed Tracing lassen sich zwei wesentliche Angaben ableiten:

  • Der Pfad einer Serviceanfrage in einem verteilten System
  • Die jeweilige Zeitspanne bis zum Abschluss einer Serviceanfrage

Beim Monitoring von Microservice-Architekturen lassen sich mit Distributed Tracing Fehlerquellen und Performance-Bremsen leichter identifizieren. Diese schematische Darstellung zeigt, wie das Distributed Tracing in New Relic abläuft:

Sie fragen sich, wie Sie Distributed Tracing in der Praxis einsetzen können? Die Traces selbst enthalten bereits alle relevanten Daten zur Fehlerquellenanalyse, aber Tracing-Tools bieten zusätzlich Folgendes:

  • Einfache Bereitstellung und Instrumentierung
  • Visualisierung und Abfragen
  • Konfiguration und Flexibilität

Die Struktur einer Distributed Trace

In New Relic erfassen Distributed Traces drei verschiedene Datentypen:

  • Ein Span ist eine definierte, zeitlich begrenzte Operation, die einen Teil des Workflows bildet. Span-Operationen umfassen beispielsweise Datastore-Abfragen, browserseitige Interaktionen, Zeitverfolgung auf Methodenebene, Aufrufe anderer Services sowie Lambda-Funktionen. Ein Beispiel wäre ein Span in einem HTTP-Service, der am Anfang einer HTTP-Anfrage erstellt und bei Ausgabe einer Antwort vom HTTP-Server beendet werden soll. Span-Attribute enthalten wichtige Informationen zur Operation, z. B. Dauer und Host-Daten.
  • Eine Transaktion ist eine logische Arbeitseinheit in einer Software-Anwendung, z. B. eine HTTP-Anfrage, eine SQL-Abfrage, ein Hintergrundprozess, eine Message-Queue-Aktivität usw. In New Relic umfasst ein Transaktions-Event beispielsweise Informationen zur App, Datenbankaufrufe, Transaktionsdauer und etwaige Fehler.
  • Kontextuelle Metadaten zeigen die Berechnungen zu einem Trace sowie die Beziehungen zwischen seinen Spans an. Zusätzlich werden angezeigt: die Dauer von Traces, alle zu einem Trace gehörenden Entitäten, die Anzahl dieser Entitäten, die Startzeit des Trace in Millisekunden sowie die IDs der Parent Spans und Child Spans, die alle Span-Beziehungen in einem Trace repräsentieren.

Mehr zu Spans

Ein Span in einem Distributed Trace steht für eine einzelne Arbeitseinheit und die Verarbeitungsdauer einer Anfrage durch einen Service. Die Spans in eines Trace sind als Baumstruktur angeordnet, d. h. mehrere Child Spans können Teil eines Parent Span sein. 

Zum besseren Verständnis von Spans im Kontext des Distributed Tracing ist die Kenntnis folgender Konzepte wesentlich:

  • Die Trace-Dauer ist die Gesamtdauer eines Trace, gemessen vom Beginn des frühesten Span bis zum Abschluss des letzten Span.
  • Ein Process Entry Span ist der erste Span in der Ausführung eines logischen Codeabschnitts (z. B. eines Backend-Service oder einer Lambda-Funktion).
  • Der Process Exit Span ist entweder ein dem Entry Span übergeordneter (Parent) Span oder ein externer Aufruf (falls er über Attribute mit dem Präfix http. oder db. verfügt).
  • Ein In Process Span ist ein Span, der einen internen Methoden- und Funktionsaufruf darstellt und weder ein Exit Span noch ein Entry Span ist.
  • Ein Client Span schließlich ist ein Aufruf einer anderen Entität oder externen Abhängigkeit. Momentan gibt es zwei Arten von Client Spans. Datastore Client Spans haben Attribute mit dem Präfix db. und externe Client Spans haben Attribute mit dem Präfix http. oder verfügen über einen Child Span in einem anderen Prozess.

Hier sehen Sie ein Beispiel aus dem New Relic Dokument How trace data is structured:

Mehr zu Transaktionen

Eine Transaktion ist eine logische Arbeitseinheit in einer Software-Anwendung. Genauer gesagt besteht diese Arbeitseinheit aus den relevanten Funktions- und Methodenaufrufen. Im Zusammenhang mit dem Application Performance Monitoring bezieht sich der Begriff oft auf eine Webtransaktion, die mit dem Empfang einer Webanfrage durch die Anwendung beginnt und mit dem Versand der Antwort endet.

In ihrem Blogpost zum Distributed Tracing erläutert Erika Arnold die drei wichtigsten Verwendungsarten von Transaktionen im Distributed Tracing:

  • Transaktionen analysieren: Beim Tracing werden die Transaktionen im gesamten System beobachtet, um die System-Performance zu analysieren. Jede einzelne Transaktion trägt zur Performance bei und mit Mängeln behaftete Services können sich negativ auf alle anderen Services auswirken. 
  • Transaktionen aufzeichnen: Tracing hilft bei der Nachverfolgung einer großen Anzahl an Transaktionen. Der Tracing-Kontext, der mit einer Anfrage in einen Service gelangt, wird zu anderen Prozessen propagiert und an Transaktionsdaten angehängt. Mit diesem Kontext können Sie die Transaktionen später zusammenfügen. Der branchenweite Trend weg von monolithischen Anwendungen und hin zu Microservices macht die Nachverfolgung von Transaktionen auch über Prozessgrenzen hinweg – dort, wo Sie keine APM-Agents installieren können – immer wichtiger.
  • Transaktionen beschreiben: Tracing hilft beim Auswerten von Transaktionen und liefert Informationen wie die Art der durchgeführten Transaktionen und ihre Dauer.

Trace-Kontext zwischen Services weiterleiten

Trace-Kontext bezieht sich auf eine Reihe von HTTP-Headern in New Relic, die Daten von einem Service zu einem anderen propagieren, um End-to-End-Traces zu schaffen. Monitoring-Agents fügen diese HTTP-Header an die ausgehenden Anfragen eines Service an. HTTP-Header dienen zum Identifizieren von Software-Traces und behalten die Identifikationsmerkmale auf ihrem Weg durch verschiedene Netzwerke, Prozesse und Sicherheitssysteme bei. Zu diesen Headern gehören:

  • Jeder Trace-Span verfügt über ein Attribut vom Typ guid. Das Attribut guid des letzten Span im Prozess wird mit der ausgehenden Anfrage versandt, sodass der erste Arbeitsabschnitt im Zielservice guid als parentID-Attribut hinzufügen kann.
  • Beim Parent Type handelt es sich um die Quelle des Trace-Headers, z. B. Mobilgerät, Browser oder Ruby-App. Diese Header-Quelle wird bei der von der Anfrage ausgelösten Transaktion zum parent.type-Attribut.
  • Der Timestamp ist der UNIX-Zeitstempel in Millisekunden, der angibt, wann die Payload erstellt wurde.
  • Beim Attribut traceId handelt es sich um die eindeutige ID, die eine einzelne Anfrage beim Überqueren von prozessinternen und prozessübergreifenden Grenzen kennzeichnet. Anhand dieser ID lassen sich Spans in einer Distributed Trace verknüpfen. 
  • Die transactionId ist die eindeutige Kennung des Transaktions-Events.
  • priority bezeichnet eine zufällig generierte Rangfolge, anhand derer festgelegt wird, für welche Daten das Sampling bei Erreichen der Sampling-Limits fortgeführt wird. 
  • Der Boolesche Wert sampled teilt dem Agent mit, ob für die betreffende Anfrage Tracing-Daten erfasst werden sollen. Diese für einen kompletten Trace durch Sampling erfassten Transaktionen erhalten für das Attribut sampled den Wert true. Dies wird an alle nachfolgenden Prozesse weitergeleitet und weist so alle anderen betroffenen APM-Agents an, Spans für diesen Trace zu erfassen. Die nachfolgenden Spans erhalten dann ebenfalls den Wert true für das Attribut sampled.

So nutzen Sie W3C Trace Context in einer verteilten Umgebung

Wie sieht es aus, wenn Sie mehrere Tools in Ihrer Umgebung einsetzen? Ist der Trace-Kontext nicht standardisiert, können Ihre Traces beim Routing zwischen verschiedenen Tools von unterschiedlichen Anbietern nicht korreliert oder propagiert werden. Das stellt ein echtes Problem dar, wenn Sie eine verteilte Umgebung mit mehreren Middleware-Services und Cloud-Plattformen verwenden. Da kommt der W3C Trace Context-Standard ins Spiel: Dieser definiert ein „einheitliches Format für den Datenaustausch bei der Trace-Kontext-Propagation“. 

Der Standard sorgt dank folgender Merkmale für bessere Interoperabilität:

  • eindeutige Kennzeichnung einzelner Traces und Anfragen
  • einheitlicher Mechanismus zur Weiterleitung anbieterspezifischer Trace-Daten und Vermeidung fehlerhafter Traces, wenn an einer Transaktion mehrere Tracing-Tools beteiligt sind
  • Branchenstandard, den Intermediäre, Plattformen und Hardwareanbieter unterstützen können

Tracing-Tools, die die Vorgaben von W3C erfüllen, müssen zur Interaktion mit dem Trace-Kontext die Header traceparent und tracestate propagieren, damit die Traces nicht unterbrochen werden. Bei New Relic wird dies durch Verwendung der W3C New Relic Agents gewährleistet, die diese beiden obligatorischen Header versenden und empfangen. Der Agent versendet und empfängt zudem den Header des vorherigen New Relic Agent. New Relic unterstützt folgenden Trace-Kontext:

  • W3C traceparent kennzeichnet den gesamten Trace (Trace-ID) sowie den aufrufenden Service (Span-ID). Der Header traceparent beschreibt die Position der eingehenden Anfrage im Trace-Diagramm in einem portierbaren Format mit festgelegter Länge. traceparent muss von jedem Tracing-Tool korrekt festgelegt werden, selbst wenn es nur anbieterspezifische Informationen in tracestate verwertet.
  • W3C tracestate enthält anbieterspezifische Informationen und verfolgt den Pfad eines Trace. Der Header tracestate erweitert traceparent um anbieterspezifische Daten, die als eine Reihe von Name-Wert-Paaren dargestellt werden. Die Speicherung von Informationen in tracestate ist nicht obligatorisch.
  • Der proprietäre New Relic Header ist der ursprüngliche proprietäre Header, der für Kompatibilität mit vorherigen New Relic Agents sorgt.

Nachstehend sehen Sie ein Beispielszenario aus dem New Relic Dokument How trace context is passed between applications. Es zeigt, was passiert, wenn eine Anfrage mit einem OpenTelemetry-Tracer, einem W3C Trace Context-konformen New Relic Agent und einem älteren New Relic Agent in Kontakt kommt.

Distributed Tracing-Diagramm, das den Header-Fluss zeigt, wenn eine Anfrage drei verschiedene Agent-Typen betrifft.

Trace-Sampling: headbasiert und tailbasiert

Das im Distributed Tracing eingesetzte Trace-Sampling ist eine Methode zur Minimierung der zu erfassenden und zu speichernden Trace-Datenmenge. Indem eine relativ geringe Anzahl an Traces im Sampling erfasst wird, wird der oft mit dem Distributed Tracing in Verbindung gebrachte Overhead reduziert. Gleichzeitig erhalten Sie repräsentative Informationen zur System-Performance. Dazu werden zwei verschiedene Trace-Sampling-Methoden verwendet: head- und tailbasiertes Sampling.

Headbasiertes Sampling

Beim headbasierten Sampling werden nach dem Zufallsprinzip Traces zur Erfassung und Speicherung am Anfang – dem „Head“ – der Trace ausgewählt. Damit lässt sich eine repräsentative Stichprobe der Aktivitäten erstellen, ohne dass es zu Speicher- und Performance-Problemen kommt. Der Trace-Ursprung – der erste in einer Distributed Trace nachverfolgte Service – wählt zufällige Anfragen zum Tracing aus. Diese Auswahl wird durch die nachfolgenden Services propagiert, die von der Anfrage betroffen sind, sodass alle Spans des Trace im Tracing-Tool zur Verfügung stehen. 

Hierzu gehört auch das adaptive Sampling, das auf das headbasierte Sampling angewendet wird. Dabei passen die APM-Agents die Höchstzahl der zu erfassenden Transaktionen an Änderungen im Transaktions-Throughput an. Angenommen, das Limit ist 10 Traces pro Minute. Der Agent verteilt die Erfassung dieser 10 Traces also auf eine Minute, um eine im Zeitverlauf repräsentative Stichprobe zu erhalten. Die Rate orientiert sich an Änderungen im Transaktions-Throughput. Wenn in der Minute zuvor z. B. 100 Transaktionen stattfanden, würde der Agent von einem ähnlichen Throughput für die kommende Minute ausgehen und jede zehnte Transaktion zum Tracing auswählen.

Tailbasiertes Sampling

Im Gegensatz zum headbasierten Sampling finden alle Entscheidungen zur Trace-Retention beim tailbasierten Sampling erst nach der Ankunft aller Spans in einem Trace statt, also am Ende („Tail“). 

Headbasiertes und tailbasiertes Sampling: Pro und Kontra

 

Headbasiertes Sampling

Tailbasiertes Sampling

Pro

  • Ideal für Anwendungen mit geringerem Transaktionsdurchsatz
  • Schnell und mit minimalem Aufwand einsatzbereit
  • Gut geeignet für Umgebungen mit einer Mischung aus monolithischen Anwendungen und Microservices
  • Keine oder nur minimale Auswirkungen auf die Anwendungs-Performance
  • Kosteneffiziente Lösung zur Übermittlung von Tracing-Daten an Drittanbietertools
  • Sampling mit statistischer Relevanz, das angemessene Transparenz für verteilte Systeme liefert
  • Observability und Analyse für 100 % aller Traces
  • Sampling vollständig abgeschlossener Traces
  • Schnellere Visualisierung von Traces, die Fehler oder ungewöhnliche lange Verarbeitungszeiten aufweisen 

Kontra

  • Auswahl der Trace-Daten nach dem Zufallsprinzip
  • Trace-Sampling vor Durchlauf eines Trace durch die Gesamtheit der Services, dadurch keine direkte Bestimmung der Auftrittspunkte von Problemen möglich
  • Traces mit Fehlern oder ungewöhnlicher hoher Latenz werden potenziell nicht im Sample-Datensatz erfasst
  • Ausführung von Sampling-Software erfordert u. U. Gateways, Proxys und Satellites
  • In manchen Fällen vergleichsweise aufwendige Verwaltung und Skalierung von Drittanbietersoftware
  • Erhöhter Kostenaufwand aufgrund höherer Mengen an übermittelten und gespeicherten Daten

 

Choosing a distributed tracing tool

Selecting the right distributed tracing tools is paramount for achieving clear visibility into application performance. A distributed tracing tool should offer more than just data collection; it should empower engineers by transforming raw data into actionable insights. When choosing such a tool, consider the following:

  • Transparency in performance claims: Ensure the tool provides accurate and honest insights about its performance capabilities.
  • Straightforward pricing: Look for clear, no-fluff pricing models that align with your usage needs and offer real value.
  • Actionable insights: Choose a tool that turns data into practical, empowering insights, facilitating informed decision-making.
  • Comprehensive language support: The tool should support a wide range of programming languages to accommodate diverse development environments.
  • Scalability: Opt for a solution that can efficiently scale with your infrastructure as it grows and evolves.
  • Real-time visualization: It's crucial to have a tool that offers real-time visualizations of your system’s health for immediate insights.
  • Alignment with team needs: The tool should resonate with your team's unique operational requirements and foster an environment conducive to learning and innovation.
  • Streamlined troubleshooting: Prioritize tools that simplify the process of identifying and resolving performance issues.
  • Continuous learning and optimization: Look for features that encourage ongoing system optimization and learning opportunities for the team.