Freigeben über


Beispiel: Verwenden von OpenTelemetry mit Prometheus, Grafana und Jaeger

In diesem Beispiel werden Prometheus für die Metriksammlung, Grafana zum Erstellen eines Dashboards und Jaeger verwendet, um die verteilte Ablaufverfolgung anzuzeigen.

1. Erstellen des Projekts

Erstellen Sie ein einfaches Web-API-Projekt, indem Sie die Vorlage ASP.NET Core Empty in Visual Studio oder den folgenden .NET CLI-Befehl verwenden:

dotnet new web

2. Hinzufügen von Metriken und Aktivitätsdefinitionen

Der folgende Code definiert eine neue Metrik (greetings.count) für die Anzahl der Aufrufe der API und eine neue Aktivitätsquelle (OtPrGrYa.Example).

// Custom metrics for the application
var greeterMeter = new Meter("OtPrGrYa.Example", "1.0.0");
var countGreetings = greeterMeter.CreateCounter<int>("greetings.count", description: "Counts the number of greetings");

// Custom ActivitySource for the application
var greeterActivitySource = new ActivitySource("OtPrGrJa.Example");

3. Erstellen eines API-Endpunkts

app.MapGet("/", SendGreeting);
async Task<String> SendGreeting(ILogger<Program> logger)
{
    // Create a new Activity scoped to the method
    using var activity = greeterActivitySource.StartActivity("GreeterActivity");

    // Log a message
    logger.LogInformation("Sending greeting");

    // Increment the custom counter
    countGreetings.Add(1);

    // Add a tag to the Activity
    activity?.SetTag("greeting", "Hello World!");

    return "Hello World!";
}

Hinweis

Die API-Definition verwendet keine spezifischen OpenTelemetry-Elemente. Für Einblick werden die .NET-APIs verwendet.

4. Verweisen auf die OpenTelemetry-Pakete

Verwenden Sie den NuGet-Paket-Manager oder die Befehlszeile, um die folgenden NuGet-Pakete hinzuzufügen:

<ItemGroup>
    <PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
    <PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
</ItemGroup>

Hinweis

Verwenden Sie die neuesten Versionen, da die OTel-APIs ständig weiterentwickelt werden.

5. Konfigurieren von OpenTelemetry mit den richtigen Anbietern

var tracingOtlpEndpoint = builder.Configuration["OTLP_ENDPOINT_URL"];
var otel = builder.Services.AddOpenTelemetry();

// Configure OpenTelemetry Resources with the application name
otel.ConfigureResource(resource => resource
    .AddService(serviceName: builder.Environment.ApplicationName));

// Add Metrics for ASP.NET Core and our custom metrics and export to Prometheus
otel.WithMetrics(metrics => metrics
    // Metrics provider from OpenTelemetry
    .AddAspNetCoreInstrumentation()
    .AddMeter(greeterMeter.Name)
    // Metrics provides by ASP.NET Core in .NET 8
    .AddMeter("Microsoft.AspNetCore.Hosting")
    .AddMeter("Microsoft.AspNetCore.Server.Kestrel")
    .AddPrometheusExporter());

// Add Tracing for ASP.NET Core and our custom ActivitySource and export to Jaeger
otel.WithTracing(tracing =>
{
    tracing.AddAspNetCoreInstrumentation();
    tracing.AddHttpClientInstrumentation();
    tracing.AddSource(greeterActivitySource.Name);
    if (tracingOtlpEndpoint != null)
    {
        tracing.AddOtlpExporter(otlpOptions =>
         {
             otlpOptions.Endpoint = new Uri(tracingOtlpEndpoint);
         });
    }
    else
    {
        tracing.AddConsoleExporter();
    }
});

Dieser Code verwendet ASP.NET Core Instrumentierung, um Metriken und Aktivitäten aus ASP.NET Core abzurufen. Außerdem werden die Anbieter Metrics und ActivitySource für Metriken bzw. die Ablaufverfolgung registriert.

Der Code verwendet den Prometheus-Exporter für Metriken, der ASP.NET Core zum Hosten des Endpunkts verwendet. Daher müssen Sie auch Folgendes hinzufügen:

// Configure the Prometheus scraping endpoint
app.MapPrometheusScrapingEndpoint();

6. Ausführen des Projekts

Führen Sie das Projekt aus, und greifen Sie dann mit dem Browser oder curl auf die API zu.

curl -k http://localhost:7275

Jedes Mal, wenn Sie die Seite anfordern, wird die Anzahl der gesendeten Begrüßungen erhöht. Sie können mit derselben Basis-URL mit dem Pfad /metrics auf den Metrikenendpunkt zugreifen.

6.1 Protokollausgabe

Die Protokollierungsanweisungen aus dem Code werden mit ILogger ausgegeben. Standardmäßig ist der Konsolenanbieter aktiviert, sodass die Ausgabe an die Konsole weitergeleitet wird.

Es gibt eine Reihe von Optionen, wie Protokolle von .NET ausgehen können:

  • Die Ausgaben stdout und stderr werden von Containersystemen wie Kubernetes an Protokolldateien umgeleitet.
  • Unter Verwendung von Protokollierungsbibliotheken, die in ILogger integriert werden, unter anderem Serilog oder NLog.
  • Verwenden von Protokollierungsanbietern für OTel wie OTLP oder dem weiter unten gezeigten Azure Monitor-Exportprogramm.

6.2 Zugreifen auf die Metriken

Sie können über den Endpunkt /metrics auf die Metriken zugreifen.

curl -k https://localhost:7275/
Hello World!

curl -k https://localhost:7275/metrics
# TYPE greetings_count counter
# HELP greetings_count Counts the number of greetings
greetings_count 1 1686894204856

# TYPE current_connections gauge
# HELP current_connections Number of connections that are currently active on the server.
current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856
current_connections{endpoint="[::1]:7275"} 0 1686894204856
current_connections{endpoint="[::1]:5212"} 1 1686894204856
...

Die Metrikausgabe ist eine Momentaufnahme der Metriken zum Zeitpunkt der Anforderung des Endpunkts. Die Ergebnisse werden im Prometheus-Expositionsformat bereitgestellt, das von Menschen lesbar ist, aber von Prometheus besser verstanden wird. Dieses Thema wird in der nächsten Stufe behandelt.

6.3 Zugreifen auf die Ablaufverfolgung

Wenn Sie sich die Konsole für den Server ansehen, sehen Sie die Ausgabe des Konsolenablaufverfolgungsexportierers, der die Informationen in einem lesbaren Format ausgibt. Dies sollte zwei Aktivitäten anzeigen, eine aus Ihrer benutzerdefinierten ActivitySource und die andere aus ASP.NET Core:

Activity.TraceId:            2e00dd5e258d33fe691b965607b91d18
Activity.SpanId:             3b7a891f55b97f1a
Activity.TraceFlags:         Recorded
Activity.ParentSpanId:       645071fd0011faac
Activity.ActivitySourceName: OtPrGrYa.Example
Activity.DisplayName:        GreeterActivity
Activity.Kind:               Internal
Activity.StartTime:          2023-06-16T04:50:26.7675469Z
Activity.Duration:           00:00:00.0023974
Activity.Tags:
    greeting: Hello World!
Resource associated with Activity:
    service.name: OTel-Prometheus-Grafana-Jaeger
    service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.5.0

Activity.TraceId:            2e00dd5e258d33fe691b965607b91d18
Activity.SpanId:             645071fd0011faac
Activity.TraceFlags:         Recorded
Activity.ActivitySourceName: Microsoft.AspNetCore
Activity.DisplayName:        /
Activity.Kind:               Server
Activity.StartTime:          2023-06-16T04:50:26.7672615Z
Activity.Duration:           00:00:00.0121259
Activity.Tags:
    net.host.name: localhost
    net.host.port: 7275
    http.method: GET
    http.scheme: https
    http.target: /
    http.url: https://localhost:7275/
    http.flavor: 1.1
    http.user_agent: curl/8.0.1
    http.status_code: 200
Resource associated with Activity:
    service.name: OTel-Prometheus-Grafana-Jaeger
    service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.5.0

Die erste ist die innere benutzerdefinierte Aktivität, die Sie erstellt haben. Die zweite wird von ASP.NET für die Anforderung erstellt und enthält Tags für die HTTP-Anforderungseigenschaften. Sie werden sehen, dass beide über denselben TraceId verfügen, der eine einzelne Transaktion identifiziert, und in einem verteilten System verwendet werden kann, um die Ablaufverfolgungen von jedem Dienst zu korrelieren, der an einer Transaktion beteiligt ist. Die IDs werden als HTTP-Header übertragen. ASP.NET Core weist eine TraceId zu, wenn keine vorhanden ist, wenn eine Anforderung empfangen wird. HttpClient enthält die Header für ausgehende Anforderungen standardmäßig. Jede Aktivität verfügt über eine SpanId, die die Kombination von TraceId und SpanId ist, die jede Aktivität eindeutig identifiziert. Die Greeter-Aktivität wird der HTTP-Aktivität über ParentSpanId übergeordnet, die der SpanId der HTTP-Aktivität zugeordnet ist.

In einer späteren Phase werden Sie diese Daten in Jaeger einspeisen, um die verteilten Ablaufverfolgungen zu visualisieren.

7. Sammeln von Metriken mit Prometheus

Prometheus ist ein Metriksammlungs-, Aggregations- und Zeitreihendatenbanksystem. Sie konfigurieren es mit den Metrikendpunkten für jeden Dienst, und die Werte werden in regelmäßigen Abständen in der Zeitreihendatenbank gespeichert. Anschließend können Sie sie bei Bedarf analysieren und verarbeiten.

Die Metrikdaten, die im Prometheus-Format verfügbar gemacht werden, sind eine Point-in-Time-Momentaufnahme der Metriken des Prozesses. Jedes Mal, wenn eine Anforderung an den Metrikendpunkt gesendet wird, werden die aktuellen Werte gemeldet. Während aktuelle Werte interessant sind, werden sie im Vergleich zu historischen Werten wertvoller, um Trends zu erkennen und zu erkennen, ob Werte anomal sind. In der Regel weisen Dienste Nutzungsspitzen auf, die von der Tageszeit oder von Weltereignissen abhängen, z. B. Weihnachtseinkäufen. Indem Sie die Werte mit historischen Trends vergleichen, können Sie erkennen, ob sie anormal sind oder ob sich eine Metrik im Laufe der Zeit langsam verschlechtert.

Der Prozess speichert keinen Verlauf dieser Metrikmomentaufnahmen. Das Hinzufügen dieser Funktion zum Prozess kann ressourcenintensiv sein. Außerdem verfügen Sie in einem verteilten System in der Regel über mehrere Instanzen jedes Knotens, sodass Sie die Metriken aller Knoten sammeln und dann mit ihren historischen Werten aggregieren und vergleichen können.

7.1 Installieren und Konfigurieren von Prometheus

Laden Sie Prometheus für Ihre Plattform von https://prometheus.io/download/ herunter, und extrahieren Sie den Inhalt des Downloads.

Sehen Sie sich den oberen Rand der Ausgabe Ihres ausgeführten Servers an, um die Portnummer für den HTTP-Endpunkt abzurufen. Beispiel:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7275
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5212

Ändern Sie die Prometheus YAML-Konfigurationsdatei, um den Port für Ihren HTTP-Scrapingendpunkt anzugeben und ein niedrigeres Scrapingintervall festzulegen. Beispiel:

  scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    scrape_interval: 1s # poll very quickly for a more responsive demo
    static_configs:
      - targets: ["localhost:5212"]

Starten Sie Prometheus, und suchen Sie in der Ausgabe nach dem Port, an dem es ausgeführt wird, in der Regel 9090:

>prometheus.exe
...
ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090

Öffnen Sie diese URL in Ihrem Browser. Auf der Prometheus-Benutzeroberfläche sollten Sie jetzt in der Lage sein, Ihre Metriken abzufragen. Verwenden Sie die hervorgehobene Schaltfläche in der folgenden Abbildung, um den Metrik-Explorer zu öffnen, der alle verfügbaren Metriken anzeigt.

Prometheus-Metrik-Explorer

Wählen Sie die greetings_count-Metrik aus, um ein Diagramm mit Werten anzuzeigen.

Diagramm von greetings_count

8. Verwenden von Grafana zum Erstellen eines Metrik-Dashboards

Grafana ist ein Dashboarding-Produkt, das Dashboards und Warnungen basierend auf Prometheus oder anderen Datenquellen erstellen kann.

Laden Sie die OSS-Version von Grafana herunter, und installieren Sie sie von https://grafana.com/oss/grafana/, indem Sie die Anweisungen für Ihre Plattform befolgen. Nach der Installation wird Grafana in der Regel an Port 3000 ausgeführt. Öffnen Sie also http://localhost:3000 in Ihrem Browser. Sie müssen sich anmelden. Der Standardbenutzername und das Standardkennwort sind beide admin.

Wählen Sie im Hamburger-Menü Verbindungen aus, und geben Sie dann den Text prometheus ein, um Ihren Endpunkttyp auszuwählen. Wählen Sie Prometheus-Datenquelle erstellen aus, um eine neue Datenquelle hinzuzufügen.

Grafana-Verbindung mit Prometheus

Sie müssen die folgenden Eigenschaften festlegen:

  • Prometheus-Server-URL: http://localhost:9090/ Port nach Bedarf ändern

Wählen Sie Speichern und testen aus, um die Konfiguration zu überprüfen.

Sobald Sie eine Erfolgsmeldung erhalten haben, können Sie ein Dashboard konfigurieren. Klicken Sie auf den Link zum Erstellen eines Dashboards, der im Popup für die Erfolgsmeldung angezeigt wird.

Wählen Sie Visualisierung hinzufügen und dann die Prometheus-Datenquelle aus, die Sie soeben als Datenquelle hinzugefügt haben.

Der Dashboard-Panel-Designer sollte angezeigt werden. In der unteren Hälfte des Bildschirms können Sie die Abfrage definieren.

Grafana-Abfrage mithilfe von greetings_count

Wählen Sie die greetings_count-Metrik und dann Abfragen ausführen aus, um die Ergebnisse anzuzeigen.

Mit Grafana können Sie komplexe Dashboards entwerfen, die eine beliebige Anzahl von Metriken nachverfolgen.

Jede Metrik in .NET kann zusätzliche Dimensionen aufweisen. Dabei handelt es sich um Schlüssel-Wert-Paare, die zum Partitionieren der Daten verwendet werden können. Die ASP.NET Metriken verfügen alle über eine Reihe von Dimensionen, die für den Indikator gelten. Der current-requests-Zähler von Microsoft.AspNetCore.Hosting weist beispielsweise die folgenden Dimensionen auf:

attribute type Beschreibung Beispiele Anwesenheit
method string HTTP-Anforderungsmethode. GET; POST; HEAD Always
scheme string Das URI-Schema, das das verwendete Protokoll identifiziert. http; https Always
host string Name des lokalen HTTP-Servers, der die Anforderung empfangen hat. localhost Always
port int Port des lokalen HTTP-Servers, der die Anforderung empfangen hat. 8080 Hinzugefügt, wenn kein Standardwert (80 für HTTP oder 443 für HTTPS)

Die Diagramme in Grafana werden in der Regel basierend auf jeder eindeutigen Kombination von Dimensionen partitioniert. Die Dimensionen können in den Grafana-Abfragen verwendet werden, um die Daten zu filtern oder zu aggregieren. Wenn Sie z. B. current_requests als Diagramm darstellen, sehen Sie die Werte, die basierend auf jeder Dimensionskombination partitioniert sind. Um nur basierend auf dem Host zu filtern, fügen Sie einen Vorgang von Sum hinzu, und verwenden Sie host als Bezeichnungswert.

Grafana current_requests nach Host

9. Verteilte Ablaufverfolgung mit Jaeger

In Schritt 6 haben Sie gesehen, dass verteilte Ablaufverfolgungsinformationen für die Konsole verfügbar gemacht wurden. Diese Informationen verfolgen Arbeitseinheiten mit Aktivitäten. Einige Aktivitäten werden automatisch von der Plattform erstellt, z. B. die von ASP.NET, um die Verarbeitung einer Anforderung darzustellen, und Bibliotheken und App-Code können auch Aktivitäten erstellen. Das Beispiel "Greetings" weist eine Greeter-Aktivität auf. Die Aktivitäten werden mithilfe der Tags TraceId, SpanIdund ParentId korreliert.

Jeder Prozess in einem verteilten System erzeugt einen eigenen Datenstrom von Aktivitätsinformationen, und wie Metriken benötigen Sie ein System zum Sammeln, Speichern und Korrelieren der Aktivitäten, um die für jede Transaktion geleistete Arbeit visualisieren zu können. Jaeger ist ein Open-Source-Projekt zum Aktivieren dieser Sammlung und für die Visualisierung.

Laden Sie das neueste Binärverteilungsarchiv von Jaeger für Ihre Plattform unter https://www.jaegertracing.io/download/ herunter.

Extrahieren Sie dann den Download an einen lokalen Speicherort, auf den einfach zugegriffen werden kann. Führen Sie die ausführbare Datei jaeger-all-in-one(.exe) aus:

./jaeger-all-in-one --collector.otlp.enabled

Durchsuchen Sie die Konsolenausgabe, um den Port zu finden, an dem sie über gRPC auf OTLP-Datenverkehr lauscht. Beispiel:

{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"}

In dieser Ausgabe erfahren Sie, dass sie auf 0.0.0.0:4317 lauscht, sodass Sie diesen Port als Ziel für Ihren OTLP-Exporter konfigurieren können.

Öffnen Sie die AppSettings.json-Datei für unser Projekt, fügen Sie die folgende Zeile hinzu, und ändern Sie ggf. den Port.

"OTLP_ENDPOINT_URL" :  "http://localhost:4317/"

Starten Sie den Begrüßungsprozess neu, damit er die Eigenschaftsänderung übernehmen und mit der Weiterleitung von Ablaufverfolgungsinformationen an Jaeger beginnen kann.

Nun sollten Sie die Jaeger-Benutzeroberfläche unter http://localhost:16686/ über einen Webbrowser anzeigen können.

Jaeger-Abfrage für Ablaufverfolgung

Um eine Liste der Ablaufverfolgungen anzuzeigen, wählen Sie OTel-Prometheus-grafana-Jaeger aus der Dropdownliste Dienst aus. Wenn Sie eine Ablaufverfolgung auswählen, sollte ein Gantt-Diagramm der Aktivitäten als Teil dieser Ablaufverfolgung angezeigt werden. Wenn Sie auf die einzelnen Vorgänge klicken, werden weitere Details zur Aktivität angezeigt.

Jaeger Operation Details

In einem verteilten System sollten Sie Ablaufverfolgungen von allen Prozessen an dieselbe Jaeger-Installation senden, damit sie die Transaktionen im gesamten System korrelieren kann.

Sie können Ihre App ein wenig interessanter gestalten, indem Sie HTTP-Aufrufe an sich selbst tätigen.

  • Hinzufügen einer HttpClient-Factory zur Anwendung

    builder.Services.AddHttpClient();
    
  • Hinzufügen eines neuen Endpunkts zum Tätigen geschachtelter Begrüßungsanrufe

    app.MapGet("/NestedGreeting", SendNestedGreeting);
    
  • Implementieren Sie den Endpunkt so, dass er HTTP-Aufrufe durchführt, die auch nachverfolgt werden können. In diesem Fall ruft er sich in einer künstlichen Schleife zurück (wirklich nur für Demoszenarien anwendbar).

    async Task SendNestedGreeting(int nestlevel, ILogger<Program> logger, HttpContext context, IHttpClientFactory clientFactory)
    {
        // Create a new Activity scoped to the method
        using var activity = greeterActivitySource.StartActivity("GreeterActivity");
    
        if (nestlevel <= 5)
        {
            // Log a message
            logger.LogInformation("Sending greeting, level {nestlevel}", nestlevel);
    
            // Increment the custom counter
            countGreetings.Add(1);
    
            // Add a tag to the Activity
            activity?.SetTag("nest-level", nestlevel);
    
            await context.Response.WriteAsync($"Nested Greeting, level: {nestlevel}\r\n");
    
            if (nestlevel > 0)
            {
                var request = context.Request;
                var url = new Uri($"{request.Scheme}://{request.Host}{request.Path}?nestlevel={nestlevel - 1}");
    
                // Makes an http call passing the activity information as http headers
                var nestedResult = await clientFactory.CreateClient().GetStringAsync(url);
                await context.Response.WriteAsync(nestedResult);
            }
        }
        else
        {
            // Log a message
            logger.LogError("Greeting nest level {nestlevel} too high", nestlevel);
            await context.Response.WriteAsync("Nest level too high, max is 5");
        }
    }
    

Dies führt zu einem interessanteren Diagramm mit einer Pyramidenform für die Anforderungen, da jede Ebene auf die Antwort des vorherigen Aufrufs wartet.

Geschachtelte Jaeger-Abhängigkeitsergebnisse