1. Introduction
Dans cet atelier de programmation, vous allez utiliser gRPC pour créer un client et un serveur qui constituent la base d'une application de mappage d'itinéraires écrite en C++.
À la fin de ce tutoriel, vous disposerez d'une application gRPC HelloWorld simple instrumentée avec le plug-in gRPC OpenTelemetry et vous pourrez afficher les métriques d'observabilité exportées dans Prometheus.
Points abordés
- Configurer le plug-in OpenTelemetry pour une application gRPC C++ existante
- Exécuter une instance Prometheus locale
- Exporter des métriques vers Prometheus
- Afficher les métriques à partir du tableau de bord Prometheus
2. Avant de commencer
Ce dont vous avez besoin
gitcurlbuild-essentialclangbazelpour compiler les exemples de cet atelier de programmation
Installez les prérequis :
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang
bazel peut être installé via bazelisk. La dernière version est disponible ici.
Pour le configurer simplement, installez-le en tant que binaire bazel dans votre PATH comme suit :
sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel
Vous pouvez également utiliser CMake. Vous trouverez des instructions sur l'utilisation de CMake ici.
Obtenir le code
Pour simplifier votre apprentissage, cet atelier de programmation propose une structure de code source prédéfinie pour vous aider à démarrer. Les étapes suivantes vous guideront dans l'instrumentation du plug-in gRPC OpenTelemetry dans une application.
grpc-codelabs
Le code source de la structure de cet atelier de programmation est disponible dans ce répertoire GitHub directory. Si vous préférez ne pas implémenter le code vous-même, le code source complet est disponible dans le répertoire completed.
Commencez par cloner le dépôt de l'atelier de programmation gRPC et accédez au dossier grpc-cpp-opentelemetry :
git clone -b v1 https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
Vous pouvez également télécharger le fichier .zip contenant uniquement le répertoire de l'atelier de programmation et le décompresser manuellement.
Compilez la bibliothèque gRPC à l'aide de bazel :
bazel build start_here/...
3. Enregistrer le plug-in OpenTelemetry
Vous avez besoin d'une application gRPC pour ajouter le plug-in gRPC OpenTelemetry. Dans cet atelier de programmation, vous allez utiliser un client et un serveur gRPC HelloWorld simples que vous allez instrumenter avec le plug-in gRPC OpenTelemetry.
La première étape consiste à enregistrer le plug-in OpenTelemetry configuré avec un exportateur Prometheus dans le client. Ouvrez codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc avec votre éditeur préféré et transformez main() comme suit :
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;
}
L'étape suivante consiste à ajouter le plug-in OpenTelemetry au serveur. Ouvrez codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc et transformez main comme suit :
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;
}
Les fichiers d'en-tête et les dépendances de compilation requis ont déjà été ajoutés pour plus de commodité.
#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>
Les dépendances de compilation sont également déjà ajoutées dans le fichier 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. Exécuter l'exemple et afficher les métriques
Pour exécuter le serveur, exécutez :
bazel run start_here:greeter_callback_server
Si la configuration est réussie, le résultat suivant s'affiche pour le serveur :
Server listening on 0.0.0.0:50051
Pendant que le serveur est en cours d'exécution, exécutez le client sur un autre terminal :
bazel run start_here:greeter_callback_client
L'exécution réussie se présente comme suit :
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
Étant donné que vous avez configuré le plug-in gRPC OpenTelemetry pour exporter des métriques à l'aide de Prometheus. Ces métriques seront disponibles sur localhost:9464 pour le serveur et localhost:9465 pour le client.
Pour afficher les métriques du client :
curl localhost:9465/metrics
Le résultat se présente sous la forme suivante :
# 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
De même, pour les métriques côté serveur :
curl localhost:9464/metrics
5. Afficher les métriques dans Prometheus
Ici, vous allez configurer une instance Prometheus qui va extraire votre exemple de client et de serveur gRPC qui exportent des métriques à l'aide de Prometheus.
Téléchargez la dernière version de Prometheus pour votre plate-forme, puis extrayez-la et exécutez-la :
tar xvfz prometheus-*.tar.gz
cd prometheus-*
Créez un fichier de configuration Prometheus avec les éléments suivants :
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
Démarrez Prometheus avec la nouvelle configuration :
./prometheus --config.file=grpc_otel_cpp_prometheus.yml
Les métriques des processus de l'atelier de programmation client et serveur seront extraites toutes les cinq secondes.
Accédez à http://localhost:9090/graph pour afficher les métriques. Par exemple, la requête :
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
affiche un graphique avec la latence médiane des tentatives, en utilisant une minute comme fenêtre temporelle pour le calcul des quantiles.
Taux de requêtes :
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (Facultatif) Exercice pour l'utilisateur
Dans les tableaux de bord Prometheus, vous remarquerez que le QPS est faible. Essayez d'identifier du code suspect dans l'exemple qui limite le QPS.
Pour les plus enthousiastes, le code client se limite à une seule RPC en attente à un moment donné. Vous pouvez le modifier pour que le client envoie plus de RPC sans attendre la fin des précédentes. (La solution n'est pas fournie.)