Verteiltes OpenTelemetry-Tracing in Dataflow verwenden

In verteilten Streamingpipelines ist es schwierig, nur anhand aggregierter Messwerte zu ermitteln, welche bestimmte Phase, welcher externe Dienstaufruf oder welcher Worker-Shuffle Latenzspitzen verursacht. Wenn Sie OpenTelemetry-Distributed Tracing in Dataflow verwenden, können Sie einzelne Elemente End-to-End über Pipeline-Transformationen, Netzwerkgrenzen und integrierte Dienste hinweg mit einer einzigen Trace-ID verfolgen. So können Sie Leistungsengpässe ermitteln, die Kosten für die Pipelineausführung optimieren und komplexe Streamingarbeitslasten beheben.

Mit den folgenden Informationen können Sie OpenTelemetry Distributed Tracing in Ihren Apache Beam-Pipelines aktivieren, konfigurieren und verwenden.

Anwendungsfälle

Verteiltes Tracing in Dataflow ist in den folgenden Szenarien besonders nützlich:

  • Streaming-Arbeitslasten:Verfolgen Sie die Verarbeitungszeit pro Element und ermitteln Sie Phasen, die Verzögerungen oder Latenzspitzen verursachen.
  • Tracing von externen Dienstaufrufen im Nutzercode:Messen Sie die Ausführungsdauer, wenn in Ihrer Pipeline externe Datenbanken, Mikroservices oder APIs für generative KI im Nutzercode (z. B. in einem DoFn) aufgerufen werden. Sie können beispielsweise die Zeit für Aufrufe von externen Datenbanken (z. B. Spanner oder Bigtable), Mikroservices (über HTTP oder gRPC) oder APIs für generative KI (z. B. Gemini) messen. Weitere Informationen finden Sie unter Benutzerdefinierte Traces in DoFn-Klassen schreiben und Mit Clientbibliotheken integrieren.
  • Kosten- und Leistungsoptimierung:Die Dauer bestimmter Vorgänge in PTransform-Instanzen unter realer Last wird angezeigt, um ineffiziente Serialisierung, langsame Abfragen oder Konflikte bei Worker-Threads zu erkennen.
  • Komplexe Agent- und Workflow-Pipelines:Sie erhalten Einblick in Pipelines mit mehreren Verzweigungen, iterativen Agent-Schleifen und Joins, um zu ermitteln, welche Transformation oder Verzweigung am längsten dauert.

Beschränkungen

Für das verteilte Tracing in Dataflow gelten die folgenden Einschränkungen:

  • Diese Funktion ist nur für Streamingpipelines konzipiert und wurde nur für diese Pipelines getestet.
  • Für diese Funktion benötigen Sie das Beam SDK for Java (Version 2.76.0 oder höher).
  • Für die kontextübergreifende Weitergabe von Daten zwischen Phasen ist der Streaming Java Runner (früher Runner v1) erforderlich. Der Portable Runner (früher Runner v2) setzt den Trace-Kontext an den Grenzen von Shuffle-Vorgängen zurück.
  • Das integrierte OpenTelemetry-Tracing in Beam-E/A-Connectors wird nur für die folgenden Connectors unterstützt: Pub/Sub, Apache Kafka und Spanner-Änderungsstreams. Bei Apache Kafka wird das Tracing nur unterstützt, wenn Sie den KafkaIO-Connector direkt verwenden. Sie wird nicht unterstützt, wenn Sie Managed I/O for Kafka verwenden. Andere E/A-Connectors (z. B. BigtableIO) haben keine integrierte Connector-Ablaufverfolgung. Wenn Sie Vorgänge mit diesen Diensten verfolgen möchten, müssen Sie Ihre Aufrufe im Nutzercode instrumentieren (z. B. in einem DoFn).
  • Bei Vorgängen, bei denen mehrere Elemente zu einem kombiniert werden, wird kein einzelner Trace-Kontext weitergegeben. Dazu gehören Vorgänge wie GroupByKey, CoGroupByKey und Combine.
  • Der Kontext wird nicht zwischen dem Festlegen eines Timers (Timer.set()) und der Ausführung der Callback-Methode @OnTimer weitergegeben.

Vorbereitung

Wenn Sie Distributed Tracing in Dataflow verwenden möchten, muss Ihre Umgebung die folgenden Anforderungen erfüllen:

  1. Ihr Job muss die folgenden Anforderungen erfüllen:
  2. Wenn Sie Traces in Cloud Trace exportieren, aktivieren Sie die Telemetry API ( telemetry.googleapis.com) in Ihrem Google Cloud Projekt.

    Rollen, die zum Aktivieren von APIs erforderlich sind

    Zum Aktivieren von APIs benötigen Sie die Berechtigung serviceusage.services.enable. Wenn Sie das Projekt erstellt haben, haben Sie diese Berechtigung wahrscheinlich bereits über die Rolle „Inhaber“ (roles/owner). Andernfalls können Sie diese Berechtigung über die Rolle „Service Usage-Administrator“ (roles/serviceusage.serviceUsageAdmin) erhalten. Informationen zum Zuweisen von Rollen

    API aktivieren

  3. Das Worker-Dienstkonto muss die Rolle „Cloud Trace-Agent“ (roles/cloudtrace.agent) haben, die die Berechtigung cloudtrace.traces.patch umfasst.

Wichtige Konzepte

Verteiltes Tracing basiert auf den folgenden OpenTelemetry-Konzepten:

Trace
Eine Darstellung einer vollständigen Transaktion oder eines vollständigen Workflows, wie er durch ein verteiltes System fließt. Ein Trace besteht aus einem oder mehreren Spans.
Span
Der grundlegende Baustein eines Traces, der eine einzelne Arbeitseinheit darstellt. Zum Beispiel eine Transformationsausführung, ein Methodenaufruf oder eine RPC-Anfrage.
Kontext
Der Status, der über Threads und API-Grenzen hinweg weitergegeben wird. Dazu gehören die Trace-ID und die Span-ID.
Propagator
Der Mechanismus, mit dem die Kontextdarstellung über Netzwerkbegrenzungen hinweg eingefügt und extrahiert wird. Beispiel: W3C Trace Context.
Exporter
Eine Komponente, die erfasste Span-Daten an ein Tracing-Backend wie Cloud Trace oder einen selbst gehosteten OpenTelemetry Collector sendet.
Sampler
Ein Mechanismus, der das Trace-Volumen steuert, indem er festlegt, welche Traces aufgezeichnet und exportiert werden. Beispiel: 1 %

OpenTelemetry-Tracing aktivieren

Wenn Sie OpenTelemetry-Tracing mit Standardeinstellungen aktivieren möchten, konfigurieren Sie die erforderlichen Pipelineoptionen und ‑tests, wenn Sie Ihren Job einreichen. Bei der Standardeinstellung werden Daten mit 1% Sampling in Cloud Trace exportiert.

Informationen zum Ändern von Standardwerten finden Sie unter Tracing-Attribute festlegen und ändern.

  1. Konfigurieren Sie Pipelineoptionen und ‑tests, wenn Sie Ihren Job ausführen.

    Job ohne flexible Vorlage

    Übergeben Sie beim Einreichen eines Jobs die folgenden Test-Flags:

    --experiments=enable_otel_defaults,element_metadata_supported,disable_portable_worker
    

    Job mit Flexible Vorlage

    Übergeben Sie die Tests mit dem Flag --additional-experiments im Befehl gcloud:

    gcloud dataflow flex-template run JOB_NAME \
        --template-file-gcs-location=gs://BUCKET_NAME/templates/TEMPLATE_NAME.json \
        --additional-experiments="enable_otel_defaults,element_metadata_supported,disable_portable_worker" \
        --region=REGION
    

    Ersetzen Sie Folgendes:

    • JOB_NAME: Der Name Ihres Dataflow-Jobs.
    • BUCKET_NAME: Der Cloud Storage-Bucket, der Ihre Vorlage enthält.
    • TEMPLATE_NAME: Der Name Ihrer Flex-Vorlagendatei.
    • REGION: Die Google Cloud Region, in der Sie den Job ausführen möchten.

    In diesen Tests wird das folgende Verhalten konfiguriert:

    • enable_otel_defaults: Konfiguriert die OpenTelemetry-Standardeinstellungen, einschließlich des Cloud Trace-Exporteurs und der Stichprobenerhebung mit 1 %.
    • element_metadata_supported: Ermöglicht die Serialisierung und Weitergabe von Metadaten (z. B. Trace-Kontext) über Pipelinephasen hinweg.
    • disable_portable_worker: Erzwingt die Verwendung des Dataflow Streaming Java Runner (früher Runner v1).
  2. Wenn Sie das Beam SDK for Java in Version 2.76.0 oder 2.77.0 verwenden, fügen Sie Ihrer Build-Konfiguration die erforderlichen OpenTelemetry-Abhängigkeiten hinzu.

    Aufgrund eines Problems in den Versionen 2.76.0 und 2.77.0 müssen Sie die folgenden Abhängigkeiten in Ihre pom.xml-Datei einfügen, damit Traces exportiert werden. Weitere Informationen finden Sie unter Pipelineabhängigkeiten verwalten.

    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
        <scope>compile</scope>
    </dependency>
    
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
        <version>1.62.0</version>
        <scope>compile</scope>
    </dependency>
    
    <dependency>
        <groupId>org.apache.beam</groupId>
        <artifactId>beam-sdks-java-extensions-opentelemetry-gcp-auth-extension</artifactId>
        <scope>compile</scope>
    </dependency>
    
  3. Aktivieren Sie OpenTelemetry-Tracing für unterstützte E/A-Connectors in Ihrem Pipeline-Code explizit:

    Pub/Sub

    So aktivieren Sie das Tracing beim Lesen aus oder Schreiben in Pub/Sub:

    // Reading from Pub/Sub
    pipeline.apply("ReadFromPubSub",
        PubsubIO.readMessages()
            .withEnableOpenTelemetryTracing()
            .fromSubscription("projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID"));
    
    // Writing to Pub/Sub
    pipeline.apply("WriteToPubSub",
        PubsubIO.writeStrings()
            .withEnableOpenTelemetryTracing()
            .to("projects/PROJECT_ID/topics/TOPIC_ID"));

    Ersetzen Sie Folgendes:

    • PROJECT_ID: Die ID Ihres Google Cloud -Projekts.
    • SUBSCRIPTION_ID: Der Name Ihres Pub/Sub-Abos.
    • TOPIC_ID: Der Name Ihres Pub/Sub-Themas.

    Details zur Weitergabe des Kontextes:

    • Upstream-Publisher:Wenn Sie Nachrichten aus einer externen Anwendung veröffentlichen, fügen Sie den W3C-Trace-Kontext in die Pub/Sub-Nachrichtenattribute ein. Weitere Informationen finden Sie unter OpenTelemetry-Tracing für Pub/Sub.
    • Attribute:Beam erwartet und füllt die folgenden Attribute aus:
      • googclient_traceparent: Enthält den W3C-Header traceparent.
      • googclient_tracestate: Enthält den W3C-Header tracestate.
    • Informationen zur Headerformatierung finden Sie in der W3C Trace Context-Spezifikation.

    Apache Kafka

    So aktivieren Sie das Tracing beim Lesen aus oder Schreiben in Apache Kafka:

    // Reading from Kafka
    pipeline.apply("ReadFromKafka",
        KafkaIO.<String, String>read()
            .withEnableOpenTelemetryTracing()
            // ... additional configuration
            );
    
    // Writing to Kafka
    pipeline.apply("WriteToKafka",
        KafkaIO.<String, String>write()
            .withEnableOpenTelemetryTracing()
            // ... additional configuration
            );

    Details zur Weitergabe des Kontextes:

    • KafkaIO verwendet Standard-Kafka-Datensatzheadern, um den Kontext weiterzugeben.
    • Es werden Header erwartet und eingefügt, die der W3C-Spezifikation für Trace-Kontext entsprechen:
      • traceparent: Gibt den eingehenden Trace-Kontext an.
      • tracestate: Stellt zusätzliche anbieterspezifische Routing- und Filtermetadaten bereit.

    Spanner

    So aktivieren Sie das Tracing beim Lesen aus Spanner-Änderungsstreams:

    pipeline.apply("ReadChangeStream",
        SpannerIO.readChangeStream()
            .withSpannerConfig(spannerConfig)
            .withEnableOpenTelemetryTracing(true)
            .withChangeStreamName("CHANGE_STREAM_NAME"));

    Ersetzen Sie Folgendes:

    • CHANGE_STREAM_NAME: Der Name des Spanner-Änderungsstreams.

    Details zur Spanne:

    • Wenn die Ablaufverfolgung aktiviert ist, wird für jeden Datenbankmutationsdatensatz, der vom CDC-Reader (Change Data Capture) gelesen wird, ein neuer Trace und Span gestartet.
    • Spanner-Lesepoll-Zyklen, die keine neuen Datensätze zurückgeben, können auch Trace-Spans erzeugen, die in der Cloud Trace-Benutzeroberfläche herausgefiltert werden können.

Tracing-Attribute festlegen und ändern

Wenn Sie enable_otel_defaults angeben, wendet Dataflow die folgenden Standardattribute an:

Attribut Standardwert Beschreibung
otel.traces.exporter otlp Exportiert Traces mit dem OpenTelemetry Protocol (OTLP).
otel.exporter.otlp.endpoint https://telemetry.googleapis.com Richtet sich an den Cloud Trace-OTLP-Empfänger.
google.cloud.project Ihre Projekt-ID Das Google Cloud Projekt, in dem die Traces gespeichert werden.
otel.traces.sampler.arg 0.01 (1%) Er zeichnet 1 von 100 Traces auf.
otel.service.name options.getAppName() Der Dienstname, der den Spans zugeordnet ist und aus der Pipeline-Option appName abgeleitet wird.

Wenn Sie die OpenTelemetry-Konfiguration anpassen möchten, übergeben Sie eine durch Semikolon getrennte Liste von Attributen an die Pipelineoption --openTelemetryProperties.

Im folgenden Beispiel werden alle Standardattribute explizit konfiguriert:

--openTelemetryProperties="otel.traces.exporter=otlp;otel.exporter.otlp.endpoint=https://telemetry.googleapis.com;google.cloud.project=PROJECT_ID;otel.traces.sampler.arg=0.01;otel.service.name=CUSTOM_SERVICE_NAME;otel.java.global-autoconfigure.enabled=true"

Benutzerdefinierten Dienstnamen festlegen

Standardmäßig wird der Name des Trace-Dienstes aus der Pipeline-Option appName (--appName=YourPipelineName) abgeleitet. Sie können diesen Wert überschreiben, indem Sie die Eigenschaft otel.service.name festlegen:

--openTelemetryProperties="otel.service.name=CUSTOM_SERVICE_NAME"

Stichprobenrate und Eigenschaften ändern

Wenn Sie eine höhere Sampling-Rate festlegen, erhalten Sie zuverlässigere Informationen zum Tracing. Hohe Stichprobenraten (z. B. 1.0 oder always_on) erhöhen jedoch das Volumen der an Cloud Trace gesendeten Tracedaten erheblich, was Ihre Google Cloud Kosten steigern kann. Verwenden Sie hohe Stichprobenraten nur für kurzfristiges Debugging und reduzieren Sie sie für Produktionsarbeitslasten.

Im folgenden Beispiel wird die Samplingrate explizit konfiguriert. In diesem Fall müssen Sie eine vollständige Liste der Argumente angeben:

--openTelemetryProperties="otel.traces.exporter=otlp;otel.exporter.otlp.endpoint=https://telemetry.googleapis.com;google.cloud.project=PROJECT_ID;otel.traces.sampler.arg=0.02;otel.service.name=CUSTOM_SERVICE_NAME;otel.java.global-autoconfigure.enabled=true"

In Back-Ends von Drittanbietern exportieren

So senden Sie Traces an ein externes verwaltetes Tracing-Backend oder einen selbst gehosteten OpenTelemetry Collector anstelle von Cloud Trace:

  1. Lassen Sie den folgenden Test aus: --experiments=enable_otel_defaults.
  2. Exportattribute manuell mit --openTelemetryProperties konfigurieren:

    --openTelemetryProperties="otel.traces.exporter=otlp;otel.exporter.otlp.endpoint=http://COLLECTOR_HOST:4317;otel.traces.sampler=parentbased_always_on"
    

Google Cloud -Authentifizierungserweiterung

Die Beam-Laufzeit enthält eine Google Cloud -Authentifizierungserweiterung, die automatisch OpenTelemetry Protocol-Anfragen (OTLP) authentifiziert, die an Google-APIs wie telemetry.googleapis.com gesendet werden.

  • Bei Verwendung von Standardeinstellungen:Die Google Cloud Authentifizierung wird automatisch aktiviert.
  • Beim Exportieren zu Nicht-Google-Endpunkten:Wenn enable_otel_defaults ausgelassen wird, deaktiviert Beam die Erweiterung für die Google Cloud -Authentifizierung, indem das Systemattribut google.otel.auth.target.signals=none festgelegt wird.
  • Manuelle Konfiguration:Sie können die System-Property google.otel.auth.target.signals auf none (deaktiviert) oder trace (nur für Traces aktiviert) festlegen.

Benutzerdefinierte Traces in DoFns schreiben

Sie können benutzerdefinierte Spans im Nutzercode erstellen, z. B. in einem DoFn, um bestimmte Vorgänge zu messen. Dazu gehören Aufrufe externer APIs oder rechenintensive Logik.

Beispiel für die DoFn-Implementierung

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.options.SdkHarnessOptions;
import org.apache.beam.sdk.transforms.DoFn;

public class InvestigateTransactionDoFn extends DoFn<Transaction, RiskAssessment> {
  private transient Tracer tracer;

  @Setup
  public void setup(PipelineOptions options) {
    // Obtain the Tracer instance from SdkHarnessOptions
    this.tracer = options
        .as(SdkHarnessOptions.class)
        .getOpenTelemetry()
        .getTracer("org.apache.beam.examples.adk.aml");
  }

  @ProcessElement
  public void processElement(@Element Transaction tx, OutputReceiver<RiskAssessment> out) {
    // Create and start a custom span as a child of the active context
    Span span = tracer.spanBuilder("InvestigateTransactionDoFn:Analyze")
        .setAttribute("transaction.id", tx.getId())
        .setAttribute("transaction.amount", tx.getAmount())
        .startSpan();

    // Make the span current in the execution thread
    try (Scope scope = span.makeCurrent()) {
      // Perform your logic (such as calling an external service)
      RiskAssessment assessment = analyzeTransactionWithLLM(tx);
      out.output(assessment);
    } catch (Exception e) {
      // Record exception details and set error status on the span
      span.recordException(e);
      span.setStatus(StatusCode.ERROR, e.getMessage());
      throw e;
    } finally {
      // Always end the span
      span.end();
    }
  }

  private RiskAssessment analyzeTransactionWithLLM(Transaction tx) {
    // Application analysis logic
    return new RiskAssessment();
  }
}

Mit Clientbibliotheken einbinden

Wenn ein DoFn externe Bibliotheken aufruft, die ebenfalls OpenTelemetry unterstützen, z. B. Google Cloud Clientbibliotheken, konfigurieren Sie den Client so, dass die OpenTelemetry-Instanz der Pipeline verwendet wird. So werden untergeordnete Spans direkt mit dem Pipeline-Trace verknüpft.

Beispiel für Spanner-Java-Client

Wenn Sie manuelle Spanner-Abfragen in einem DoFn ausführen und diese Spans mit dem aktiven Pipeline-Trace verknüpfen möchten, initialisieren Sie SpannerOptions mit der OpenTelemetry-Instanz der Pipeline:

import com.google.cloud.spanner.DatabaseClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.ResultSet;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.Statement;
import org.apache.beam.sdk.options.PipelineOptions;
import org.apache.beam.sdk.options.SdkHarnessOptions;
import org.apache.beam.sdk.transforms.DoFn;

public class SpannerEnrichmentDoFn extends DoFn<String, String> {
  private transient Spanner spanner;
  private transient DatabaseClient dbClient;
  private final String instanceId;
  private final String databaseId;

  public SpannerEnrichmentDoFn(String instanceId, String databaseId) {
    this.instanceId = instanceId;
    this.databaseId = databaseId;
  }

  @Setup
  public void setup(PipelineOptions pipelineOptions) {
    // Build SpannerOptions leveraging the pipeline's active OpenTelemetry instance
    SpannerOptions options =
        SpannerOptions.newBuilder()
            .setOpenTelemetry(pipelineOptions
                .as(SdkHarnessOptions.class)
                .getOpenTelemetry())
            .setEnableEndToEndTracing(true)
            .setEnableExtendedTracing(true)
            .build();

    this.spanner = options.getService();
    DatabaseId db = DatabaseId.of(options.getProjectId(), instanceId, databaseId);
    this.dbClient = spanner.getDatabaseClient(db);
  }

  @ProcessElement
  public void processElement(@Element String inputId, OutputReceiver<String> out) {
    Statement statement = Statement.newBuilder(
        "SELECT Details FROM AccountTable WHERE AccountId = @id")
        .bind("id").to(inputId)
        .build();

    // Spanner queries automatically generate child spans linked to the active trace
    try (ResultSet rs = dbClient.singleUse().executeQuery(statement)) {
      while (rs.next()) {
        out.output(rs.getString("Details"));
      }
    }
  }

  @Teardown
  public void tearDown() {
    if (spanner != null) {
      spanner.close();
    }
  }
}

Kontextweitergabe über Phasen hinweg

In verteilten Datenpipelines ist es für die End-to-End-Transparenz unerlässlich, den Trace-Kontext über Worker-Grenzen und Shuffle-Vorgänge hinweg zu übertragen.

Wenn Sie Redistribute.arbitrarily() in Ihre Pipeline einfügen, wird der aktive Trace-Kontext in Elementmetadaten serialisiert, über das Netzwerk übertragen und auf der empfangenden Worker-VM deserialisiert. So wird sichergestellt, dass die Ablaufverfolgung über alle Phasen hinweg fortgesetzt wird.

Das folgende Beispiel veranschaulicht, wie der Trace-Kontext über Pipelinephasen hinweg weitergegeben wird:

  1. Kontext extrahieren:KafkaIO.read() liest einen eingehenden Datensatz und extrahiert den Trace-Kontext aus den Nachrichtenheadern.
  2. Spans verarbeiten und erstellen:DoFn A verarbeitet das Element in einem aktiven Trace-Span.
  3. Propagate across workers (Über Worker weitergeben): Redistribute.arbitrarily() serialisiert den aktiven Trace-Kontext in den Elementmetadaten und verschiebt das Element über das Netzwerk.
  4. Trace fortsetzen:DoFn B empfängt das Element auf einer Worker-VM, deserialisiert den Trace-Kontext und setzt die Verarbeitung mit derselben Trace-ID fort.

Traces in Cloud Trace ansehen

Nachdem Sie das Tracing aktiviert haben, können Sie sich die Pipeline-Traces in der Google Cloud -Konsole ansehen:

  1. Rufen Sie in der Google Cloud Console die Seite Trace Explorer auf.

    Zum Trace Explorer

  2. Filtern Sie im Filterfeld nach Service: SERVICE_NAME. Dieser SERVICE_NAME entspricht Ihrer Pipeline appName oder dem konfigurierten otel.service.name.

    Alternativ können Sie nach einer bestimmten Trace-ID suchen.

  3. Wählen Sie einen Trace aus der Ergebnisliste aus, um die detaillierte Zeitachse aufzurufen.

    Das Zeitachsendiagramm enthält Folgendes:

    • Runner-Spans:Spans, die von der Beam-Laufzeit generiert werden. Beispiel: PubSubIO.Read und ProcessElement.
    • Benutzerdefinierte Spans:Spans, die in Ihren DoFn-Implementierungen erstellt wurden. Beispiel: InvestigateTransactionDoFn:Analyze.
    • Spans der Clientbibliothek:Untergeordnete Spans, die von integrierten Clientbibliotheken generiert werden. Beispiel: Spanner-RPC-Abfragen.

      Ansicht von Traces in der Console.

Nächste Schritte