1. Introducción
En este codelab, usarás gRPC para crear un cliente y un servidor que formen la base de una aplicación de asignación de rutas escrita en C++.
Al final del instructivo, tendrás una aplicación simple de gRPC HelloWorld instrumentada con el complemento de OpenTelemetry de gRPC y podrás ver las métricas de observabilidad exportadas en Prometheus.
Qué aprenderás
- Cómo configurar el complemento de OpenTelemetry para una aplicación existente de gRPC C++
- Ejecuta una instancia local de Prometheus
- Exporta métricas a Prometheus
- Visualiza las métricas del panel de Prometheus
2. Antes de comenzar
Requisitos
gitcurlbuild-essentialclangbazelpara compilar ejemplos en este codelab
Instala los requisitos previos:
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang
bazel se puede instalar a través de bazelisk. Puedes encontrar la versión más reciente aquí.
Una forma sencilla de configurarlo es instalarlo como el binario de Bazel en tu PATH de la siguiente manera:
sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel
Como alternativa, también puedes usar CMake. Aquí encontrarás las instrucciones para usar CMake.
Obtén el código
Para optimizar tu aprendizaje, este codelab ofrece un código fuente precompilado que te ayudará a comenzar. Los siguientes pasos te guiarán para instrumentar el complemento de OpenTelemetry de gRPC en una aplicación.
grpc-codelabs
El código fuente del andamio para este codelab está disponible en este directorio de GitHub. Si prefieres no implementar el código por tu cuenta, el código fuente completo está disponible en el directorio completed.
Primero, clona el repo del codelab de gRPC y cambia el directorio a la carpeta grpc-cpp-opentelemetry:
git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
También puedes descargar el archivo .zip que contiene solo el directorio del codelab y descomprimirlo de forma manual.
Compila la biblioteca de gRPC con Bazel:
bazel build start_here/...
3. Cómo registrar el complemento de OpenTelemetry
Necesitamos una aplicación de gRPC para agregar el complemento de OpenTelemetry de gRPC. En este codelab, usaremos un cliente y un servidor simples de gRPC HelloWorld que instrumentaremos con el complemento de gRPC OpenTelemetry.
El primer paso es registrar el complemento de OpenTelemetry configurado con un exportador de Prometheus en el cliente. Abre codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc con tu editor favorito y transforma main() para que se vea de la siguiente manera:
int main(int argc, char **argv) {
absl::ParseCommandLine(argc, argv);
// Codelab Solution: Register a global gRPC OpenTelemetry plugin configured
// with a prometheus exporter.
opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
auto prometheus_exporter =
opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
// The default histogram boundaries are not granular enough for RPCs. Override
// the "grpc.client.attempt.duration" view as recommended by
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
AddLatencyView(meter_provider.get(), "grpc.client.attempt.duration", "s");
meter_provider->AddMetricReader(std::move(prometheus_exporter));
auto status = grpc::OpenTelemetryPluginBuilder()
.SetMeterProvider(std::move(meter_provider))
.BuildAndRegisterGlobal();
if (!status.ok()) {
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
<< status.ToString() << std::endl;
return static_cast<int>(status.code());
}
// Continuously send RPCs.
RunClient(absl::GetFlag(FLAGS_target));
return 0;
}
El siguiente paso es agregar el complemento de OpenTelemetry al servidor. Abre codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc y transforma main para que se vea de la siguiente manera:
int main(int argc, char** argv) {
absl::ParseCommandLine(argc, argv);
// Register a global gRPC OpenTelemetry plugin configured with a prometheus
// exporter.
opentelemetry::exporter::metrics::PrometheusExporterOptions opts;
opts.url = absl::GetFlag(FLAGS_prometheus_endpoint);
auto prometheus_exporter =
opentelemetry::exporter::metrics::PrometheusExporterFactory::Create(opts);
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
// The default histogram boundaries are not granular enough for RPCs. Override
// the "grpc.server.call.duration" view as recommended by
// https://github.com/grpc/proposal/blob/master/A66-otel-stats.md.
AddLatencyView(meter_provider.get(), "grpc.server.call.duration", "s");
meter_provider->AddMetricReader(std::move(prometheus_exporter));
auto status = grpc::OpenTelemetryPluginBuilder()
.SetMeterProvider(std::move(meter_provider))
.BuildAndRegisterGlobal();
if (!status.ok()) {
std::cerr << "Failed to register gRPC OpenTelemetry Plugin: "
<< status.ToString() << std::endl;
return static_cast<int>(status.code());
}
RunServer(absl::GetFlag(FLAGS_port));
return 0;
}
Para mayor comodidad, ya se agregaron los archivos de encabezado y las dependencias de compilación necesarios.
#include "opentelemetry/exporters/prometheus/exporter_factory.h"
#include "opentelemetry/exporters/prometheus/exporter_options.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include <grpcpp/ext/otel_plugin.h>
Las dependencias de compilación también se agregaron en el archivo BUILD:
cc_binary(
name = "greeter_callback_client",
srcs = ["greeter_callback_client.cc"],
defines = ["BAZEL_BUILD"],
deps = [
"//util:util",
"@com_github_grpc_grpc//:grpc++",
"@com_github_grpc_grpc//:grpcpp_otel_plugin",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/flags:parse",
"@io_opentelemetry_cpp//exporters/prometheus:prometheus_exporter",
"@io_opentelemetry_cpp//sdk/src/metrics",
],
)
4. Cómo ejecutar el ejemplo y ver las métricas
Para ejecutar el servidor, ejecuta el siguiente comando:
bazel run start_here:greeter_callback_server
Si la configuración se realiza correctamente, verás el siguiente resultado para el servidor:
Server listening on 0.0.0.0:50051
Mientras el servidor se ejecuta, en otra terminal, ejecuta el cliente:
bazel run start_here:greeter_callback_client
Una ejecución correcta se verá de la siguiente manera:
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Greeter received: Hello world
Dado que configuramos el complemento de OpenTelemetry de gRPC para exportar métricas con Prometheus. Esas métricas estarán disponibles en localhost:9464 para el servidor y en localhost:9465 para el cliente.
Para ver las métricas del cliente, haz lo siguiente:
curl localhost:9465/metrics
El resultado tendría la siguiente forma:
# HELP exposer_transferred_bytes_total Transferred bytes to metrics services
# TYPE exposer_transferred_bytes_total counter
exposer_transferred_bytes_total 0
# HELP exposer_scrapes_total Number of times metrics were scraped
# TYPE exposer_scrapes_total counter
exposer_scrapes_total 0
# HELP exposer_request_latencies Latencies of serving scrape requests, in microseconds
# TYPE exposer_request_latencies summary
exposer_request_latencies_count 0
exposer_request_latencies_sum 0
exposer_request_latencies{quantile="0.5"} Nan
exposer_request_latencies{quantile="0.9"} Nan
exposer_request_latencies{quantile="0.99"} Nan
# HELP target Target metadata
# TYPE target gauge
target_info{otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev",service_name="unknown_service",telemetry_sdk_version="1.13.0",telemetry_sdk_name="opentelemetry",telemetry_sdk_language="cpp"} 1 1721958543107
# 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_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-c++",otel_scope_version="1.67.0-dev"} 96 1721958543107
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-c++",otel_scope_version="1.67.0-dev"} 1248 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="0"} 0 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="5"} 0 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="10"} 0 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="25"} 96 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="50"} 96 1721958543107
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-c++",otel_scope_version="1.67.0-dev",le="75"} 96 1721958543107
Del mismo modo, para las métricas del servidor, se aplican las siguientes consideraciones:
curl localhost:9464/metrics
5. Visualiza métricas en Prometheus
Aquí, configuraremos una instancia de Prometheus que extraerá nuestro cliente y servidor de ejemplo de gRPC que exportan métricas con Prometheus.
Descarga la versión más reciente de Prometheus para tu plataforma, y luego extráela y ejecútala:
tar xvfz prometheus-*.tar.gz
cd prometheus-*
Crea un archivo de configuración de Prometheus con lo siguiente:
cat > grpc_otel_cpp_prometheus.yml <<EOF
scrape_configs:
- job_name: "prometheus"
scrape_interval: 5s
static_configs:
- targets: ["localhost:9090"]
- job_name: "grpc-otel-cpp"
scrape_interval: 5s
static_configs:
- targets: ["localhost:9464", "localhost:9465"]
EOF
Inicia Prometheus con la nueva configuración:
./prometheus --config.file=grpc_otel_cpp_prometheus.yml
Esto configurará las métricas de los procesos del codelab del cliente y del servidor para que se extraigan cada 5 segundos.
Ve a http://localhost:9090/graph para ver las métricas. Por ejemplo, la siguiente consulta:
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
mostrará un gráfico con la latencia mediana de intentos usando 1 minuto como el período para el cálculo del cuantil.
Tasa de consultas:
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. Ejercicio opcional para el usuario
En los paneles de Prometheus, observarás que el QPS es bajo. Intenta identificar código sospechoso en el ejemplo que limita las QPS.
Para los entusiastas, el código del cliente se limita a tener solo un RPC pendiente en un momento determinado. Esto se puede modificar para que el cliente envíe más RPC sin esperar a que se completen las anteriores. (No se proporcionó la solución para este problema).