1. Introduzione
In questo codelab, utilizzerai gRPC per creare un client e un server che costituiscono la base di un'applicazione di mappatura di itinerari scritta in C++.
Al termine del tutorial, avrai una semplice applicazione gRPC HelloWorld instrumentata con il plug-in gRPC OpenTelemetry e potrai visualizzare le metriche di osservabilità esportate in Prometheus.
Obiettivi didattici
- Come configurare il plug-in OpenTelemetry per l'applicazione gRPC C++ esistente
- Esecuzione di un'istanza Prometheus locale
- Esportazione delle metriche in Prometheus
- Visualizzare le metriche dalla dashboard Prometheus
2. Prima di iniziare
Che cosa ti serve
gitcurlbuild-essentialclangbazelper creare esempi in questo codelab
Installa i prerequisiti:
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang
bazel può essere installato tramite bazelisk. La versione più recente è disponibile qui.
Un modo semplice per configurarlo è installarlo come programma binario Bazel nel tuo PATH nel seguente modo:
sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel
In alternativa, puoi anche utilizzare CMake. Le istruzioni per l'utilizzo di CMake sono disponibili qui.
Ottieni il codice
Per semplificare l'apprendimento, questo codelab offre uno scaffold di codice sorgente predefinito per aiutarti a iniziare. I seguenti passaggi ti guideranno nell'instrumentazione del plug-in gRPC OpenTelemetry in un'applicazione.
grpc-codelabs
Il codice sorgente dello scaffold per questo codelab è disponibile in questa directory di GitHub. Se preferisci non implementare il codice autonomamente, il codice sorgente completato è disponibile nella directory completed.
Innanzitutto, clona il repository del codelab grpc e passa alla cartella grpc-cpp-opentelemetry:
git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
In alternativa, puoi scaricare il file .zip contenente solo la directory del codelab e decomprimerlo manualmente.
Crea la libreria gRPC utilizzando Bazel:
bazel build start_here/...
3. Registra il plug-in OpenTelemetry
Abbiamo bisogno di un'applicazione gRPC per aggiungere il plug-in gRPC OpenTelemetry. In questo codelab utilizzeremo un semplice client e server gRPC HelloWorld che strumenteremo con il plug-in gRPC OpenTelemetry.
Il primo passaggio consiste nel registrare il plug-in OpenTelemetry configurato con un esportatore Prometheus nel client. Apri codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc con il tuo editor preferito e trasforma main() in modo che abbia questo aspetto:
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;
}
Il passaggio successivo consiste nell'aggiungere il plug-in OpenTelemetry al server. Apri codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc e trasforma main in modo che abbia questo aspetto:
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;
}
Per comodità, sono già stati aggiunti i file di intestazione e le dipendenze di compilazione richiesti.
#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>
Anche le dipendenze di build sono già state aggiunte nel file 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. Esecuzione dell'esempio e visualizzazione delle metriche
Per eseguire il server, esegui:
bazel run start_here:greeter_callback_server
Se la configurazione è riuscita, vedrai il seguente output per il server:
Server listening on 0.0.0.0:50051
Mentre il server è in esecuzione, in un altro terminale esegui il client:
bazel run start_here:greeter_callback_client
Un'esecuzione corretta sarà simile a questa:
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
Poiché abbiamo configurato il plug-in gRPC OpenTelemetry per esportare le metriche utilizzando Prometheus. Queste metriche saranno disponibili su localhost:9464 per il server e su localhost:9465 per il client.
Per visualizzare le metriche client:
curl localhost:9465/metrics
Il risultato sarà del tipo:
# 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
Analogamente, per le metriche lato server:
curl localhost:9464/metrics
5. Visualizzazione delle metriche su Prometheus
Qui configureremo un'istanza Prometheus che eseguirà lo scraping del nostro client e server di esempio gRPC che esportano metriche utilizzando Prometheus.
Scarica l'ultima release di Prometheus per la tua piattaforma, quindi estraila ed eseguila:
tar xvfz prometheus-*.tar.gz
cd prometheus-*
Crea un file di configurazione di Prometheus con quanto segue:
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
Avvia prometheus con la nuova configurazione:
./prometheus --config.file=grpc_otel_cpp_prometheus.yml
In questo modo, le metriche dei processi codelab client e server verranno sottoposte a scraping ogni 5 secondi.
Vai a http://localhost:9090/graph per visualizzare le metriche. Ad esempio, la query
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
mostrerà un grafico con la latenza mediana dei tentativi utilizzando 1 minuto come finestra temporale per il calcolo del quantile.
Tasso di query:
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (Facoltativo) Esercizio per l'utente
Nelle dashboard di Prometheus noterai che il valore QPS è basso. Vedi se riesci a identificare del codice sospetto nell'esempio che limita le QPS.
Per gli utenti entusiasti, il codice client si limita a una sola RPC in attesa in un determinato momento. Questo valore può essere modificato in modo che il client invii più RPC senza attendere il completamento di quelle precedenti. (La soluzione non è stata fornita.)