Настройка базового плагина OpenTelemetry в gRPC Java

1. Введение

В этом практическом занятии вы используете gRPC для создания клиента и сервера, которые составляют основу приложения для сопоставления маршрутов, написанного на Java.

К концу этого урока у вас будет простое gRPC-приложение HelloWorld, оснащенное плагином gRPC OpenTelemetry, и вы сможете увидеть экспортированные метрики наблюдаемости в Prometheus.

Что вы узнаете

  • Как настроить плагин OpenTelemetry для существующего Java-приложения gRPC
  • Запуск локального экземпляра Prometheus
  • Экспорт метрик в Prometheus
  • Просмотр метрик на панели мониторинга Prometheus

2. Прежде чем начать

Что вам понадобится

  • git
  • curl
  • JDK версии 8 или выше

Установите необходимые компоненты:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl

Получите код

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

grpc-codelabs

Исходный код для этого практического занятия доступен в этой директории GitHub. Если вы предпочитаете не реализовывать код самостоятельно, готовый исходный код доступен в директории completed .

Сначала клонируйте репозиторий grpc codelab и перейдите в папку grpc-java-opentelemetry:

git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-java-opentelemetry/

В качестве альтернативы вы можете скачать ZIP-архив, содержащий только папку codelab, и распаковать его вручную.

3. Зарегистрируйте плагин OpenTelemetry.

Нам потребуется gRPC-приложение для добавления плагина gRPC OpenTelemetry. В этом практическом занятии мы будем использовать простой gRPC-клиент и сервер HelloWorld, которые мы будем модифицировать с помощью плагина gRPC OpenTelemetry.

Первым делом зарегистрируйте плагин OpenTelemetry, настроенный с использованием экспортера Prometheus в клиенте. Откройте codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java в вашем любимом редакторе, затем измените файл main, добавив код для настройки API OpenTelemetry для gRPC Java.

Настройка инструментов мониторинга на клиентском компьютере.

Создать экспортер Prometheus

Создайте объект PrometheusHttpServer для преобразования метрик OpenTelemetry в формат Prometheus и предоставления к ним доступа через HttpServer. Следующий фрагмент кода создает новый объект Prometheus Exporter .

// Default prometheus port i.e `prometheusPort` has been initialized to 9465
 
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)         
        .build();

Создайте экземпляр OpenTelemetry SDK.

Зарегистрируйте объект prometheusExporter и создайте объект MetricReader для чтения метрик из SdkMeterProvider . SdkMeterProvider используется для настройки параметров метрик.

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

Создайте экземпляр OpenTelemetrySdk с помощью созданного выше sdkMeterProvider для реализации OpenTelemetry в SDK.

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

Создайте экземпляр GrpcOpenTelemetry.

Используя API GrpcOpenTelemetry, настройте SDK OpenTelemetry, который использует экспортер метрик Prometheus.

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();

// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

После глобальной регистрации экземпляра GrpcOpenTelemetry с помощью registerGlobal все последующие gRPC-клиенты и серверы будут оснащены OpenTelemetry.

Завершение работы OpenTelemetry SDK

Завершение работы должно происходить внутри ShutDownHook. openTelemetrySdk.close() завершает работу SDK и также вызывает метод shutdown для SdkMeterProvider.

Настройка средств мониторинга на сервере.

Аналогичным образом добавим GrpcOpenTelemetry к серверу. Откройте codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java и добавьте код для инициализации GrpcOpenTelemetry.

Создать экспортер Prometheus

Поскольку данное практическое задание может выполняться на одной и той же машине, мы используем разные порты для размещения метрик на стороне gRPC-сервера, чтобы избежать конфликтов портов при создании PrometheusHttpServer.

// Default prometheus port i.e `prometheusPort` has been set to 9464

PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)
        .build();

Создайте экземпляр OpenTelemetry SDK.

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

Инициализация GrpcOpenTelemetry с помощью OpenTelemetry SDK

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

Создайте экземпляр GrpcOpenTelemetry.

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();
    // Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

Завершение работы OpenTelemetry SDK

После завершения работы gRPC-канала вызов метода openTelemetrySdk.close() завершает работу SDK, а также вызывает метод shutdown для SdkMeterProvider.

4. Запуск примера и просмотр метрик.

Для запуска сервера выполните команду:

cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server

После успешной настройки вы увидите следующий вывод для сервера:

[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051

Пока сервер работает, на другом терминале запустите клиент.

./build/install/start_here/bin/opentelemetry-client world

Успешный забег будет выглядеть следующим образом:

[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world 
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Will try to greet world ...
[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world

Поскольку мы настроили плагин gRPC OpenTelemetry для экспорта метрик с помощью Prometheus, эти метрики будут доступны на localhost:9464 для сервера и localhost:9465 для клиента.

Чтобы просмотреть показатели клиента -

curl localhost:9465/metrics

В результате получится следующее:

# HELP grpc_client_attempt_duration_seconds Time taken to complete a client call attempt
# TYPE grpc_client_attempt_duration_seconds histogram
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.002"} 0
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.004"} 14
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.005"} 29
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.1"} 33
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_attempt_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_attempt_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.46512665300000006
# HELP grpc_client_attempt_rcvd_total_compressed_message_size_bytes Compressed message bytes received per call attempt
# TYPE grpc_client_attempt_rcvd_total_compressed_message_size_bytes histogram
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_rcvd_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 442.0
# HELP grpc_client_attempt_sent_total_compressed_message_size_bytes Compressed message bytes sent per client call attempt
# TYPE grpc_client_attempt_sent_total_compressed_message_size_bytes histogram
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="1024.0"} 34
grpc_client_attempt_sent_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 238.0
# HELP grpc_client_attempt_started_total Number of client call attempts started
# TYPE grpc_client_attempt_started_total counter
grpc_client_attempt_started_total{grpc_method="helloworld.Greeter/SayHello",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34.0
# HELP grpc_client_call_duration_seconds Time taken by gRPC to complete an RPC from application's perspective
# TYPE grpc_client_call_duration_seconds histogram
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_call_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_call_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.512708707
# TYPE target_info gauge
target_info{service_name="unknown_service:java",telemetry_sdk_language="java",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.40.0"} 1

Аналогично и для метрик на стороне сервера -

curl localhost:9464/metrics

5. Просмотр метрик в Prometheus

Здесь мы настроим экземпляр Prometheus, который будет собирать метрики с нашего примера gRPC-клиента и сервера, экспортирующих метрики с помощью Prometheus.

Загрузите последнюю версию Prometheus для вашей платформы, затем распакуйте и запустите её:

tar xvfz prometheus-*.tar.gz
cd prometheus-*

Создайте конфигурационный файл Prometheus со следующим содержимым:

cat > grpc_otel_java_prometheus.yml <<EOF
scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "grpc-otel-java"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9464", "localhost:9465"]
EOF

Запустите Prometheus с новой конфигурацией.

./prometheus --config.file=grpc_otel_java_prometheus.yml

Это позволит настроить сбор метрик из процессов Codelab на стороне клиента и сервера таким образом, чтобы они собирались каждые 5 секунд.

Для просмотра метрик перейдите по адресу http://localhost:9090/graph . Например, запрос -

histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))

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

Частота запросов -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (Необязательное) Упражнение для пользователя

На панелях мониторинга Prometheus вы заметите, что показатель QPS низкий. Попробуйте определить в примере какой-нибудь подозрительный код, который ограничивает показатель QPS.

Для энтузиастов: клиентский код ограничивается выполнением только одного ожидающего RPC-вызова в данный момент. Это можно изменить, чтобы клиент отправлял больше RPC-вызовов, не дожидаясь завершения предыдущих. (Решение для этого пока не предложено.)