ตั้งค่าปลั๊กอิน OpenTelemetry พื้นฐานใน gRPC C++

1. บทนำ

ในโค้ดแล็บนี้ คุณจะได้ใช้ gRPC เพื่อสร้างไคลเอ็นต์และเซิร์ฟเวอร์ซึ่งเป็นรากฐานของแอปพลิเคชันการแมปเส้นทางที่เขียนด้วย C++

เมื่อสิ้นสุดบทแนะนำนี้ คุณจะมีแอปพลิเคชัน gRPC HelloWorld แบบง่ายๆ ที่ติดตั้งปลั๊กอิน gRPC OpenTelemetry และจะดูเมตริกการสังเกตการณ์ที่ส่งออกใน Prometheus ได้

สิ่งที่คุณจะได้เรียนรู้

  • วิธีตั้งค่าปลั๊กอิน OpenTelemetry สำหรับแอปพลิเคชัน gRPC C++ ที่มีอยู่
  • การเรียกใช้อินสแตนซ์ Prometheus ในเครื่อง
  • ส่งออกเมตริกไปยัง Prometheus
  • ดูเมตริกจากแดชบอร์ด Prometheus

2. ก่อนเริ่มต้น

สิ่งที่คุณต้องมี

  • git
  • curl
  • build-essential
  • clang
  • bazel เพื่อสร้างตัวอย่างใน Codelab นี้

ติดตั้งข้อกำหนดเบื้องต้น

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

bazel สามารถติดตั้งผ่าน bazelisk ได้ ดูเวอร์ชันล่าสุดได้ที่นี่

วิธีตั้งค่าอย่างง่ายคือการติดตั้งเป็นไบนารี Bazel ใน PATH ดังนี้

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

หรือจะใช้ CMake ก็ได้ ดูวิธีการใช้ CMake ได้ที่นี่

รับโค้ด

Codelab นี้มีโครงร่างซอร์สโค้ดที่สร้างไว้ล่วงหน้าเพื่อช่วยให้คุณเริ่มต้นใช้งานได้ง่ายขึ้น ขั้นตอนต่อไปนี้จะแนะนําคุณตลอดการติดตั้งใช้งานปลั๊กอิน gRPC OpenTelemetry ในแอปพลิเคชัน

grpc-codelabs

ซอร์สโค้ดโครงร่างสำหรับ Codelab นี้อยู่ในไดเรกทอรีนี้ใน GitHub หากไม่ต้องการติดตั้งใช้งานโค้ดด้วยตนเอง คุณจะดูซอร์สโค้ดที่เสร็จสมบูรณ์ได้ในไดเรกทอรี completed

ก่อนอื่น ให้โคลนที่เก็บโค้ดแล็บ grpc แล้ว cd ไปยังโฟลเดอร์ 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 เพื่อเพิ่มปลั๊กอิน gRPC OpenTelemetry ในโค้ดแล็บนี้ เราจะใช้ไคลเอ็นต์และเซิร์ฟเวอร์ gRPC HelloWorld แบบง่ายๆ ซึ่งเราจะติดตั้งเครื่องมือด้วยปลั๊กอิน OpenTelemetry ของ gRPC

ขั้นตอนแรกคือการลงทะเบียนปลั๊กอิน OpenTelemetry ที่กำหนดค่าด้วยเครื่องมือส่งออก Prometheus ในไคลเอ็นต์ เปิด 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;
}

เราได้เพิ่มไฟล์ส่วนหัวและการพึ่งพาการบิลด์ที่จำเป็นไว้แล้วเพื่อความสะดวก

#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

เนื่องจากเราได้ตั้งค่าปลั๊กอิน 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 ที่จะคัดลอกไคลเอ็นต์และเซิร์ฟเวอร์ตัวอย่าง 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]))

จะแสดงกราฟที่มีเวลาในการตอบสนองของความพยายามที่ค่ามัธยฐานโดยใช้ 1 นาทีเป็นกรอบเวลาสำหรับการคำนวณควอนไทล์

อัตราการค้นหา -

increase(grpc_client_attempt_duration_seconds_bucket[1m])

6. (ไม่บังคับ) แบบฝึกหัดสำหรับผู้ใช้

ในแดชบอร์ด Prometheus คุณจะเห็นว่า QPS ต่ำ ดูว่าคุณสามารถระบุโค้ดที่น่าสงสัยในตัวอย่างที่จำกัด QPS ได้หรือไม่

สำหรับผู้ที่กระตือรือร้น รหัสไคลเอ็นต์จะจำกัดตัวเองให้มี RPC ที่รอดำเนินการเพียงรายการเดียวในขณะใดก็ตาม ซึ่งสามารถแก้ไขเพื่อให้ไคลเอ็นต์ส่ง RPC เพิ่มเติมได้โดยไม่ต้องรอให้ RPC ก่อนหน้าเสร็จสมบูรณ์ (ยังไม่มีวิธีแก้ปัญหานี้)