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
undstderr
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.
Wählen Sie die greetings_count
-Metrik aus, um ein Diagramm mit Werten anzuzeigen.
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.
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.
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.
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
, SpanId
und 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.
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.
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 Anwendungbuilder.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.