gRPC C++ में Basic OpenTelemetry Plugin सेट अप करना

1. परिचय

इस कोडलैब में, gRPC का इस्तेमाल करके एक क्लाइंट और सर्वर बनाया जाएगा. ये दोनों, C++ में लिखे गए रूट-मैपिंग ऐप्लिकेशन की बुनियादी संरचना तैयार करेंगे.

ट्यूटोरियल के आखिर तक, आपके पास gRPC OpenTelemetry प्लगिन से इंस्ट्रुमेंट किया गया एक सामान्य gRPC HelloWorld ऐप्लिकेशन होगा. साथ ही, Prometheus में एक्सपोर्ट की गई ऑब्ज़र्वेबिलिटी मेट्रिक देखी जा सकेंगी.

आपको क्या सीखने को मिलेगा

  • मौजूदा gRPC C++ ऐप्लिकेशन के लिए, OpenTelemetry प्लगिन को सेट अप करने का तरीका
  • स्थानीय Prometheus इंस्टेंस चलाना
  • Prometheus में मेट्रिक एक्सपोर्ट करना
  • Prometheus डैशबोर्ड से मेट्रिक देखना

2. शुरू करने से पहले

आपको किन चीज़ों की ज़रूरत होगी

  • git
  • curl
  • build-essential
  • clang
  • bazel का इस्तेमाल करके, इस कोडलैब में उदाहरण बनाए गए हैं

ज़रूरी शर्तें पूरी करें:

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl build-essential clang

bazel को bazelisk के ज़रिए इंस्टॉल किया जा सकता है. सबसे नया वर्शन यहां देखा जा सकता है.

इसे सेट अप करने का आसान तरीका यह है कि इसे अपने PATH में bazel बाइनरी के तौर पर इंस्टॉल करें. इसके लिए, यह तरीका अपनाएं:

sudo cp bazelisk-linux-amd64 /usr/local/bin/bazel
sudo chmod a+x /usr/local/bin/bazel

इसके अलावा, CMake का भी इस्तेमाल किया जा सकता है. CMake का इस्तेमाल करने के निर्देश यहां दिए गए हैं.

कोड प्राप्त करें

इस कोडलैब में, पहले से बना हुआ सोर्स कोड स्केफ़ोल्ड उपलब्ध है. इससे आपको शुरुआत करने में मदद मिलेगी. यहां दिए गए चरणों से, आपको किसी ऐप्लिकेशन में gRPC OpenTelemetry प्लगिन को लागू करने के बारे में जानकारी मिलेगी.

grpc-codelabs

इस कोडलैब के लिए, स्कैफ़ोल्ड सोर्स कोड इस GitHub डायरेक्ट्री में उपलब्ध है. अगर आपको कोड खुद लागू नहीं करना है, तो पूरा सोर्स कोड completed डायरेक्ट्री में उपलब्ध है.

सबसे पहले, grpc कोडलैब रेपो को क्लोन करें और grpc-cpp-opentelemetry फ़ोल्डर में cd करें:

git clone https://github.com/grpc-ecosystem/grpc-codelabs.git
cd grpc-codelabs/codelabs/grpc-cpp-opentelemetry/

इसके अलावा, सिर्फ़ कोडलैब डायरेक्ट्री वाली .zip फ़ाइल डाउनलोड करके, उसे मैन्युअल तरीके से अनज़िप किया जा सकता है.

bazel का इस्तेमाल करके, gRPC लाइब्रेरी बनाएं:

bazel build start_here/...

3. OpenTelemetry प्लगिन रजिस्टर करना

gRPC OpenTelemetry प्लगिन जोड़ने के लिए, हमें gRPC ऐप्लिकेशन की ज़रूरत होती है. इस कोडलैब में, हम एक सामान्य gRPC HelloWorld क्लाइंट और सर्वर का इस्तेमाल करेंगे. हम इसे gRPC OpenTelemetry प्लगिन के साथ इंस्ट्रुमेंट करेंगे.

सबसे पहले, आपको क्लाइंट में Prometheus एक्सपोर्टर के साथ कॉन्फ़िगर किए गए OpenTelemetry प्लगिन को रजिस्टर करना होगा. अपने पसंदीदा एडिटर में 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 खोलें और मुख्य बातचीत को इस तरह बदलें:

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;
}

ज़रूरी हेडर फ़ाइलें और बिल्ड डिपेंडेंसी पहले ही जोड़ दी गई हैं, ताकि आपको आसानी हो.

#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 फ़ाइल में पहले से ही जोड़ दिया गया है -

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

हमने Prometheus का इस्तेमाल करके मेट्रिक एक्सपोर्ट करने के लिए, gRPC OpenTelemetry प्लगिन सेट अप किया है. ये मेट्रिक, सर्वर के लिए 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 इंस्टेंस सेटअप करेंगे जो हमारे 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

इससे क्लाइंट और सर्वर कोडलैब प्रोसेस से मेट्रिक को कॉन्फ़िगर किया जाएगा, ताकि हर पांच सेकंड में उन्हें स्क्रैप किया जा सके.

मेट्रिक देखने के लिए, 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 डैशबोर्ड में दिखेगा कि QPS कम है. देखें कि क्या आपको उदाहरण में कोई ऐसा संदिग्ध कोड मिल रहा है जिसकी वजह से क्यूपीएस सीमित हो रहा है.

क्लाइंट कोड, एक समय में सिर्फ़ एक आरपीसी को प्रोसेस कर सकता है. इसे बदला जा सकता है, ताकि क्लाइंट पिछले आरपीसी के पूरा होने का इंतज़ार किए बिना ज़्यादा आरपीसी भेज सके. (इस समस्या को हल करने का तरीका नहीं बताया गया है.)