Поделиться через


Пример. Использование OpenTelemetry с Prometheus, Grafana и Jaeger

В этом примере для сбора метрик используется Prometheus, Grafana для создания панели мониторинга и Jaeger для отображения распределенной трассировки.

1. Создание проекта

Создайте простой проект веб-API с помощью шаблона ASP.NET Core Empty в Visual Studio или следующей команды .NET CLI:

dotnet new web

2. Добавление метрик и определений действий

Следующий код определяет новую метрику (greetings.count) для количества вызовов API и нового источника действия (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. Создание конечной точки 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!";
}

Примечание.

Определение API не использует ничего конкретного для OpenTelemetry. Он использует API .NET для наблюдения.

4. Ссылка на пакеты OpenTelemetry

Используйте диспетчер пакетов NuGet или командную строку, чтобы добавить следующие пакеты 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>

Примечание.

Используйте последние версии, так как API OTel постоянно развиваются.

5. Настройка OpenTelemetry с правильными поставщиками

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

Этот код использует инструментирование ASP.NET Core для получения метрик и действий из ASP.NET Core. Он также регистрирует Metrics и ActivitySource поставщики для метрик и трассировки соответственно.

В коде используется экспортер Prometheus для метрик, который использует ASP.NET Core для размещения конечной точки, поэтому вам также необходимо добавить следующее:

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

6. Запуск проекта

Запустите проект и получите доступ к API с помощью браузера или curl.

curl -k http://localhost:7275

Каждый раз, когда вы запрашиваете страницу, он увеличивает количество приветствий, которые были сделаны. Вы можете получить доступ к конечной точке метрик с помощью того же базового URL-адреса, используя путь /metrics.

Выходные данные журнала 6.1

Операторы ведения журнала из кода выходные данные используются ILogger. По умолчанию поставщик консоли включен, чтобы выходные данные направлялись в консоль.

Существует несколько вариантов выхода журналов из .NET:

  • stdout и stderr выходные данные перенаправляются в файлы журнала по системам контейнеров, таким как Kubernetes.
  • Используя библиотеки ведения журнала, которые будут интегрироваться с ILogger, к ним относятся Serilog или NLog.
  • Использование поставщиков ведения журнала для OTel, таких как OTLP или экспортер Azure Monitor, показанный ниже.

6.2 Доступ к метрикам

Доступ к метрикам можно получить с помощью конечной /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
...

Выходные данные метрик — это моментальный снимок метрик во время запроса конечной точки. Результаты представлены в формате экспозиции Prometheus, который является удобочитаемым, но лучше понятен Prometheus. Этот раздел рассматривается на следующем этапе.

6.3 Доступ к трассировке

Если вы посмотрите на консоль для сервера, вы увидите выходные данные из экспортера трассировки консоли, который выводит информацию в удобочитаемом формате. Это должно отобразить два действия, один из пользовательских ActivitySourceи другой из 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

Первое — это созданное внутреннее настраиваемое действие. Второй создается ASP.NET для запроса и содержит теги для свойств HTTP-запроса. Вы увидите, что оба имеют одно и то же TraceId, что идентифицирует одну транзакцию и в распределенной системе, можно использовать для сопоставления трассировок из каждой службы, связанной с транзакцией. Идентификаторы передаются в виде заголовков HTTP. ASP.NET Core назначает значение, TraceId если оно отсутствует при получении запроса. HttpClient включает заголовки по умолчанию для исходящих запросов. Каждое действие имеет сочетание SpanIdTraceId и SpanId уникально идентифицирует каждое действие. Действие Greeter является родительским для действия HTTP через его ParentSpanId, которое сопоставляется с SpanId действием HTTP.

На более позднем этапе вы будете передавать эти данные в Jaeger, чтобы визуализировать распределенные трассировки.

7. Сбор метрик с помощью Prometheus

Prometheus — это коллекция метрик, агрегирование и система баз данных временных рядов. Вы настраиваете ее с конечными точками метрик для каждой службы и периодически удаляет значения и сохраняет их в базе данных временных рядов. Затем можно проанализировать и обработать их по мере необходимости.

Данные метрик, предоставляемые в формате Prometheus, — это моментальный снимок метрик процесса. Каждый раз, когда запрос выполняется в конечную точку метрик, он будет сообщать о текущих значениях. Хотя текущие значения интересны, они становятся более ценными по сравнению с историческими значениями, чтобы увидеть тенденции и определить, являются ли значения аномальными. Как правило, службы имеют пики использования на основе времени дня или мировых событий, таких как праздничные покупки. Сравнивая значения с историческими тенденциями, вы можете определить, являются ли они ненормальными, или если метрика медленно ухудшается с течением времени.

Процесс не сохраняет журнал этих моментальных снимков метрик. Добавление этой возможности в процесс может быть ресурсоемким. Кроме того, в распределенной системе обычно используется несколько экземпляров каждого узла, поэтому вы хотите иметь возможность собирать метрики из всех них, а затем агрегировать и сравнивать их с историческими значениями.

7.1. Установка и настройка Prometheus

Скачайте Prometheus для своей платформы https://prometheus.io/download/ и извлеките содержимое скачивания.

Просмотрите верхнюю часть выходных данных запущенного сервера, чтобы получить номер порта для конечной точки http . Например:

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

Измените файл конфигурации YamL Prometheus, чтобы указать порт для конечной точки слом HTTP и задать более низкий интервал очистки. Например:

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

Запустите Prometheus и просмотрите выходные данные порта, на котором он работает, обычно 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

Откройте этот URL-адрес в браузере. В пользовательском интерфейсе Prometheus теперь вы сможете запрашивать метрики. Используйте выделенную кнопку на следующем рисунке, чтобы открыть обозреватель метрик, в котором отображаются все доступные метрики.

Обозреватель метрик Prometheus

Выберите метрику greetings_count , чтобы просмотреть граф значений.

Граф greetings_count

8. Использование Grafana для создания панели мониторинга метрик

Grafana — это продукт мониторинга, который может создавать панели мониторинга и оповещения на основе Prometheus или других источников данных.

Скачайте и установите версию OSS Grafana, следуя https://grafana.com/oss/grafana/ инструкциям для вашей платформы. После установки Grafana обычно выполняется через порт 3000, поэтому откройте http://localhost:3000 его в браузере. Вам потребуется войти в систему; Имя пользователя и пароль по умолчанию являются обоими admin.

В меню гамбургера выберите подключения, а затем введите текст prometheus , чтобы выбрать тип конечной точки. Выберите "Создать источник данных Prometheus", чтобы добавить новый источник данных.

Подключение Grafana к prometheus

Необходимо задать следующие свойства:

  • URL-адрес сервера Prometheus: http://localhost:9090/ изменение порта в соответствии с применимым

Нажмите кнопку "Сохранить" и " Проверить ", чтобы проверить конфигурацию.

После получения сообщения об успешном выполнении можно настроить панель мониторинга. Щелкните ссылку на панель мониторинга, показанную во всплывающем окне для сообщения об успешном выполнении.

Выберите " Добавить визуализацию", а затем выберите источник данных Prometheus, который вы только что добавили в качестве источника данных.

Должен появиться конструктор панели мониторинга. В нижней половине экрана можно определить запрос.

Запрос Grafana с помощью greetings_count

Выберите метрику greetings_count и выберите команду "Выполнить запросы" , чтобы просмотреть результаты.

С помощью Grafana можно создавать сложные панели мониторинга, которые будут отслеживать любое количество метрик.

Каждая метрика в .NET может иметь дополнительные измерения, которые являются парами "ключ-значение", которые можно использовать для секционирования данных. Все метрики ASP.NET имеют ряд измерений, применимых к счетчику. Например, счетчик current-requests Microsoft.AspNetCore.Hosting имеет следующие измерения:

Атрибут Тип Описание Примеры Присутствие
method string Метод HTTP-запроса. GET; POST; HEAD Всегда
scheme string Схема URI, определяющая используемый протокол. http; https Всегда
host string Имя локального HTTP-сервера, который получил запрос. localhost Всегда
port int Порт локального HTTP-сервера, который получил запрос. 8080 Добавлено, если не по умолчанию (80 для http или 443 для https)

Графы в Grafana обычно секционируются на основе каждого уникального сочетания измерений. Измерения можно использовать в запросах Grafana для фильтрации или статистической обработки данных. Например, если вы граф current_requests, вы увидите значения, секционированные на основе каждого сочетания измерений. Чтобы отфильтровать только узел, добавьте операцию Sum и используйте host ее в качестве значения метки.

Grafana current_requests по узлу

9. Распределенная трассировка с помощью Jaeger

На шаге 6 вы узнали, что информация распределенной трассировки предоставляется консоли. Эта информация отслеживает единицы работы с действиями. Некоторые действия создаются автоматически платформой, например одной, ASP.NET для представления обработки запроса, а библиотеки и код приложения также могут создавать действия. Пример приветствия имеет Greeter действие. Действия коррелируются с помощью тегов TraceIdSpanId, и ParentId тегов.

Каждый процесс в распределенной системе создает собственный поток сведений о действиях, а также такие метрики, как и метрики, требуется система для сбора, хранения и сопоставления действий для визуализации работы для каждой транзакции. Jaeger — это проект с открытым исходным кодом для включения этой коллекции и визуализации.

Скачайте последний архив двоичного дистрибутива Jaeger для вашей платформы https://www.jaegertracing.io/download/.

Затем извлеките скачивание в локальное расположение, которое легко получить доступ. Запустите исполняемый файл jaeger-all-in-one(.exe ):

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

Просмотрите выходные данные консоли, чтобы найти порт, где он прослушивает трафик OTLP через gRPC. Например:

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

В этом выходных данных говорится, что он прослушивается 0.0.0.0:4317, поэтому вы можете настроить этот порт в качестве назначения для экспортера OTLP.

AppSettings.json Откройте файл для нашего проекта и добавьте следующую строку, изменив порт, если это применимо.

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

Перезапустите процесс приветствия, чтобы он смог забрать изменение свойства и начать перенаправление сведений трассировки в Jaeger.

Теперь вы сможете просмотреть пользовательский интерфейс http://localhost:16686/ Jaeger из веб-браузера.

Запрос Jaeger для трассировок

Чтобы просмотреть список трассировок, выберите OTel-Prometheus-grafana-Jaeger в раскрывающемся списке службы . При выборе трассировки должна отображаться диаграмма ганта действий в рамках этой трассировки. При щелчке по каждому из операций отображаются дополнительные сведения о действии.

Сведения об операции Jaeger

В распределенной системе необходимо отправлять трассировки из всех процессов в одну установку Jaeger, чтобы она могли сопоставить транзакции в системе.

Вы можете сделать приложение более интересным, сделав его http-вызовами к себе.

  • Добавление фабрики HttpClient в приложение

    builder.Services.AddHttpClient();
    
  • Добавление новой конечной точки для выполнения вложенных вызовов приветствия

    app.MapGet("/NestedGreeting", SendNestedGreeting);
    
  • Реализуйте конечную точку, чтобы выполнять HTTP-вызовы, которые также можно отслеживать. В этом случае он возвращается к себе в искусственном цикле (действительно применимо только к демонстрационных сценариям).

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

Это приводит к более интересному графу с фигурой пирамиды для запросов, так как каждый уровень ожидает ответа от предыдущего вызова.

Результаты вложенных зависимостей Jaeger