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

1. บทนำ

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

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

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

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

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

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

  • git
  • curl
  • JDK เวอร์ชัน 8 ขึ้นไป

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

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

รับโค้ด

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

grpc-codelabs

ซอร์สโค้ดโครงสร้างสำหรับ Codelab นี้อยู่ในไดเรกทอรี github นี้ directory หากไม่ต้องการเขียนโค้ดด้วยตนเอง คุณสามารถดูซอร์สโค้ดที่เสร็จสมบูรณ์แล้วได้ในไดเรกทอรี completed

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

git clone -b v1 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 อย่างง่ายที่เราจะติดตั้งปลั๊กอิน gRPC OpenTelemetry

ขั้นตอนแรกคือการลงทะเบียนปลั๊กอิน 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 ใหม่

// 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

ใช้ GrpcOpenTelemetry API เพื่อตั้งค่า OpenTelemetry SDK ที่ใช้เครื่องมือส่งออกเมตริก Prometheus

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

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

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

ปิด OpenTelemetry SDK

การปิดใช้งานต้องเกิดขึ้นภายใน 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();

ปิด OpenTelemetry SDK

หลังจากปิดแชแนล 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. (ไม่บังคับ) แบบฝึกหัดสำหรับผู้ใช้

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

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