Observability Pt. 2: Tracing with OpenTelemetry and Jaeger
จากบทความตอนก่อนหน้านี้ในพาร์ท 1 เราได้ทำความรู้จักกับหลักการของเรื่อง Observability ว่ามีอยู่ 3 เรื่องประกอบกันไปแล้ว คือ Logging, Metrics และ Tracing ที่แต่ละเรื่องจะเป็นการมองเข้าไปที่ระบบของเราในแง่มุมที่แตกต่างกันออกไป และถ้าระบบเรามีทั้ง 3 เรื่องนี้ที่ครบถ้วน ก็จะถือว่าระบบของเรามีความสุดยอดที่เราจะสามารถเห็นการทำงานทุกอย่างภายในระบบอย่างชัดเจน ครอบคลุมทุกแง่มุม ทุกขั้นตอน ง่ายแก่การนำข้อมูลไปใช้งานจริง ไม่ว่าจะเป็นการปรับปรุงประสิทธิภาพของระบบของเราได้ดียิ่งๆ ขึ้นไป หรือสามารถรู้จุดที่ผิดปกติได้อย่างรวดเร็ว เป็นต้น
ในพาร์ทนี้ เรามาลงลึกกันมากขึ้นในเรื่องของ Tracing กันดีกว่า ถึงแม้คำนี้จะมาเป็นอันดับ 3 เวลาเราพูดว่า Observability ประกอบด้วยอะไรบ้าง แต่ผมขอยกเรื่อง Tracing มาเล่าก่อน เพราะผมคิดว่าเป็นเรื่องที่น่าสนใจและเข้าใจไม่ค่อยยากเท่าไหร่ใน 3 เรื่องนี้ และจะมีโปรแกรมจริงให้ทดลองเล่นหรือทำตามกันดูด้วยครับ
และนี่คือหัวข้อทั้งหมดที่เราจะพูดถึงกันในบทความนี้ครับ
- ทบทวนคอนเซ็ปต์เรื่อง Tracing
- ทำความรู้จักกับ OpenTelemetry
- ทำความรู้จักกับ Jaeger และ Architecture ของ Jaeger
- มาทดลองเล่นจริงกันดีกว่า
สำหรับใครที่คุ้นเคยกับบางหัวข้อแล้ว จะคลิกลิงก์เพื่อกระโดดไป Section ที่ต้องการเลยก็ไม่ว่ากัน แต่สำหรับใครที่ต้องการทวนเนื้อหากันตั้งแต่ต้น เลื่อนเพื่ออ่านต่อได้เลยครับ
ทบทวนเรื่อง Tracing กันนิดนึง
ในโลกของ Microservice หรือ Distributed Application สิ่งที่แตกต่างจาก Application ที่เป็นแบบ Monolithic ในยุคก่อนๆ ก็คือ Application ถูกแบ่งออกเป็นหลายๆ Microservice ที่เป็น Process แยกกัน มี Environment ในการทำงานเป็นของตัวเอง รายการหรือ Request หนึ่งๆ ที่เข้ามายัง Application ของเรา การทำงานตั้งแต่ต้นจนจบอาจจะผ่าน Microservice และ Component อื่นๆ เช่น Database หรือ Message Queue System อีกจำนวนมาก การที่เราสามารถเห็นข้อมูลของ Request หนึ่งๆ ได้แบบ End-to-end หรือตั้งแต่ต้นจนจบนั้นเป็นสิ่งจำเป็นมาก ไม่ว่าจะสำหรับการ Investigate ปัญหาต่างๆ เช่น มีรายการนึงวิ่งเข้ามาแล้วช้า เราจะรู้ได้อย่างไรว่าช้าใน Service ตัวไหนหรือที่ Component ไหน หรือกรณีเกิดปัญหาใดๆ ขึ้น เราจะรู้ได้อย่างไรว่าปัญหาเกิดที่จุดไหน เพื่อที่จะได้เข้าไปดูหรือแก้ไขได้ตรงจุด หรือเราจะรู้ได้อย่างไรว่าตรงจุดไหนของ Application เราที่ยังสามารถปรับปรุงประสิทธิภาพในการทำงานได้อีก
ความสามารถในการเห็นข้อมูลของแต่ละ Request แบบ End-to-end นี้คือสิ่งที่เรียกว่า Tracing และพอเราต้องสามารถเห็นข้อมูลของระบบใหม่ๆ มีการทำงานแบบ Distributed เราเลยเรียกว่า Distributed Tracing นั่นเอง
Software ที่เก็บข้อมูล Tracing ส่วนใหญ่แล้วมักจะเก็บข้อมูล Trace ในลักษณะ Waterfall ก็คือวาดการทำงานแต่ละขั้นตอนหรือกิจกรรม (Activity) ต่อๆ กัน ว่าแต่ละกิจกรรมมีจุดเริ่มต้นและจุดสิ้นสุดตรงไหน ใช้เวลาในแต่ละกิจกรรมเท่าไหร่ โดยถ้าการทำงานในกิจกรรมไหนมีกิจกรรมย่อยๆ ของมันอีก ก็จะแตกเวลาที่ใช้ในแต่ละกิจกรรมย่อยๆ อยู่ข้างใต้เวลาของกิจกรรมใหญ่ลงไปอีกที ถ้ามีกิจกรรมย่อยๆ อีก ก็แตกลงไปเรื่อยๆ ตามรูป
แต่ละกิจกรรมที่เห็นเป็นเส้นๆ นี้ ศัพท์ทาง Tracing จะเรียกว่า Span ครับ โดยนอกจากแต่ละ Span จะมีชื่อและข้อมูลเกี่ยวกับเวลาแล้ว ยังเก็บข้อมูล Log Message และข้อมูลประกอบอื่นๆ (ที่เราเรียกว่า Metadata หรือ Attribute) ตามต้องการได้อีกด้วย
Distributed Tracing Architecture
สำหรับ Architecture ของการทำ Tracing ไม่ว่าจะใช้ Software ตัวไหน หลักการแบบ High Level จะเหมือนๆ กันครับ โดยจะแบ่งเป็น 4 ส่วน คือ Agent, Collector, Database, และ UI
- Agent เป็นส่วนที่ให้ Application ใช้ในการส่งข้อมูล Tracing ออกมาจาก Application ที่เรียกว่าการทำ Instrumentation จะมีทั้งลักษณะที่เป็นการนำ Agent ไปจับที่ตัว Application หรือลักษณะที่เป็น SDK หรือ Client Library ที่ต้องเขียนโค้ดเพื่อส่งข้อมูล Tracing ออกมา ซึ่งจะมีอธิบายถึงในหัวข้อถัดไป
- Collector เป็นตัวที่รับข้อมูล Trace จาก Agent แล้วส่งไปบันทึกใน Database อีกทีนึง
- Database ตรงไปตรงมาครับ เอาไว้เก็บข้อมูล Tracing นั่นเอง โดย Database ของ Software Tracing มักจะเป็น Database ที่สามารถค้นหาข้อมูลได้รวดเร็ว เช่น Elasticsearch
- Web UI ใช้ในการ Query และค้นหาข้อมูล
Automatic Instrumentation & Manual Instrumentation
จากที่กล่าวไว้ในหัวข้อก่อนหน้านี้ว่าในการทำ Tracing เราต้องทำให้ Application ส่งข้อมูลออกมายังระบบ Back-end ที่เก็บข้อมูล Trace การทำสิ่งนี้เราเรียกว่าการทำ Instrumentation ซึ่งมีอยู่ 2 วิธีการใหญ่ๆ คือวิธีที่ไม่ต้องแก้โค้ด (Automatic Instrumentation) กับวิธีที่ต้องแก้โค้ด (Manual Instrumentation) นั่นเอง
Automatic Instrumentation มักจะทำด้วยการนำโปรแกรม Agent ไปจับที่ตัวโปรแกรมของเราที่ต้องการทำ Tracing โดยขั้นตอนอาจจะแตกต่างกันไปตามแต่ละ Software บางตัว ก็อาจจะต้องแก้ Script ในการ Start Application นิดหน่อย เพื่อฝังตัว Agent เข้าไปใน Process ของ Application แต่บาง Software ที่ Agent ฉลาดหน่อย ก็อาจจะลงไว้เฉยๆ ในเครื่อง ไม่ต้องแก้ไข Script ใดๆ แล้ว แล้วตัว Agent จะ Detect ได้เองว่ามี Application Process อะไรรันอยู่ แล้วเข้าไป Monitor ให้เอง
สำหรับ Application ที่รันใน Environment ที่เป็น Container ตัว Agent เหล่านี้มักจะสามารถติดตั้งเป็น Sidecar เพื่อให้ Monitor Container ของ Application ได้โดยอัตโนมัติ
ส่วนวิธี Manual Instrumentation เราจะต้องแก้ไขโค้ด ด้วยการแทรกโค้ดเข้าไปตรงจุดที่เราต้องการส่งข้อมูล Tracing ออกมา โดยใช้ Client Library ที่ตัว Tracing Software มีให้ใช้
เราสามารถใช้งานทั้ง Automatic และ Manual Instrumentation ร่วมกันก็ได้ กล่าวคือโดยปกติ Auto Instrumentation จะมีจุดที่ส่งข้อมูล Tracing ออกมาอยู่ ซึ่งส่วนใหญ่ก็จะเป็นจุดที่เป็นข้อต่อระหว่าง Tier หรือ Component เช่น ระหว่าง Tier ที่รับ HTTP Request กับ Tier ของ Business Logic หรือข้อต่อกับ Component ภายนอก เช่น จุดที่มีการเรียก Database หรือมีการเรียก API หรือ Microservice อื่นๆ เป็นต้น ถ้าหากเราต้องการ Trace ในระดับที่ลึกลงไปอีก เช่น อาจจะเป็น Code Block หนึ่งๆ เท่านั้น หรือเป็นการเรียกกันระหว่าง Component ใน Tier เดียวกัน ซึ่ง Automation Instrumentation อาจจะไม่มีข้อมูลส่วนนี้ให้ เราก็สามารถใช้วิธี Manual Instrumentation เพื่อแทรกโค้ดเพิ่มเติมตรงจุดที่เราต้องการให้ส่งข้อมูล Tracing ออกมาได้
Context Propagation
หัวใจของ Tracing คือเราต้องการเห็นข้อมูลของแต่ละ Request ที่เป็นสายแบบ End-to-end ตั้งแต่ต้นจนจบ ซึ่งแน่นอนว่าจะต้องวิ่งผ่านหลายๆ Component ดังนั้นข้อมูล Trace ที่แต่ละ Component ส่งออกมาจะต้องบอกว่าเป็นข้อมูลของ Request เดียวกันหรือเปล่า และแต่ละขั้นตอนหรือ Span นั้นอยู่ตรงไหนใน Trace ของ Request นั้นๆ เวลาที่แต่ละ Component หรือ Service มีการเรียกต่อกัน ก็จะต้องส่งข้อมูลที่สามารถระบุแต่ละ Request ได้ต่อๆ กันไปด้วย ข้อมูลนี้ ประกอบกับข้อมูลอื่นๆ ที่จำเป็นเรียกว่า Context การที่เราส่งข้อมูล Context ต่อๆ กันไป ก็เลยเรียกว่า Context Propagation
ในสมัยก่อน Software ที่ทำเรื่อง Tracing แต่ละตัว ก็ออกแบบ Format ของข้อมูล Trace Context เป็นของตัวเองที่แตกต่างกันไป เช่น Jaeger, B3, W3C เป็นต้น จนในตอนหลังได้มีความพยายามกำหนดให้เป็นมาตรฐานเดียวกัน ซึ่งจะได้กล่าวถึงต่อไป
OpenTelemetry
ทีนี้พอความต้องการในการทำ Tracing มีมากขึ้น แน่นอนว่าจะมี Vendor หลายเจ้าสร้าง Software หรือ Solution ที่ทำเรื่อง Distributed Tracing ออกมาเป็นจำนวนมาก แต่ละตัวก็จะมีฟีเจอร์หรือลูกเล่นที่แตกต่างกันออกไป มีทั้งตัวที่ใช้งานได้ฟรีและเสียเงิน ผมจะลองยกตัวอย่างชื่อของ Software ในกลุ่มนี้ ถ้าผู้อ่านสนใจหาข้อมูลเพิ่มเติม ลองไปค้นหากูเกิลเพิ่มเติมได้ไม่ยากครับ
พอมี Software หลายตัวที่ทำเรื่องแบบเดียวกัน ปัญหาที่ตามมาหนีไม่พ้นความเข้ากันได้ครับ เนื่องจากหลักการของการทำ Tracing อยู่บนหลักการที่เป็น Common คือต้องมี Agent หรือ Library ที่เอาไปฝังไว้ กับ Application ต้องมี Collector ที่ใช้รับข้อมูล Tracing และต้องมี Database ที่ใช้เก็บข้อมูล เจ้าของผลิตภัณฑ์แต่ละตัวก็ต้องสร้างสิ่งเหล่านี้ขึ้นมาเองและต้องกำหนดวิธีที่ Component เหล่านี้คุยกันเอง ไม่ว่าจะเป็นตัว API สำหรับเขียนโปรแกรม หรือรูปแบบข้อมูลที่ใช้รับส่งกันระหว่าง Agent กับ Collector หรือรูปแบบของ Context Propagation ที่ต้องส่งต่อให้กันระหว่างแต่ละ Service เพื่อให้ข้อมูล Trace เชื่อมต่อกัน เป็นต้น ซึ่งสร้างความยุ่งยากให้กับทั้งฝ่ายของ Software Vendor รวมถึงผู้ใช้งานเอง
ในฝั่งของผู้ใช้งาน แน่นอนว่าปัญหาคือเรื่องของ Vendor Lock-in นั่นก็คือถ้าเราใช้ Software ตัวไหนไปแล้ว ใช้ Agent ตัวไหน หรือเขียนโค้ดโดยใช้ Library ของใครคนใดคนหนึ่งไปแล้ว ถ้าวันนึง Software ตัวนั้นไม่ตอบโจทย์เราอีกต่อไป การที่เราจะเปลี่ยนไปใช้ Product ตัวอื่นก็จะทำได้อยาก เพราะ Application ของเราใช้ API หรือ Library ที่ใช้งานได้เฉพาะกับ Software ตัวนั้นๆ เท่านั้น
ในฝั่งของ Software Vendor ก็เช่นกัน แต่ละยี่ห้อต้องมาพัฒนาตัว Agent หรือ Collector ของตัวเองใหม่ทั้งหมด แทนที่จะทุ่มเทใช้เวลาและทรัพยากร เพื่อแข่งขันกันในเรื่องของความสามารถหลักที่ช่วยสนับสนุนผู้ใช้งานมากกว่า เช่น ทำให้หน้าจอ UI ใช้งานง่าย สามารถค้นหาข้อมูลได้ไว ไม่กระทบกับประสิทธิภาพของระบบที่ถูก Monitor เป็นต้น
จากปัญหาเรื่องความเข้ากันได้ของ Software สำหรับทำเรื่อง Tracing นี่เอง ทำให้บริษัทต่างๆ ที่ทำ Software เกี่ยวกับ Tracing รวมตัวกันสร้างเป็น Standard ขึ้นมา โดย Standard ตัวนี้มีชื่อเรียกว่า OpenTelemetry (ก่อนหน้านั้นมี Standard อีกตัวหนึ่งชื่อ OpenTracing แต่ภายหลังได้ถูกรวมเข้ามาใน OpenTelemetry)
โดยสิ่งที่ OpenTelemetry ต้องการจะทำเป็น Standard นั้นจะประกอบไปด้วย Observability ทั้ง 3 เรื่อง คือ Logging, Metrics, และ Tracing โดยมีทั้งส่วนที่เป็น Client Library หรือ Agent สำหรับทำ Instrumentation ที่ Application และตัว Collector ที่รับข้อมูลจาก Application ส่งต่อไปยัง Backend ที่เก็บข้อมูล Observability ทั้ง Logging, Metrics, และ Tracing
ตัว Collector ก็จะประกอบไปด้วยส่วนที่เป็น Receiver สำหรับรับข้อมูล Tracing จาก Application ซึ่งออกแบบเป็นแบบ Plugin ที่สามารถพัฒนาเพิ่มเติมแล้ว Plug เข้าไปในตัว Collector ได้ นอกจากจะสามารถรับข้อมูลในรูปแบบของ OpenTelemetry Protocol ที่ใช้งานกับ OpenTelemetry Agent หรือ SDK แล้ว ก็ยังสามารถรับข้อมูลในรูปแบบอื่นๆ เช่น Jaeger Format จาก Jaeger Client ได้ด้วย เป็นต้น
ในส่วนที่ใช้ส่งข้อมูลออกไปหา Back-end ต่างๆ นั้น จะเรียกว่า Exporter ซึ่งก็ Support หลายๆ รูปแบบและสามารถเขียนเป็น Plugin เพิ่มเข้าไปได้อีกเช่นกัน
Jaeger คืออะไร
จากที่ได้กล่าวมาตอนต้นแล้วว่า Software ที่ทำเรื่อง Tracing นั้นมีอยู่ด้วยกันหลายตัว Jaeger ก็เป็นหนึ่งในนั้น และเป็นตัวที่เราจะนำมาทดลองเล่นกันในบทความนี้ครับ โดย Jaeger นั้นเป็น Open Source Software หนึ่งใน Project ภายใต้ CNCF หรือ Cloud Native Computing Foundation ที่ดูแลโครงการมากมายที่เกี่ยวข้องกับการพัฒนา Application แบบ Cloud Native
Architecture ของ Jaeger
สำหรับภาพ Architecture ของตัว Jaeger นั้นจะเหมือนๆ กับภาพ High Level ที่ผมอธิบายไว้ก่อนหน้านี้ คือจะมี Agent ที่ไปอยู่กับ Application และส่งข้อมูลมาให้ Jaeger Collector ที่จะรับข้อมูล Tracing จาก Agent ก่อนที่จะบันทึกลง Database ของตัวเอง
ใน Version หลังๆ ตัว Jaeger ได้ทำให้ตัวเองเข้ากับ Standard ของ OpenTelemetry โดยตัว Jaeger Collector จะสามารถรับข้อมูล Trace จาก OpenTelemetry Agent โดยตรงได้เลย ดังนั้นภาพที่เราจะเล่นกันในบทความนี้จะไม่ได้ใช้ Jaeger Agent แต่เราจะใช้ OpenTelemetry Agent แทน และส่งข้อมูล Trace ไปยัง Jaeger Collector โดยตรง
มาทดลองเล่นจริงกันดีกว่า
ในบทความนี้ เราจะทำ Tracing กับ Java Application ตัวหนึ่ง โดยใช้ OpenTelemetry ในแบบ Automatic Instrumentation และใช้ Jaeger เป็น Tracing Backend ครับ
สิ่งที่ต้องเตรียม
- JDK17 ดาวน์โหลดได้จากเว็บ Adoptium
- Docker Desktop สามารถดาวน์โหลดได้จากเว็บ Docker
- Git Client (Optional) เพื่อความสะดวกในการดาวน์โหลด Source Code ที่ใช้ในบทความนี้ แต่ถ้าไม่มีและไม่ต้องการลง ก็สามารถดาวน์โหลดแบบ Manual ได้เช่นกันครับ
โดยโปรแกรมที่เราจะเอามาทดลอง จะเป็น Stand-alone Web Application มีการเรียกใช้งาน Database แต่ไม่มีการเรียก Service หรือ API อื่นๆ ทั้งนี้ขั้นตอนก็เหมือนกันครับ ไม่ว่าจะมีการเรียกไปหา Service หรือ API อื่นๆ หรือไม่ก็ตาม
ก่อนอื่นใครที่ยังไม่ได้เปิดโปรแกรม Docker Desktop ในเครื่อง ให้เปิดขึ้นมาก่อน
1. ลอง Start Jaeger ขึ้นมา โดยใช้คำสั่งต่อไปนี้
docker run --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-e COLLECTOR_OTLP_ENABLED=true \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.40
โดย jaegertracing/all-in-one จะเป็น Image ของ Jaeger ที่มีทุก Component ทั้ง Agent, Collector, และ Web UI รวมอยู่ด้วยกัน ทำให้สะดวกกับการนำมาลองเล่นครับ
จะเห็นว่ามี Port เยอะแยะไปหมด เพราะเป็น Port ที่ Component ต่างๆ ของ Jaeger ใช้งานนั่นเอง อันที่สำคัญกับการทดลองในบทความนี้จะเป็น Port 16686 ที่เป็น Port ของ Web UI กับ Port 4317 ที่เป็น Port ของ Jaeger Collector ที่ใช้ในการรับข้อมูล Tracing จากตัว Agent ในรูปแบบของ OpenTelemetry (OTLP) ผ่าน Protocol gRPC
สำหรับข้อมูลเพิ่มเติมของ Port ต่างๆ ว่าใช้ทำอะไรบ้าง สามารถดูได้จากเอกสารนี้ครับ
หลังจากที่เรา Start Jaeger ขึ้นมาแล้ว ก็ลองเข้าหน้าจอ Web UI ดูครับ โดยเปิด Web Browser ไปที่ http://localhost:16686 ถ้าทุกอย่างเรียบร้อย เราควรจะเห็นหน้าจอแบบนี้ครับ
2. พอลง Jaeger เสร็จ ก็มาลง Application ที่เราจะลองเล่นกันบ้างครับ โดยผมได้วาง Source Code ของ Application ไว้บน GitHub สามารถ Clone ลงมาโดยใช้ Git Command Line ได้เลยครับ
git clone https://github.com/tinnapat/spring-petclinic-opentelemetry.git
หรือถ้าไม่มี Git Client สามารถดาวน์โหลดได้จากลิงก์นี้เลย
กรณีดาวน์โหลดจากลิงก์ตรงๆ เมื่อดาวน์โหลดมาเสร็จแล้วก็ให้ Extract ไฟล์ออกมาก่อนนะครับ
เมื่อเอา Source Code มาเรียบร้อย ให้เข้าไปใน Directory spring-petclinic-opentelemetry และสังเกตไฟล์นึงที่ชื่อ opentelemetry-javaagent.jar ไฟล์นี้คือ OpenTelemetry Agent นั่นเอง ซึ่งเราต้องสั่งให้มันเข้ามาทำงานใน JVM ที่เราใช้ในการรันโปรแกรม ผมดาวน์โหลดไฟล์นี้มาไว้ให้ก่อนแล้ว ต้นฉบับจะมาจากลิงก์นี้ครับ สามารถเข้าไปดูข้อมูลเพิ่มเติมและประวัติของเวอร์ชันได้เลย
3. Build Source Code ของเราให้เป็น Executable JAR File โดยใช้คำสั่งดังนี้ อย่าลืมเข้าไปใน Directory spring-petclinic-opentelemetry ก่อนด้วยนะครับ
cd spring-petclinic-opentelemetry
./mvnw package
ตอนรันครั้งแรก อาจจะนานสักหน่อย เนื่องจากต้องรอให้ Maven ที่เป็น Build Tool ทำการดาวน์โหลด Dependencies ต่างๆ มาให้ครบ
เมื่อสำเร็จแล้ว เราควรจะต้องได้ไฟล์ชื่อ spring-petclinic-<version>.jar ใน Directory /target ซึ่งก็คือโปรแกรมที่ Build เสร็จแล้ว <version> ในส่วนของชื่อไฟล์ก็คือ Version ของโปรแกรม Petclinic ครับ ซึ่งในอนาคตผมอาจจะมีการปรับปรุงเป็น Version ใหม่ ทำให้ชื่อ Version และชื่อไฟล์เปลี่ยนไป โดย ณ วันที่ผมเขียนบทความนี้ ชื่อไฟล์นี้จะเป็น spring-petclinic-3.0.0-SNAPSHOT.jar
4. รันโปรแกรม spring-clinic ขึ้นมา โดยให้ Start OpenTelemetry Agent ขึ้นมาใน JVM เดียวกันด้วย ด้วยคำสั่งดังนี้ (ทั้งหมดอยู่ในบรรทัดเดียวกัน)
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=spring-petclinic \
-Dotel.metrics.exporter=none \
-jar target/*.jar
มาดูรายละเอียดกันว่า Parameter ที่เราระบุเพิ่มเติมเข้าไปตอน Start Application ของเรามีอะไรบ้าง
javaagent:opentelemetry-javaagent.jar
เป็นการระบุชื่อไฟล์ Java Agent ที่เราต้องการ Start ขึ้นมาใน JVM ในที่นี้ก็คือไฟล์ของ OpenTelemetry Agent นั่นเอง
otel.service.name=spring-petclinic
เป็นการตั้งชื่อให้ Application ของเรา โดยชื่อนี้จะไปปรากฏใน Jaeger UI เวลาเราทำการค้นหาครับ
otel.metrics.exporter=none
จากที่ได้เล่าไปตอนต้นแล้วว่า OpenTelemetry นั้นซัพพอร์ตการส่งข้อมูล Observability ทั้ง Logging, Metrics, และ Tracing โดย Parameter ตัวนี้เป็นการปิดการส่งข้อมูล Metrics เนื่องจาก Jaeger ไม่ได้ซัพพอร์ตข้อมูล Metrics และเราจะยังไม่พูดถึงเรื่อง Metrics ในบทความนี้
ทีนี้จะเห็นว่าเราไม่ได้ระบุ Parameter ใดๆ เกี่ยวกับเรื่องของ Tracing เลย ไม่ว่าจะเป็น URL/Port ของ Jaeger หรือ Protocol ที่ใช้คุยกัน นั่นเป็นเพราะว่าถ้าไม่มีการระบุทับเข้าไปโดย Default แล้ว ตัว OpenTelemetry Agent จะส่งข้อมูล Tracing ออกมาด้วย OpenTelemetry Protocol ไปยัง OpenTelemetry Collector ที่ URL http://localhost:4317
สำหรับ Parameter ทั้งหมดของ OpenTelemetry Agent สามารถดูรายละเอียดได้ที่นี่ครับ
ถ้าทุกอย่างเรียบร้อย หลังจากที่ Application Start เสร็จ ควรจะได้หน้าจอดังนี้ครับ
5. มาทดลองเล่น App กัน เข้าไปที่ URL http://localhost:8080/ หน้าจอควรออกมาเป็นแบบนี้
จากนั้นให้เลือกแท็บ FIND OWNERS ด้านบน และกดปุ่ม Find Owner ด้านล่างครับ
จะได้หน้าจอประมาณนี้
เปิด Jaeger UI ที่ http://localhost:16686/ แล้วลองกด Find Traces ดูครับ
ลองคลิกข้อมูลที่มีชื่อตรงหัวข้อว่า /owners ดูครับ (ไม่ใช่ /owners/find นะครับ)
จะเห็นว่านี่คือข้อมูล Trace ของ Request หนึ่งที่วิ่งผ่าน Component ต่างๆ ของ Application ลงไปเป็นชั้นๆ ตั้งแต่เข้ามาแตะที่ Application Server จนเข้ามาที่ Application ออกไปถึง Database และกลับมาแสดงผลที่หน้าจอ โดยแต่ละขั้นตอนจะมีเวลากำกับว่าเริ่มต้นเมื่อไหร่ สิ้นสุดเมื่อไหร่ รวมถึงเวลาที่ใช้ในแต่ละขั้นตอน
ถ้าผู้อ่านยังไม่ลืม สิ่งนี้ในภาษาของทาง Tracing เรียกว่า Span
เมื่อเราคลิกที่แต่ละ Span จะมีข้อมูลประกอบแสดงให้เห็น หรือก็คือ Attribute ของ Span นั่นเอง ทั้งนี้ Attribute จะเป็นคำเรียกของ OpenTelemetry ส่วน Jaeger จะเรียกสิ่งนี้ว่า Tag ครับ ซึ่งจะมีทั้ง Tag ที่เป็น Common แสดงเหมือนกันในทุกๆ Span กับ Tag ที่เป็นข้อมูลเฉพาะเจาะจงว่าเป็นการทำงานที่ Component ไหน
เดี๋ยวเรามาลองค่อยๆ ดูกันว่าข้อมูลใน Span แต่ละชั้น เป็นข้อมูลที่ออกมาจาก Component ตัวไหนในโค้ดของเรา และมีข้อมูลที่แตกต่างกันออกไปอย่างไรบ้าง
1. /owners ถ้าเปิดดู Tag ที่ชื่อ otel.library.name จะเห็นว่ามาจาก io.opentelemetry.tomcat-10.0 ก็คือ Tomcat ที่เป็น Application Server เป็น Layer ก่อนที่จะเข้ามาถึงตัว Application โดยใน Span นี้จะเห็นข้อมูลเกี่ยวกับ HTTP Request ไม่ว่าจะเป็น URL, Host, Port ที่เรียก และ HTTP Status Code ที่ตอบกลับไป เป็นต้น
2. OwnerController.processFindForm มาจาก io.opentelemetry.spring-webmvc-6.0 หรือ Spring Web MVC ที่เป็น Library คือรายการนี้เข้ามาถึง Application Layer ในส่วนที่เป็น Spring Controller แล้ว
ตรงส่วนนี้เป็น Layer ของ Business Logic ภายในโค้ดที่เราเขียนขึ้นเอง ข้อมูลใน Tag จะเป็นข้อมูลทั่วไปของ Process เช่น Thread ID หรือชื่อ Thread เป็นต้น เท่านั้น
3. OwnerRepository.findByLastName มาจาก io.opentelemetry.spring-data-1.8 คือ Spring Data ซึ่งเป็น Library เช่นกัน หรือก็คือส่วนที่เป็น Repository Layer ของ Application
เช่นเดียวกับในส่วนของ Controller ที่เป็นโค้ดที่เราเขียนขึ้นมาเอง ข้อมูลใน Tag จะมีเฉพาะข้อมูล Common เช่นกัน แต่จะข้อมูลเพิ่มเติมว่าเป็นการทำงานใน Method ไหนของ Class ไหน
4. SELECT มาจาก io.opentelemetry.jdbc เป็น JDBC Layer ที่ Application ใช้ในการติดต่อ Database ตรงส่วนนี้จะเห็นข้อมูลเลยว่าเราส่ง Query อะไรเข้าไปที่ Database รวมถึงข้อมูลอื่นๆ เช่น Database ชื่ออะไร เราใช้ User Login อะไรในการติดต่อ Database เป็นต้น
สำหรับเนื้อหาในตอนนี้ผมขอจบไว้ตรงนี้นะครับ เพื่อไม่ให้เยิ่นเย้อจนเกินไป แต่ยังมีเรื่องของ Tracing ที่อยากเล่าต่อในเรื่องของการทำ Instrumentation แบบ Manual ขอยกยอดไปเล่าในตอนหน้าแทนนะครับ
ถ้าเพื่อนๆ ผู้อ่านลองทำตามขั้นตอนในบทความนี้แล้ว พบปัญหาประการใด สามารถคอมเม้นต์ทิ้งไว้ได้ หรือจะเปิดเป็น Issue ใน GitHub Reposority ของ Project นี้ก็ได้นะครับ
แล้วพบกันใหม่ในตอนหน้าครับ
Happy New Year และ Happy Tracing
แหล่งข้อมูลอ้างอิง
- OpenTelemetry เว็บไซต์ทางการของ OpenTelemetry
- Distributed Tracing: A Complete Guide (lightstep.com)
- What is Context Propagation in Distributed Tracing? | SigNoz
- GitHub — open-telemetry/opentelemetry-java-instrumentation
- Jaeger: open source, end-to-end distributed tracing (jaegertracing.io)
- Deployment — Jaeger documentation (jaegertracing.io)
สำหรับใครที่สนใจเรื่องราวดีๆ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ จากชาว KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech