OpenTelemetry в Dotnet


Введение

Почему именно OpenTelemetry?

По мере развития экосистем приложения неизменно привязываются к сервисам, которые обрабатывают данные протоколирования, трассировки и наблюдения. Примером этого является использование агентов Grafana и prometheus для сбора данных из контейнеров.

OpenTelemetry предлагает простой способ сбора всех этих данных, не зависящий от платформы. Вместо привязки к конкретной платформе или SaaS вы можете переходить с одной платформы на другую по своему усмотрению. Вы даже можете использовать несколько платформ, например, соединить Grafana и Zipkin. В этом материале я подвожу итог своего путешествия в OpenTelemetry в .NET.

Инструментирование приложения

Что мы собираем?

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

  1. Журналы — Журналы являются стандартом, к которому мы привыкли. Они представляют собой захваченный фрагмент данных в определенный момент времени. В этом материале мы не будем их рассматривать, поскольку журналы собирались и отправлялись с помощью других инструментов. Я обнаружил, что OpenTelemetry все еще находится в зачаточном состоянии.
  2. Следы — это отрезки времени, которые могут иметь несколько дочерних отрезков, иллюстрирующих произошедшее событие и продолжительность времени. Эти следы могут распространяться и отслеживаться в нескольких системах при условии, что они оснащены соответствующими инструментами. Следы также имеют TraceIds в форме guid, который может связывать журналы и следы.

    Causal relationships between Spans in a single Trace
    
            [Span A]  ←←←(the root span)
                |
        +------+------+
        |             |
    [Span B]      [Span C] ←←←(Span C is a `child` of Span A)
        |             |
    [Span D]      +---+-------+
                |           |
            [Span E]    [Span F]
    

    Источник иллюстрации

  3. Метрики — Метрики — это временные характеристики определенных событий, которые могут быть использованы для сбора статистических данных и наблюдения. Например, сколько времени занимает запрос или информация о процессоре в определенный момент времени.

Интеграция инструментария

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

Трассировки

Ниже приведен пример конфигурации для одного из наших приложений. Мы устанавливаем трассировщик как одну из служб в .NET. Синтаксис AddSource используется для наименования типа провайдера. Построитель ресурсов разделяется между всеми OpenTelemetry подобно построителю хостов в dotnet. Мы также указываем конечную точку коллектора.

    services.AddOpenTelemetryTracing(options =>
    {
        options.AddSource("Email.RiskScore.Tracing");
        options.SetResourceBuilder(rb).SetSampler(new AlwaysOnSampler()).AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddMySqlDataInstrumentation(settings => settings.SetDbStatement = true);
        options.AddOtlpExporter(otlpOptions => { otlpOptions.Endpoint = new Uri(endpoint + "/api/v1/traces"); });
    });
Вход в полноэкранный режим Выход из полноэкранного режима

В данном случае я включил инструментарий AspNet, который фиксирует метрики, связанные с dotnet, включая такие вещи, как сборка мусора. Кроме того, мы фиксируем каждый исходящий http-запрос и все sql-запросы. Это позволяет получить гораздо более точную картину того, что делают запросы или сколько времени они отнимают от потока приложения.

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

public static ActivitySource Writer = null;
public static void Init(string tracerName)
{
    Writer = new ActivitySource(tracerName,"1.0.0");
    var listener = new ActivityListener()
    {
        ShouldListenTo = _ => true,
        Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData
    };
    ActivitySource.AddActivityListener(listener);
}
Вход в полноэкранный режим Выйти из полноэкранного режима

После этого введите трассировку вокруг какой-либо операции. В случае с получением данных мы помечаем активность как Client, поскольку мы получаем данные. Server и Internal — другие маркеры, которые мы используем. Кроме того, мы можем хранить теги с соответствующей информацией, например, параметрами.

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

var activity = SharedTelemetryUtilities.Writer?.StartActivity("get_phone_data", ActivityKind.Client);
//Some operation takes place for a length of time
activity.AddTag("param", paramValue)
activity?.Stop();
Вход в полноэкранный режим Выход из полноэкранного режима

С точки зрения доступа к трассам оптимальным способом является встраивание идентификатора трассы в журналы. Тогда вы сможете увидеть связанный трафик вместе с журналами. Такие инструменты, как Grafana Tempo или Zipkin, могут создать график пламени с довольно красивой разбивкой запросов. Ниже я привел пример трассировки, которая была полностью произведена с помощью автоинструментария. Она способна улавливать все исходящие и входящие вызовы. Это касается как урлов, так и данных запросов. Ниже приведен пример трассировки из Grafana. Вы можете видеть, как длина каждого отрезка дает четкое представление о том, какие операции занимают больше всего времени. Вы можете более подробно изучить вызов в проводнике.

Метрики

Метрики позволяют собирать статистические данные, которые можно использовать в сочетании с такими инструментами, как Prometheus.

    services.AddOpenTelemetryMetrics(options =>
    {
        options.SetResourceBuilder(rb)
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation();
        options.AddMeter(SharedTelemetryUtilities.MeterName);
        options.AddView(TelemetryProperties.RequestMeter, new ExplicitBucketHistogramConfiguration()
        {
            Boundaries = { 50, 75, 100, 250, 500, 1000, 2000, 5000, 10000 }
        });
        options.AddOtlpExporter(otlpOptions => { otlpOptions.Endpoint = new Uri(endpoint + "/api/v1/metrics"); });
    });
Вход в полноэкранный режим Выход из полноэкранного режима

Настройка аналогична настройке трассировки. Единственное небольшое отличие — включение представления. Это позволяет использовать нестандартные ведра для отслеживания длительности запроса, мы включаем имя инструментария, чтобы он знал, к чему его применить. Кроме того, мы ввели отслеживание длительности запросов httpclient и других функций aspnet.

        Histogram<double> histogram = null;
        var stopwatch = Stopwatch.StartNew();
        histogram = SharedTelemetryUtilities.Meter.CreateHistogram<double>(TelemetryProperties.RequestMeter);

        //Operation occurs here.
        histogram.Record(stopwatch.Elapsed.TotalMilliseconds);
Вход в полноэкранный режим Выход из полноэкранного режима

Аналогично трассировке, мы можем получить доступ к статическому счетчику и создать с его помощью нужный нам счетчик. Гистограммы имеют смысл для статистической информации, но счетчики и другие инструменты включаются в счетчик в зависимости от потребностей. И снова, мы все еще используем встроенную функциональность dotnet для измерения этих данных.

Ниже показано, как такие данные отправляются в Grafana. Особенно хорошо, что при таком сборе метрик ваши приборные панели загружаются мгновенно, поскольку они не пытаются, например, анализировать журналы.

Сбор данных

Итак, одним из способов, с помощью которого мы сохраняем независимость от платформы, является использование встроенных инструментов dotnet. Следующий способ — это коллектор, такие инструменты, как Grafana или Datadog, имеют свои собственные агенты, которые обычно запускаются вместе с вашими контейнерами. В мире открытой телеметрии мы можем позволить этим агентам умереть той смертью, которой они заслуживают.

Коллектор OpenTelemetry Collector должен работать рядом с вашим приложением аналогично агентам. В примере ниже мы отправляем метрики в экземпляр prometheus, а трассировки — в Tempo, оба основаны на Grafana Cloud, я обфусцировал api ключи.

receivers:
    prometheus:
        config:
        scrape_configs:
        - job_name: 'otel-collector'
            scrape_interval: 10s
            static_configs:
            - targets: ['0.0.0.0:8888']
    otlp:
        protocols:
        grpc:
        http:
processors:
    batch:
        timeout: 10s
    resourcedetection:
        detectors: [ecs,system,env]
exporters:
    otlp:
        endpoint: tempo-us-central1.grafana.net:443
        headers:
        authorization: Basic [ApiKey]
    prometheusremotewrite:
        endpoint: https://364320:api_key@prometheus-prod-10-prod-us-central-0.grafana.net/api/prom/push
        external_labels:
            env: dev
            region: us-west-2    
            app: sample-app
service:
    pipelines:
        traces:
            receivers: [otlp]
            processors: [batch, resourcedetection]
            exporters: [otlp]
        metrics:
            receivers: [prometheus]
            processors: [batch,resourcedetection]
            exporters: [prometheusremotewrite]
Вход в полноэкранный режим Выход из полноэкранного режима

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

Приемники

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

receivers:
    prometheus:
        config:
        scrape_configs:
        - job_name: 'otel-collector'
            scrape_interval: 10s
            static_configs:
            - targets: ['0.0.0.0:8888']
        - job_name: 'node'
            scrape_interval: 30s
            static_configs:
            - targets: ['localhost:9100']
    otlp:
        protocols:
        grpc:
        http:
Вход в полноэкранный режим Выход из полноэкранного режима

Если мы захотим расширить эту систему для использования других инструментов, то просто добавим их. Некоторые не требуют конфигурации, например zipkin: будет конфигурацией по умолчанию для zipkin. Однако собранные данные никуда не попадут, если они не настроены на уровне экспортера.

Процессоры

Я использую процессоры довольно просто. Процессоры работают с самими данными, но также могут дополнительно обогащать их на основе собранной контекстной информации, такой как региональная информация. Обычно они используются для выборки, чтобы уменьшить объем данных, отправляемых провайдерам, и для предотвращения чрезмерного использования памяти сборщиком. В нашем случае мы полагаемся на пакетное и ресурсное обнаружение.

Batch позволяет объединять метрики, трассы и т.д. в пакеты. По умолчанию это 8192, а затем он будет отправлять, таймаут — это максимальное время перед отправкой независимо от этого. Я бы настоятельно рекомендовал использовать пакеты, если вы имеете дело с большими объемами трафика.

    batch:
        timeout: 10s
    resourcedetection:
        detectors: [ecs,system,env]
Вход в полноэкранный режим Выход из полноэкранного режима

Обнаружение ресурсов собирает данные о типе системы, в которой работает. Например, в данном случае он будет собирать стандартную информацию о машине, а также специфическую для Amazon ECS и все связанные с ней переменные окружения.

Экспортеры

Экспортеры эффективно настраивают, в какие места будут отправляться данные. Например, метрики и трассы могут отправляться в несколько разных мест, и мы можем встраивать в них информацию об аутентификации и т. д. Кроме того, в случае с метриками я включил пользовательскую маркировку для отправки метрик в prometheus и настроил аутентификацию для grafana tempo.

exporters:
        otlp:
            endpoint: tempo-us-central1.grafana.net:443
            headers:
            authorization: Basic [Base64 ApiKey]
        prometheusremotewrite:
            endpoint: https://364320:api_key@prometheus-prod-10-prod-us-central-0.grafana.net/api/prom/push
            external_labels:
                env: dev
                region: us-west-2    
                app: sample-app
Вход в полноэкранный режим Выход из полноэкранного режима

Сервис

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

service:
    pipelines:
        traces:
            receivers: [otlp]
            processors: [batch, resourcedetection]
            exporters: [otlp]
        metrics:
            receivers: [prometheus]
            processors: [batch,resourcedetection]
            exporters: [prometheusremotewrite]
Вход в полноэкранный режим Выход из полноэкранного режима

Конвейеры можно представить себе как конвейер развертывания. У нас есть несколько частей, которые могут быть использованы, но если они не настроены в нашем конвейере, они не будут использованы.

Трассы, метрики и журналы настраиваются отдельно. В данном случае у нас настроены только трассировки и метрики.

Приемник otlp используется для трасс, он будет сортировать данные и вставлять в трассы дополнительную информацию о ресурсах. Затем они экспортируются по протоколу otlp в службу grafana’s tempo.

Тем временем метрика использует данные, собранные приемником prometheus, и задействует те же процессоры. Затем они экспортируются с помощью настроенной нами удаленной записи prometheus.

Заключение

OpenTelemetry предлагает решение, которое позволяет гораздо больше контролировать наши данные. В этом материале мы лишь коснулись его преимуществ. Оно получило широкое распространение среди сообщества opensource и основных телеметрических служб.

Есть еще моменты, которые не до конца проработаны, поскольку он все еще находится на стадии бета-версии, однако я уверен, что он готов к производству. Большим преимуществом инструментария является поддержка SaaS-инструментов, таких как Grafana, Datadog и Splunk. Еще одним огромным преимуществом является то, что это заставляет разработчиков думать о большем, чем просто журналы. Трассировки Eg могут предложить гораздо больше пользы, чем журналы, потому что они постоянно фиксируют детали запросов, в том числе, если они не работают и почему.

Общая поддержка различных языков очень сильна, хотя многие библиотеки все еще находятся в бета-версии. Для тех компаний, которые уже используют OpenTraces или OpenCensus, это будет особенно простой переход. Aws также предлагает пользовательские коллекторы, если это необходимо, хотя я обнаружил, что достаточно OpenTelemetry.

Ресурсы

  • Спецификации OpenTelemetry
  • Реестр инструментария для основных языков
  • Документация OpenTelemetry
  • Образец проекта Datadog OpenTelemetry Project
  • Типы метрик OpenTelemetry

Оцените статью
devanswers.ru
Добавить комментарий