1. מבוא
ב-codelab הזה תשתמשו ב-gRPC כדי ליצור לקוח ושרת שמהווים את הבסיס לאפליקציית מיפוי מסלולים שנכתבה ב-C++.
בסוף המדריך תהיה לכם אפליקציית gRPC HelloWorld פשוטה עם מכשור של פלאגין gRPC OpenTelemetry, ותוכלו לראות את מדדי יכולת התצפית המיוצאים ב-Prometheus.
מה תלמדו
- איך מגדירים את OpenTelemetry Plugin לאפליקציית gRPC C++ קיימת
- הפעלת מופע מקומי של Prometheus
- ייצוא מדדים אל Prometheus
- הצגת מדדים מלוח הבקרה של Prometheus
2. לפני שמתחילים
הדרישות
gitcurlbuild-essentialclangbazelכדי לבנות דוגמאות ב-Codelab הזה
מתקינים את הדרישות המוקדמות:
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang
אפשר להתקין את bazel דרך bazelisk. הגרסה העדכנית זמינה כאן.
דרך פשוטה להגדיר אותו היא להתקין אותו כקובץ הבינארי של bazel בנתיב, באופן הבא:
sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel
אפשר גם להשתמש ב-CMake. הוראות לשימוש ב-CMake זמינות כאן.
קבל את הקוד
כדי לייעל את הלמידה, ב-Codelab הזה מוצעת מסגרת קוד מקור מוכנה מראש שתעזור לכם להתחיל. השלבים הבאים יעזרו לכם להטמיע את הפלאגין gRPC OpenTelemetry באפליקציה.
grpc-codelabs
קוד המקור של ה-scaffold ל-codelab הזה זמין בספרייה הזו ב-GitHub. אם אתם מעדיפים לא להטמיע את הקוד בעצמכם, קוד המקור המלא זמין בספרייה completed.
קודם כול, משכפלים את מאגר ה-codelab של grpc ועוברים לתיקייה grpc-cpp-opentelemetry:
git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/
אפשרות אחרת היא להוריד את קובץ ה- .zip שמכיל רק את ספריית ה-codelab ולבטל את הדחיסה שלו באופן ידני.
יוצרים את ספריית gRPC באמצעות Bazel:
bazel build start_here/...
3. רישום הפלאגין OpenTelemetry
כדי להוסיף את הפלאגין gRPC OpenTelemetry, צריך אפליקציית gRPC. ב-codelab הזה נשתמש בלקוח ובשרת פשוטים של gRPC HelloWorld, שנתקין בהם את הפלאגין gRPC OpenTelemetry.
השלב הראשון הוא לרשום את OpenTelemetry Plugin שהוגדר עם Prometheus exporter בלקוח. פותחים את codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_client.cc בעורך המועדף, ומשנים את main() כך שייראה כמו
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;
}
השלב הבא הוא להוסיף את הפלאגין OpenTelemetry לשרת. פותחים את codelabs/grpc-cpp-opentelemetry/start_here/greeter_callback_server.cc ומשנים את המראה של main כך:
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;
}
כדי שיהיה לכם נוח, כבר הוספנו את קובצי הכותרות הנדרשים ואת יחסי התלות ב-Build.
#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>
יחסי התלות של ה-Build כבר נוספו לקובץ 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. הרצת הדוגמה והצגת המדדים
כדי להריץ את השרת, מריצים את הפקודה –
bazel run start_here:greeter_callback_server
אם ההגדרה הושלמה בהצלחה, הפלט הבא יוצג בשרת –
Server listening on 0.0.0.0:50051
בזמן שהשרת פועל, במסוף אחר, מריצים את הלקוח –
bazel run start_here:greeter_callback_client
הפעלה מוצלחת תיראה כך:
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
הגדרנו את הפלאגין gRPC OpenTelemetry לייצוא מדדים באמצעות Prometheus. המדדים האלה יהיו זמינים בכתובת localhost:9464 לשרת ובכתובת localhost:9465 ללקוח.
כדי לראות את מדדי הלקוח –
curl localhost:9465/metrics
התוצאה תהיה מהצורה –
# 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
באופן דומה, לגבי המדדים בצד השרת –
curl localhost:9464/metrics
5. הצגת מדדים ב-Prometheus
במאמר הזה נגדיר מופע של Prometheus שיבצע scraping של הלקוח והשרת לדוגמה של gRPC שמייצאים מדדים באמצעות Prometheus.
מורידים את הגרסה האחרונה של Prometheus לפלטפורמה שלכם, מחלצים אותה ומריצים אותה:
tar xvfz prometheus-*.tar.gz
cd prometheus-*
יוצרים קובץ תצורה של Prometheus עם ההגדרות הבאות:
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
מפעילים את Prometheus עם ההגדרה החדשה –
./prometheus --config.file=grpc_otel_cpp_prometheus.yml
הפעולה הזו תגדיר את המדדים מתהליכי ה-codelab של הלקוח והשרת כך שהם ייגרדו כל 5 שניות.
כדי לראות את המדדים, עוברים אל http://localhost:9090/graph. לדוגמה, השאילתה –
histogram_quantile(0.5, rate(grpc_client_attempt_duration_seconds_bucket[1m]))
יוצג תרשים עם חביון הניסיון החציוני, כשדקה אחת מוגדרת כחלון הזמן לחישוב הכמותי.
קצב השאילתות –
increase(grpc_client_attempt_duration_seconds_bucket[1m])
6. (אופציונלי) תרגיל למשתמש
במרכזי הבקרה של Prometheus, תראו שערך השאילתות לשנייה נמוך. בודקים אם אפשר לזהות בקוד לדוגמה קוד חשוד שמגביל את מספר השאילתות לשנייה.
למי שרוצה להעמיק, קוד הלקוח מוגבל כך שרק בקשת RPC אחת יכולה להיות בהמתנה בכל רגע נתון. אפשר לשנות את ההגדרה כך שהלקוח ישלח יותר בקשות RPC בלי לחכות להשלמת הבקשות הקודמות. (הפתרון לבעיה הזו לא סופק).