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 gRPC OpenTelemetry 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 de gRPC C++ existente
- Cómo ejecutar una instancia local de Prometheus
- Cómo exportar métricas a Prometheus
- Cómo ver métricas desde el 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. La versión más reciente se puede encontrar aquí.
Una forma sencilla de configurarlo es instalarlo como el objeto 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. Las instrucciones para usar CMake se pueden encontrar aquí.
Obtén el código
Para optimizar tu aprendizaje, este codelab ofrece un scaffold de código fuente precompilado para ayudarte a comenzar. En los siguientes pasos, se te guiará para instrumentar el complemento de gRPC OpenTelemetry en una aplicación.
grpc-codelabs
El código fuente del scaffold 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 completed directorio.
Primero, clona el repositorio de codelab de grpc y cd en la carpeta grpc-cpp-opentelemetry:
git clone -b v1 https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
Como alternativa, 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. Registra el complemento de OpenTelemetry
Necesitamos una aplicación de gRPC para agregar el complemento de gRPC OpenTelemetry. 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 así:
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 así:
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;
}
Los archivos de encabezado y las dependencias de compilación necesarios ya se agregaron para mayor comodidad.
#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 ya 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. Ejecuta el ejemplo y visualiza las métricas
Para ejecutar el servidor, ejecuta lo siguiente:
bazel run start_here:greeter_callback_server
Si la configuración es correcta, verás el siguiente resultado para el servidor:
Server listening on 0.0.0.0:50051
Mientras se ejecuta el servidor, 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
Como configuramos el complemento de gRPC OpenTelemetry para exportar métricas con Prometheus, esas métricas estarán disponibles en localhost:9464 para el servidor y localhost:9465 para el cliente.
Para ver las métricas del cliente, haz lo siguiente:
curl localhost:9465/metrics
El resultado sería de la 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, haz lo siguiente:
curl localhost:9464/metrics
5. Visualiza métricas en Prometheus
Aquí, configuraremos una instancia de Prometheus que analizará 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, 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 analicen 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 de intento mediana usando 1 minuto como el período para el cálculo de cuantiles.
Tasa de consultas:
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. Ejercicio opcional para el usuario
En los paneles de Prometheus, notarás que las QPS son bajas. Comprueba si puedes identificar algún código sospechoso en el ejemplo que limite las QPS.
Para los entusiastas, el código del cliente se limita a tener una sola 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 esto).