Partager via


Exemple : Utiliser OpenTelemetry avec Prometheus, Grafana et Jaeger

Cet exemple utilise Prometheus pour effectuer des collections de métriques, Grafana pour créer des tableaux de bord et Jaeger pour afficher des suivis distribués.

1. Créer le projet

Créez un projet d’API web simple en utilisant le modèle ASP.NET Core vide dans Visual Studio ou la commande CLI .NET suivante :

dotnet new web

2. Ajouter des métriques et des définitions d’activité

Le code suivant définit une nouvelle métrique (greetings.count) correspondant au nombre de fois où l’API a été appelée et une nouvelle source d’activité (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. Créer un point de terminaison d’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!";
}

Notes

La définition d’API n’utilise aucun élément spécifique à OpenTelemetry. Elle utilise les API .NET pour l’observabilité.

4. Référencer les packages OpenTelemetry

Utilisez le Gestionnaire de package NuGet ou la ligne de commande pour ajouter les packages NuGet suivants :

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

Notes

Utilisez les versions les plus récentes, car les API d’OTel sont en constante évolution.

5. Configurer OpenTelemetry avec les fournisseurs appropriés

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

Ce code utilise l’instrumentation ASP.NET Core pour obtenir des métriques et des activités à partir d’ASP.NET Core. Il enregistre également les fournisseurs Metrics et ActivitySource pour les métriques et le suivi respectivement.

Le code utilise l’exporter Prometheus pour les métriques, qui utilise ASP.NET Core afin d’héberger le point de terminaison. Vous devez donc également ajouter :

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

6. Exécuter le projet

Exécutez le projet, puis accédez à l’API avec le navigateur ou curl.

curl -k http://localhost:7275

Chaque fois que vous demandez la page, elle incrémente le nombre de messages d’accueil effectués. Vous pouvez accéder au point de terminaison des métriques en utilisant la même URL de base, avec le chemin d’accès /metrics.

6.1 Sortie du journal

Les instructions de journalisation du code sont générées à l’aide de ILogger. Par défaut, le Fournisseur Console est activé afin que la sortie soit dirigée vers la console.

Il existe deux possibilités pour effectuer une sortie de journaux à partir de .NET :

  • Les sorties stdout et stderr sont redirigées vers des fichiers journaux par des systèmes de conteneur tels que Kubernetes.
  • À l’aide de bibliothèques de journalisation, incluant Serilog ou NLog, qui s’intègrent à ILogger.
  • À l’aide de fournisseurs de journalisation pour OTel, comme OTLP ou l’exporter Azure Monitor, comme illustré ci-dessous.

6.2 Accéder aux métriques

Vous pouvez accéder aux métriques en tirant parti du point de terminaison /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
...

La sortie des métriques est une capture instantanée des métriques au moment où le point de terminaison est demandé. Les résultats sont fournis au format d’exposition Prometheus, lisible par l’utilisateur et mieux compris par Prometheus. Ce sujet est abordé dans la phase suivante.

6.3 Accéder au suivi

Si vous examinez la console du serveur, vous verrez la sortie de l’exporter de trace de console, qui génère les informations dans un format lisible par l’utilisateur. Elle doit afficher deux activités, l’une à partir de votre ActivitySource personnalisé et l’autre d’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

La première est l’activité personnalisée interne que vous avez créée. La seconde est créée par ASP.NET pour la requête et inclut des balises pour les propriétés de requête HTTP. Vous verrez que les deux ont le même TraceId, qui identifie une seule transaction et dans un système distribué qui peut être utilisé pour mettre en corrélation les traces de chaque service impliqué dans une transaction. Les ID sont transmis sous forme d’en-têtes HTTP. ASP.NET Core attribue un TraceId si aucun n’est présent lorsqu’il reçoit une requête. HttpClient inclut les en-têtes par défaut sur des requêtes sortantes. Chaque activité a un SpanId, qui est la combinaison de TraceId et SpanId permettant d’identifier chaque activité de manière unique. L’activité Greeter est apparentée à l’activité HTTP via son ParentSpanId, qui mappe vers SpanId de l’activité HTTP.

Dans une phase ultérieure, vous allez alimenter ces données dans Jaeger pour visualiser les traces distribuées.

7. Collection de métriques avec Prometheus

Prometheus est un système de collection de métriques, d’agrégation et de base de données de série chronologique. Vous le configurez avec les points de terminaison de métrique pour chaque service pour qu’il récupère régulièrement les valeurs et les stocke dans sa base de données de série chronologique. Vous pouvez ensuite les analyser et les traiter en fonction des besoins.

Les données de métriques exposées au format Prometheus sont une capture instantanée dans le temps des métriques du processus. Chaque fois qu’une requête est envoyée au point de terminaison de métriques, elle signale les valeurs actuelles. Bien que les valeurs actuelles soient intéressantes, elles deviennent plus utiles que les valeurs historiques pour voir des tendances et détecter des valeurs inhabituelles. Souvent, les services connaissent des pics d’utilisation en fonction de l’heure de la journée ou des événements mondiaux, comme une frénésie d’achats lors des fêtes. En comparant les valeurs aux tendances historiques, vous pouvez détecter si elles sont inhabituelles ou si une métrique se détériore lentement au fil du temps.

Le processus ne stocke aucun historique de ces captures instantanées de métriques. L’ajout de cette fonctionnalité au processus peut consommer beaucoup de ressources. En outre, vous disposez généralement de plusieurs instances de chaque nœud dans un système distribué. Vous souhaitez donc pouvoir y collecter toutes les métriques, puis les agréger et les comparer avec leurs valeurs historiques.

7.1 Installation et configuration de Prometheus

Téléchargez Prometheus pour votre plateforme à partir de https://prometheus.io/download/ et extrayez le contenu du téléchargement.

Examinez la partie supérieure de la sortie de votre serveur en cours d’exécution afin d’obtenir le numéro de port du point de terminaison http. Par exemple :

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

Modifiez le fichier de configuration YAML Prometheus pour spécifier le port de votre point de terminaison de scraping HTTP et définissez un intervalle de scraping inférieur. Par exemple :

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

Démarrez Prometheus et recherchez dans la sortie le port sur lequel il s’exécute, en général 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

Ouvrez cette URL dans votre navigateur. Dans l’interface utilisateur Prometheus, vous devriez maintenant pouvoir interroger vos métriques. Utilisez le bouton mis en surbrillance dans l’image suivante pour ouvrir Metrics Explorer qui affiche toutes les métriques disponibles.

Metrics Explorer Prometheus

Sélectionnez la métrique greetings_count pour afficher un graphique des valeurs.

Graphique de greetings_count

8. Utiliser Grafana pour la création d’un tableau de bord de métriques

Grafana est un produit de création de tableaux de bord qui peut créer des tableaux de bord et des alertes basés sur Prometheus ou d’autres sources de données.

Téléchargez et installez la version OSS de Grafana https://grafana.com/oss/grafana/ en suivant les instructions de votre plateforme. Une fois installée, la plateforme Grafana est généralement exécutée sur le port 3000. Ouvrez donc http://localhost:3000 dans votre navigateur. Vous devez vous connecter. Le nom d’utilisateur et le mot de passe par défaut sont tous deux admin.

Dans le menu « hamburger », choisissez Connexions, puis entrez le texte prometheus pour sélectionner votre type de point de terminaison. Sélectionnez Créer une source de données Prometheus pour ajouter une nouvelle source de données.

Connexion de Grafana à Prometheus

Vous devez définir les propriétés suivantes :

  • URL du serveur Prometheus : http://localhost:9090/ modifiant le port, le cas échéant

Sélectionnez Enregistrer et Tester pour vérifier la configuration.

Une fois que vous obtenez un message de réussite, vous pouvez configurer un tableau de bord. Cliquez sur le lien générer un tableau de bord affiché dans la fenêtre contextuelle du message de réussite.

Sélectionnez Ajouter une visualisation, puis choisissez la source de données Prometheus que vous venez d’ajouter comme source de données.

Le concepteur de panneau de tableau de bord doit s’afficher. Dans la moitié inférieure de l’écran, vous pouvez définir la requête.

Requête Grafana utilisant greetings_count

Sélectionnez la métrique greetings_count, puis sélectionnez Exécuter des requêtes pour afficher les résultats.

Vous pouvez, avec Grafana, concevoir des tableaux de bord sophistiqués qui effectuent le suivi d’un nombre quelconque de métriques.

Chaque métrique dans .NET peut avoir des dimensions supplémentaires, qui sont des paires clé-valeur pouvant être utilisées pour partitionner les données. Les métriques ASP.NET présentent toutes un certain nombre de dimensions applicables au compteur. Par exemple, le compteur current-requests de Microsoft.AspNetCore.Hosting a les dimensions suivantes :

Attribut Type Description Exemples Présence
method string Méthode de requête HTTP. GET; POST; HEAD Toujours
scheme string Schéma d’URI identifiant le protocole utilisé. http; https Toujours
host string Nom du serveur HTTP local qui a reçu la requête. localhost Toujours
port int Port du serveur HTTP local qui a reçu la requête. 8080 Ajouté s’il ne s’agit pas de la valeur par défaut (80 pour http ou 443 pour https)

Les graphiques dans Grafana sont généralement partitionnés en fonction de chaque combinaison unique de dimensions. Les dimensions peuvent être utilisées dans les requêtes Grafana pour filtrer ou agréger les données. Par exemple, si vous représentez un graphique current_requests, vous verrez des valeurs partitionnée basées sur chaque combinaison de dimensions. Pour filtrer uniquement en fonction de l’hôte, ajoutez une opération de Sum et utilisez host en tant que valeur d’étiquette.

current_requests Grafana par hôte

9. Suivi distribué avec Jaeger

À l’Étape 6, vous avez vu que les informations de suivi distribué étaient exposées dans la console. Ces informations effectuent un suivi des unités de travail avec des activités. Certaines activités sont créées automatiquement par la plateforme, comme celle de ASP.NET pour représenter la gestion d’une requête. Les bibliothèques et le code d’application peuvent également créer des activités. L’exemple « greetings » a une activité Greeter. Les activités sont corrélées à l’aide des balises TraceId, SpanId et ParentId.

Chaque processus dans un système distribué génère son propre flux d’informations d’activité et, comme pour les métriques, vous avez besoin d’un système pour collecter, stocker et mettre en corrélation les activités afin de pouvoir visualiser le travail effectué pour chaque transaction. Jaeger est un projet open source permettant d’activer cette collection et cette visualisation.

Téléchargez la dernière archive de distribution binaire de Jaeger pour votre plateforme depuis https://www.jaegertracing.io/download/.

Extrayez ensuite le téléchargement vers un emplacement local facile d’accès. Exécutez le fichier exécutable jaeger-all-in-one(.exe) :

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

Parcourez la sortie de la console pour rechercher le port où elle écoute le trafic OTLP via gRPC. Par exemple :

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

Cette sortie vous indique qu’elle écoute sur 0.0.0.0:4317. Vous pouvez donc configurer ce port comme destination pour votre exporter OTLP.

Ouvrez le fichier AppSettings.json pour notre projet, puis ajoutez la ligne suivante, en modifiant le port le cas échéant.

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

Redémarrez le processus d’accueil afin qu’il puisse récupérer la modification de propriété et commencer à diriger les informations de suivi vers Jaeger.

Vous devez maintenant être en mesure de voir l’interface utilisateur Jaeger à l’adresse http://localhost:16686/ à partir d’un navigateur web.

Requête Jaeger pour des traces

Pour afficher la liste de traces, sélectionnez OTel-Prometheus-grafana-Jaeger dans la liste déroulante Service. La sélection d’une trace devrait afficher un diagramme de Gantt des activités faisant partie de cette trace. Cliquez sur chacune des opérations pour afficher d’autres informations sur l’activité.

Informations sur l’opération Jaeger

Dans un système distribué, vous souhaitez envoyer des traces de tous les processus vers la même installation Jaeger afin qu’elle puisse mettre en corrélation les transactions dans le système.

Vous pouvez rendre votre application un peu plus intéressante en lui faisant passer des appels HTTP à elle-même.

  • Ajouter une fabrique HttpClient à l’application

    builder.Services.AddHttpClient();
    
  • Ajouter un nouveau point de terminaison pour effectuer des appels d’accueil imbriqués

    app.MapGet("/NestedGreeting", SendNestedGreeting);
    
  • Implémentez le point de terminaison afin qu’il effectue des appels HTTP qui peuvent également faire l’objet d’une trace. Dans ce cas, il se rappelle dans une boucle artificielle (applicable uniquement aux scénarios de démonstration).

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

Il en résulte un graphique plus intéressant avec une forme pyramidale pour les requêtes, car chaque niveau attend la réponse de l’appel précédent.

Résultats de la dépendance imbriquée Jaeger