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 cartographie d'itinéraire écrite en C++.
À la fin de ce tutoriel, vous disposerez d'une application gRPC HelloWorld simple instrumentée avec le plug-in gRPC OpenTelemetry. Vous pourrez également consulter 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 du tableau de bord Prometheus
2. Avant de commencer
Prérequis
gitcurlbuild-essentialclangbazelpour créer des exemples dans 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. Pour accéder à la dernière version, cliquez ici.
Un moyen simple de le configurer consiste à l'installer 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. Pour obtenir des instructions sur l'utilisation de CMake, consultez cette page.
Obtenir le code
Pour faciliter 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 du scaffold pour cet atelier de programmation est disponible dans ce répertoire GitHub. 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 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.
Créez la bibliothèque gRPC à l'aide de Bazel :
bazel build start_here/...
3. Enregistrer le plug-in OpenTelemetry
Nous avons besoin d'une application gRPC pour ajouter le plug-in gRPC OpenTelemetry. Dans cet atelier de programmation, nous allons utiliser un client et un serveur gRPC HelloWorld simples que nous instrumenterons 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() pour qu'il ressemble à ceci :
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 la commande suivante :
bazel run start_here:greeter_callback_server
Si la configuration réussit, 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
Une 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 nous avons configuré le plug-in gRPC OpenTelemetry pour exporter les métriques à l'aide de Prometheus. Ces métriques seront disponibles sur localhost:9464 pour le serveur et sur localhost:9465 pour le client.
Pour afficher les métriques client :
curl localhost:9465/metrics
Le résultat doit respecter le format suivant :
# 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 sur Prometheus
Ici, nous allons configurer une instance Prometheus qui extraira notre exemple de client et de serveur gRPC exportant 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
Cela configurera les métriques des processus de l'atelier de programmation client et serveur pour qu'elles soient récupérées 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]))
affichera un graphique avec la latence médiane des tentatives, en utilisant une fenêtre temporelle d'une minute pour le calcul du quantile.
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 nombre de requêtes par seconde est faible. Essayez d'identifier dans l'exemple de code un code suspect qui limite les requêtes par seconde.
Pour les plus enthousiastes, le code client se limite à n'avoir qu'un seul RPC en attente à un moment donné. Vous pouvez modifier cette valeur pour que le client envoie davantage de RPC sans attendre que les précédents se terminent. (La solution n'a pas été fournie.)