Udostępnij za pośrednictwem


Przykład: używanie funkcji OpenTelemetry z rozwiązaniem Prometheus, Grafana i Jaeger

W tym przykładzie użyto rozwiązania Prometheus do zbierania metryk, narzędzia Grafana do tworzenia pulpitu nawigacyjnego i narzędzia Jaeger do wyświetlania śledzenia rozproszonego.

1. Tworzenie projektu

Utwórz prosty projekt internetowego interfejsu API przy użyciu szablonu ASP.NET Core Empty w programie Visual Studio lub następującego polecenia interfejsu wiersza polecenia platformy .NET:

dotnet new web

2. Dodawanie metryk i definicji działań

Poniższy kod definiuje nową metrykę (greetings.count) dla liczby wywołań interfejsu API i nowego źródła działań (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. Tworzenie punktu końcowego interfejsu API

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!";
}

Uwaga

Definicja interfejsu API nie używa żadnych elementów specyficznych dla biblioteki OpenTelemetry. Używa on interfejsów API platformy .NET do obserwowania.

4. Odwołanie do pakietów OpenTelemetry

Użyj Menedżer pakietów NuGet lub wiersza polecenia, aby dodać następujące pakiety NuGet:

<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>

Uwaga

Użyj najnowszych wersji, ponieważ interfejsy API OTel stale ewoluują.

5. Konfigurowanie biblioteki OpenTelemetry przy użyciu odpowiednich dostawców

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();
    }
});

Ten kod używa instrumentacji ASP.NET Core, aby uzyskać metryki i działania z platformy ASP.NET Core. Rejestruje również Metrics dostawców i ActivitySource na potrzeby metryk i śledzenia odpowiednio.

Kod używa eksportera Prometheus dla metryk, który używa ASP.NET Core do hostowania punktu końcowego, dlatego należy również dodać:

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

6. Uruchamianie projektu

Uruchom projekt, a następnie uzyskaj dostęp do interfejsu API za pomocą przeglądarki lub narzędzia curl.

curl -k http://localhost:7275

Za każdym razem, gdy zażądasz strony, będzie ona zwiększać liczbę wykonanych powitań. Dostęp do punktu końcowego metryk można uzyskać przy użyciu tego samego podstawowego adresu URL ze ścieżką /metrics.

6.1 Dane wyjściowe dziennika

Instrukcje rejestrowania z kodu są danymi wyjściowymi przy użyciu polecenia ILogger. Domyślnie dostawca konsoli jest włączony, aby dane wyjściowe są kierowane do konsoli.

Istnieje kilka opcji, w jaki sposób dzienniki mogą być wychodzące z platformy .NET:

  • stdout dane wyjściowe są stderr przekierowywane do plików dziennika przez systemy kontenerów, takie jak Kubernetes.
  • Korzystając z bibliotek rejestrowania, które zostaną zintegrowane z usługą ILogger, należą do nich serilog lub NLog.
  • Korzystając z dostawców rejestrowania dla OTel, takich jak OTLP lub eksporter usługi Azure Monitor, pokazano poniżej.

6.2 Uzyskiwanie dostępu do metryk

Dostęp do metryk można uzyskać przy użyciu punktu końcowego /metrics .

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
...

Dane wyjściowe metryk to migawka metryk w momencie żądania punktu końcowego. Wyniki są udostępniane w formacie ekspozycji Prometheus, który jest czytelny dla człowieka, ale lepiej rozumiany przez Prometheus. Ten temat został omówiony na następnym etapie.

6.3 Uzyskiwanie dostępu do śledzenia

Jeśli spojrzysz na konsolę serwera, zobaczysz dane wyjściowe eksportera śledzenia konsoli, który generuje informacje w formacie czytelnym dla człowieka. Powinno to spowodować wyświetlenie dwóch działań: jednego z niestandardowego ActivitySourceelementu , a drugiego z 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

Pierwszy to wewnętrzne utworzone działanie niestandardowe. Drugi jest tworzony przez ASP.NET dla żądania i zawiera tagi właściwości żądania HTTP. Zobaczysz, że oba te elementy mają ten sam TraceIdelement , który identyfikuje jedną transakcję i w systemie rozproszonym może służyć do korelowania śladów z każdej usługi zaangażowanej w transakcję. Identyfikatory są przesyłane jako nagłówki HTTP. ASP.NET Core przypisuje element TraceId , jeśli żaden z nich nie istnieje, gdy odbiera żądanie. HttpClient domyślnie zawiera nagłówki dla żądań wychodzących. Każde działanie ma wartość , która jest kombinacją SpanIdTraceId i SpanId jednoznacznie identyfikuje każde działanie. Działanie Greeter jest nadrzędne dla działania HTTP za pośrednictwem elementu ParentSpanId, który jest mapowany na SpanId działanie HTTP.

W późniejszym etapie przekażesz te dane do firmy Jaeger, aby zwizualizować rozproszone ślady.

7. Zbieranie metryk za pomocą rozwiązania Prometheus

Prometheus to system bazy danych metryk, agregacji i szeregów czasowych. Konfigurujesz je przy użyciu punktów końcowych metryk dla każdej usługi i okresowo usuwa wartości i przechowuje je w bazie danych szeregów czasowych. Następnie możesz je analizować i przetwarzać zgodnie z potrzebami.

Dane metryk uwidocznione w formacie Prometheus to migawka metryk procesu w czasie. Za każdym razem, gdy żądanie zostanie wykonane do punktu końcowego metryk, będzie zgłaszać bieżące wartości. Chociaż bieżące wartości są interesujące, stają się bardziej cenne w porównaniu z wartościami historycznymi, aby zobaczyć trendy i wykryć, czy wartości są nietypowe. Często usługi mają skoki użycia na podstawie pory dnia lub wydarzeń światowych, takich jak spree zakupów świątecznych. Porównując wartości z trendami historycznymi, można wykryć, czy są one nietypowe, lub jeśli metryka powoli się pogarsza w czasie.

Proces nie przechowuje żadnej historii tych migawek metryk. Dodanie tej możliwości do procesu może być intensywnie korzystające z zasobów. Ponadto w systemie rozproszonym często masz wiele wystąpień każdego węzła, więc chcesz mieć możliwość zbierania metryk ze wszystkich z nich, a następnie agregowania i porównywania z ich wartościami historycznymi.

7.1 Instalowanie i konfigurowanie rozwiązania Prometheus

Pobierz aplikację Prometheus dla swojej platformy z https://prometheus.io/download/ witryny i wyodrębnij zawartość pobierania.

Przyjrzyj się górnej części danych wyjściowych uruchomionego serwera, aby uzyskać numer portu dla punktu końcowego HTTP . Na przykład:

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

Zmodyfikuj plik konfiguracji YAML rozwiązania Prometheus, aby określić port punktu końcowego złomowania HTTP i ustawić niższy interwał złomowania. Na przykład:

  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"]

Uruchom aplikację Prometheus i poszukaj w danych wyjściowych portu, na którym działa, zazwyczaj 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

Otwórz ten adres URL w przeglądarce. W interfejsie użytkownika rozwiązania Prometheus powinno być teraz możliwe wykonywanie zapytań dotyczących metryk. Użyj wyróżnionego przycisku na poniższej ilustracji, aby otworzyć eksploratora metryk, który pokazuje wszystkie dostępne metryki.

Eksplorator metryk prometheus

Wybierz metrykę greetings_count , aby wyświetlić wykres wartości.

Wykres greetings_count

8. Tworzenie pulpitu nawigacyjnego metryk przy użyciu narzędzia Grafana

Grafana to produkt do tworzenia pulpitów nawigacyjnych, który może tworzyć pulpity nawigacyjne i alerty na podstawie rozwiązania Prometheus lub innych źródeł danych.

Pobierz i zainstaluj wersję systemu operacyjnego Grafana, postępując zgodnie z https://grafana.com/oss/grafana/ instrukcjami dotyczącymi platformy. Po zainstalowaniu narzędzie Grafana jest zwykle uruchamiane na porcie 3000, więc otwórz http://localhost:3000 go w przeglądarce. Musisz się zalogować; domyślna nazwa użytkownika i hasło to .admin

Z menu hamburgera wybierz połączenia, a następnie wprowadź tekst prometheus , aby wybrać typ punktu końcowego. Wybierz pozycję Utwórz źródło danych Prometheus, aby dodać nowe źródło danych.

Połączenie Grafana z prometheus

Należy ustawić następujące właściwości:

  • Adres URL serwera Prometheus: http://localhost:9090/ zmiana portu zgodnie z zastosowaniem

Wybierz pozycję Zapisz i testuj , aby zweryfikować konfigurację.

Po otrzymaniu komunikatu o powodzeniu możesz skonfigurować pulpit nawigacyjny. Kliknij link do tworzenia pulpitu nawigacyjnego wyświetlany w oknie podręcznym komunikatu o powodzeniu.

Wybierz pozycję Dodaj wizualizację, a następnie wybierz źródło danych Prometheus, które właśnie dodano jako źródło danych.

Powinien zostać wyświetlony projektant panelu pulpitu nawigacyjnego. W dolnej połowie ekranu można zdefiniować zapytanie.

Zapytanie Grafana przy użyciu greetings_count

Wybierz metryki greetings_count , a następnie wybierz pozycję Uruchom zapytania , aby wyświetlić wyniki.

Za pomocą narzędzia Grafana możesz zaprojektować zaawansowane pulpity nawigacyjne, które będą śledzić dowolną liczbę metryk.

Każda metryka na platformie .NET może mieć dodatkowe wymiary, które są parami klucz-wartość, których można użyć do partycjonowania danych. Metryki ASP.NET wszystkie zawierają szereg wymiarów mających zastosowanie do licznika. Na przykład current-requests licznik z Microsoft.AspNetCore.Hosting ma następujące wymiary:

Atrybut Type Opis Przykłady Obecność
method string Metoda żądania HTTP. GET; POST; HEAD Zawsze
scheme string Schemat identyfikatora URI identyfikujący używany protokół. http; https Zawsze
host string Nazwa lokalnego serwera HTTP, który odebrał żądanie. localhost Zawsze
port int Port lokalnego serwera HTTP, który odebrał żądanie. 8080 Dodano wartość domyślną (80 dla protokołu HTTP lub 443 dla protokołu https)

Wykresy w narzędziu Grafana są zwykle partycjonowane na podstawie każdej unikatowej kombinacji wymiarów. Wymiary mogą być używane w zapytaniach Grafana do filtrowania lub agregowania danych. Na przykład w przypadku wykresu current_requestszobaczysz wartości podzielone na partycje na podstawie każdej kombinacji wymiarów. Aby filtrować tylko na hoście, dodaj operację Sum i użyj jej host jako wartości etykiety.

Grafana current_requests według hosta

9. Śledzenie rozproszone za pomocą narzędzia Jaeger

W kroku 6 pokazano, że informacje dotyczące śledzenia rozproszonego były uwidocznione w konsoli programu . Te informacje śledzą jednostki pracy z działaniami. Niektóre działania są tworzone automatycznie przez platformę, na przykład przez ASP.NET do reprezentowania obsługi żądania, a biblioteki i kod aplikacji mogą również tworzyć działania. Przykład powitania zawiera Greeter działanie. Działania są skorelowane przy użyciu tagów TraceId, SpanIdi ParentId .

Każdy proces w systemie rozproszonym tworzy swój własny strumień informacji o aktywności i takich jak metryki, potrzebny jest system do zbierania, przechowywania i korelowania działań, aby móc wizualizować pracę wykonaną dla każdej transakcji. Jaeger to projekt open source umożliwiający włączenie tej kolekcji i wizualizacji.

Pobierz najnowsze archiwum dystrybucji binarnej jaeger dla swojej platformy z witryny https://www.jaegertracing.io/download/.

Następnie wyodrębnij pobieranie do lokalizacji lokalnej, która jest łatwa do uzyskania dostępu. Uruchom plik wykonywalny jaeger-all-in-one(.exe):

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

Przejrzyj dane wyjściowe konsoli, aby znaleźć port, w którym nasłuchuje ruchu OTLP za pośrednictwem kontrolera gRPC. Na przykład:

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

Te dane wyjściowe informują, że nasłuchuje w systemie 0.0.0.0:4317, aby można było skonfigurować ten port jako miejsce docelowe eksportera OTLP.

AppSettings.json Otwórz plik dla naszego projektu i dodaj następujący wiersz, zmieniając port, jeśli ma to zastosowanie.

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

Uruchom ponownie proces powitania, aby można było pobrać zmianę właściwości i rozpocząć kierowanie informacji śledzenia do jaeger.

Teraz powinien być widoczny interfejs użytkownika jaegera w http://localhost:16686/ przeglądarce internetowej.

Zapytanie Jaeger dotyczące śladów

Aby wyświetlić listę śladów, wybierz z OTel-Prometheus-grafana-Jaeger listy rozwijanej Usługa . Wybranie śledzenia powinno zawierać wykres gantta działań w ramach tego śladu. Kliknięcie każdej operacji spowoduje wyświetlenie dodatkowych szczegółów dotyczących działania.

Szczegóły operacji Jaeger

W systemie rozproszonym chcesz wysyłać ślady ze wszystkich procesów do tej samej instalacji usługi Jaeger, aby mogła skorelować transakcje w całym systemie.

Możesz sprawić, że aplikacja stanie się nieco bardziej interesująca, wykonując wywołania HTTP do siebie.

  • HttpClient Dodawanie fabryki do aplikacji

    builder.Services.AddHttpClient();
    
  • Dodawanie nowego punktu końcowego do tworzenia zagnieżdżonych wywołań powitania

    app.MapGet("/NestedGreeting", SendNestedGreeting);
    
  • Zaimplementuj punkt końcowy, aby wykonywać wywołania HTTP, które mogą być również śledzone. W takim przypadku wywołuje się z powrotem w sztucznej pętli (dotyczy to tylko scenariuszy demonstracyjnych).

    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");
        }
    }
    

Powoduje to bardziej interesujący wykres z kształtem ostrosłupu dla żądań, ponieważ każdy poziom czeka na odpowiedź z poprzedniego wywołania.

Wyniki zależności zagnieżdżonych w usłudze Jaeger