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

1. บทนำ

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

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

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

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

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

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

  • git
  • curl
  • JDK v8 ขึ้นไป

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

sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y git curl

รับโค้ด

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

grpc-codelabs

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

ก่อนอื่น ให้โคลนที่เก็บโค้ดแล็บ grpc แล้ว cd ไปยังโฟลเดอร์ grpc-java-opentelemetry

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

หรือคุณจะดาวน์โหลดไฟล์ .zip ที่มีเฉพาะไดเรกทอรี Codelab แล้วแตกไฟล์ด้วยตนเองก็ได้

3. ลงทะเบียนปลั๊กอิน OpenTelemetry

เราต้องมีแอปพลิเคชัน gRPC เพื่อเพิ่มปลั๊กอิน gRPC OpenTelemetry ใน Codelab นี้ เราจะใช้ไคลเอ็นต์และเซิร์ฟเวอร์ gRPC HelloWorld แบบง่ายๆ ซึ่งเราจะติดตั้งเครื่องมือด้วยปลั๊กอิน OpenTelemetry ของ gRPC

ขั้นตอนแรกคือการลงทะเบียนปลั๊กอิน OpenTelemetry ที่กำหนดค่าด้วยเครื่องมือส่งออก Prometheus ในไคลเอ็นต์ เปิด codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryClient.java ด้วยโปรแกรมแก้ไขที่คุณชื่นชอบ จากนั้นแก้ไข main เพื่อเพิ่มโค้ดในการตั้งค่า gRPC Java OpenTelemetry API

ตั้งค่าการวัดผลในไคลเอ็นต์

สร้างตัวส่งออก Prometheus

สร้าง PrometheusHttpServer เพื่อแปลงเมตริก OpenTelemetry เป็นรูปแบบ Prometheus และแสดงเมตริกเหล่านี้ผ่าน HttpServer ข้อมูลโค้ดต่อไปนี้จะสร้าง Prometheus Exporter ใหม่

// Default prometheus port i.e `prometheusPort` has been initialized to 9465
 
PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)         
        .build();

สร้างอินสแตนซ์ OpenTelemetry SDK

ลงทะเบียน prometheusExporter ด้านบนเป็น MetricReader เพื่ออ่านเมตริกจาก SdkMeterProvider SdkMeterProvider ใช้เพื่อกำหนดการตั้งค่าเมตริก

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

สร้างอินสแตนซ์ของ OpenTelemetrySdk ด้วย sdkMeterProvider ที่สร้างขึ้นข้างต้นสำหรับการติดตั้งใช้งาน SDK ของ OpenTelemetry

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

สร้างอินสแตนซ์ GrpcOpenTelemetry

ใช้ API ของ GrpcOpenTelemetry เพื่อตั้งค่า OpenTelemetry SDK ซึ่งใช้ตัวส่งออกเมตริกของ Prometheus

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();

// Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

เมื่อลงทะเบียนอินสแตนซ์ GrpcOpenTelemetry ทั่วโลกโดยใช้ registerGlobal ไคลเอ็นต์และเซิร์ฟเวอร์ gRPC ที่สร้างขึ้นในภายหลังทั้งหมดจะได้รับการวัดผลด้วย OpenTelemetry

ปิด SDK ของ OpenTelemetry

การปิดระบบต้องเกิดขึ้นภายใน ShutDownHook openTelemetrySdk.close() จะปิด SDK และเรียกใช้การปิดใน SdkMeterProvider ด้วย

ตั้งค่าการตรวจสอบในเซิร์ฟเวอร์

ในทำนองเดียวกัน เรามาเพิ่ม GrpcOpenTelemetry ลงในเซิร์ฟเวอร์ด้วย เปิด codelabs/grpc-java-opentelemetry/start_here/src/main/java/io/grpc/codelabs/opentelemetry/OpenTelemetryServer.java แล้วเพิ่มโค้ดเพื่อเริ่มต้น GrpcOpenTelemetry

สร้างตัวส่งออก Prometheus

เนื่องจาก Codelab นี้อาจทำงานจากเครื่องเดียวกัน เราจึงใช้พอร์ตอื่นเพื่อโฮสต์เมตริกฝั่งเซิร์ฟเวอร์ gRPC เพื่อหลีกเลี่ยงความขัดแย้งของพอร์ตขณะสร้าง PrometheusHttpServer

// Default prometheus port i.e `prometheusPort` has been set to 9464

PrometheusHttpServer prometheusExporter = PrometheusHttpServer.builder()
        .setPort(prometheusPort)
        .build();

สร้างอินสแตนซ์ OpenTelemetry SDK

SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
        .registerMetricReader(prometheusExporter)
        .build();

เริ่มต้น GrpcOpenTelemetry ด้วย OpenTelemetry SDK

OpenTelemetrySdk openTelemetrySdk =OpenTelemetrySdk.builder()
        .setMeterProvider(sdkMeterProvider)
        .build();

สร้างอินสแตนซ์ GrpcOpenTelemetry

GrpcOpenTelemetry grpcOpenTelmetry = GrpcOpenTelemetry.newBuilder()
        .sdk(openTelemetrySdk)
        .build();
    // Registers gRPC OpenTelemetry globally.
grpcOpenTelmetry.registerGlobal();

ปิด SDK ของ OpenTelemetry

หลังจากปิดช่อง gRPC การเรียกใช้ openTelemetrySdk.close() จะปิด SDK และเรียกใช้การปิดใน SdkMeterProvider ด้วย

4. การเรียกใช้ตัวอย่างและการดูเมตริก

หากต้องการเรียกใช้เซิร์ฟเวอร์ ให้เรียกใช้ -

cd start_here
../gradlew installDist
./build/install/start_here/bin/opentelemetry-server

หากตั้งค่าสำเร็จ คุณจะเห็นเอาต์พุตต่อไปนี้สำหรับเซิร์ฟเวอร์

[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryServer start
INFO: Server started, listening on 50051

ขณะที่เซิร์ฟเวอร์ทำงาน ให้เรียกใช้ไคลเอ็นต์ในเทอร์มินัลอื่นโดยใช้คำสั่งต่อไปนี้

./build/install/start_here/bin/opentelemetry-client world

การเรียกใช้ที่สำเร็จจะมีลักษณะดังนี้

[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world 
[date and time] io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Will try to greet world ...
[date and time]io.grpc.codelabs.opentelemetry.OpenTelemetryClient greet
INFO: Greeting: Hello world

เนื่องจากเราได้ตั้งค่าปลั๊กอิน gRPC OpenTelemetry เพื่อส่งออกเมตริกโดยใช้ Prometheus เมตริกเหล่านั้นจะพร้อมใช้งานใน localhost:9464 สำหรับเซิร์ฟเวอร์และ localhost:9465 สำหรับไคลเอ็นต์

วิธีดูเมตริกไคลเอ็นต์

curl localhost:9465/metrics

ผลลัพธ์จะมีรูปแบบดังนี้

# HELP grpc_client_attempt_duration_seconds Time taken to complete a client call attempt
# TYPE grpc_client_attempt_duration_seconds histogram
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.002"} 0
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.004"} 14
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.005"} 29
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.1"} 33
grpc_client_attempt_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_attempt_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_attempt_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.46512665300000006
# 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_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
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-java",otel_scope_version="1.66.0"} 442.0
# HELP grpc_client_attempt_sent_total_compressed_message_size_bytes Compressed message bytes sent per client call attempt
# TYPE grpc_client_attempt_sent_total_compressed_message_size_bytes histogram
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_attempt_sent_total_compressed_message_size_bytes_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="1024.0"} 34
grpc_client_attempt_sent_total_compressed_message_size_bytes_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 238.0
# HELP grpc_client_attempt_started_total Number of client call attempts started
# TYPE grpc_client_attempt_started_total counter
grpc_client_attempt_started_total{grpc_method="helloworld.Greeter/SayHello",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34.0
# HELP grpc_client_call_duration_seconds Time taken by gRPC to complete an RPC from application's perspective
# TYPE grpc_client_call_duration_seconds histogram
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.0"} 0
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="0.003"} 2
grpc_client_call_duration_seconds_bucket{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0",le="+Inf"} 34
grpc_client_call_duration_seconds_count{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 34
grpc_client_call_duration_seconds_sum{grpc_method="helloworld.Greeter/SayHello",grpc_status="OK",grpc_target="dns:///localhost:50051",otel_scope_name="grpc-java",otel_scope_version="1.66.0"} 0.512708707
# TYPE target_info gauge
target_info{service_name="unknown_service:java",telemetry_sdk_language="java",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.40.0"} 1

ในทำนองเดียวกัน สำหรับเมตริกฝั่งเซิร์ฟเวอร์ ให้ทำดังนี้

curl localhost:9464/metrics

5. การดูเมตริกใน Prometheus

ในที่นี้ เราจะตั้งค่าอินสแตนซ์ Prometheus ที่จะคัดลอกไคลเอ็นต์และเซิร์ฟเวอร์ตัวอย่าง gRPC ที่ส่งออกเมตริกโดยใช้ Prometheus

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

tar xvfz prometheus-*.tar.gz
cd prometheus-*

สร้างไฟล์การกำหนดค่า Prometheus ด้วยข้อมูลต่อไปนี้

cat > grpc_otel_java_prometheus.yml <<EOF
scrape_configs:
  - job_name: "prometheus"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: "grpc-otel-java"
    scrape_interval: 5s
    static_configs:
      - targets: ["localhost:9464", "localhost:9465"]
EOF

เริ่ม Prometheus ด้วยการกำหนดค่าใหม่ -

./prometheus --config.file=grpc_otel_java_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. (ไม่บังคับ) แบบฝึกหัดสำหรับผู้ใช้

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

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