4 คะแนน โดย GN⁺ 2025-01-11 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • คำขอจากลูกค้าให้ รองรับ OpenTelemetry ทำให้เห็นว่า observability ที่อิง Prometheus·Jaeger·OpenTracing เดิมนั้นไม่เหมือนกับความต้องการด้าน metrics·logs·traces ของ OTel เสียทีเดียว
  • logs และ metrics ย้ายได้ค่อนข้างง่ายด้วยการเพิ่ม appender และการตั้งค่า แต่ distributed tracing ยากกว่ามาก เพราะ Spring และ Akka ต้องแชร์ trace context เดียวกันภายใน JVM เดียวกัน
  • OTel และ Lightbend Telemetry/OpenTracing ใช้ Tracer API และการติดตั้ง SpanContext คนละแบบ ทำให้ทรานแซกชันเดียวกันถูกแยกออกเป็นคนละ trace
  • วิธีแก้คือ inject context ของ OTel ลงใน Java Map, extract ออกมาเป็น Jaeger SpanContext แล้วค่อย activate แบบแมนนวลด้วย GlobalExtendedTracer.get().local().activateContext() ของ Lightbend
  • OTel ช่วยเรื่องการทำมาตรฐาน observability ได้ แต่เมื่อผสมกับ instrumentation เดิมที่อิง Akka·Jaeger อาจจำเป็นต้องมี การเชื่อม context อย่างชัดเจน

สัญญาณ observability ที่ OTel พยายามรวมเข้าด้วยกัน

  • OpenTelemetry(OTel) เป็นทั้งเฟรมเวิร์กและทูลคิทด้าน observability ที่พยายามทำมาตรฐานเครื่องมือซึ่งเดิมแยกกันอยู่ ให้มาอยู่ในสัญญาณ 3 แบบคือ metrics, logs และ traces
  • ชุดเครื่องมือเดิมถูกแยกตามหน้าที่
    • Prometheus: metrics
    • ตัวรวบรวมศูนย์กลางอย่าง Logstash และ Elasticsearch: logs
    • OpenTracing: distributed tracing
  • OTel ไม่ได้ให้แค่สเปก แต่ยังมีคอมโพเนนต์หลักด้วย
    • OpenTelemetry Protocol(OTLP): โปรโตคอลที่แอปพลิเคชันใช้รายงานข้อมูล telemetry
    • OpenTelemetry Collector: คอมโพเนนต์สำหรับรับ ประมวลผล และส่งออกข้อมูล telemetry แบบไม่ผูกกับ vendor
    • SDK สำหรับมากกว่า 10 ภาษา: ใช้ทำ OTLP และ telemetry export
  • แอปพลิเคชันสามารถส่งสัญญาณไปยัง OTel Collector ผ่าน auto-instrumentation, API และ language SDK ได้ และฝั่ง infrastructure ก็สามารถส่งสัญญาณเข้า Collector ได้เช่นกัน

การรองรับเดิมของ IPF และคำขอจากลูกค้า

  • IPF มีการเชื่อมต่อกับ ELK Stack, ระบบ monitoring และเอกสารด้าน observability อยู่แล้ว และลูกค้าก็ใช้งานในสภาพแวดล้อม production มาตลอด
  • ด้วยลักษณะของเฟรมเวิร์ก จึงไม่อาจรู้ได้ว่าแอปพลิเคชันที่ใช้ไลบรารีจะถูกนำไป deploy ในสภาพแวดล้อมแบบใด จึงต้องไม่บังคับ vendor รายใดรายหนึ่ง แต่ให้ abstraction และค่าเริ่มต้นที่ override ได้แทน
  • คำแนะนำพื้นฐานเดิมคือส่ง logs ไปยังตัวรวบรวมอย่าง Elasticsearch หรือ LogScale และเปิดใช้/รองรับ Prometheus ซึ่งแทบกลายเป็นมาตรฐานสำหรับ metrics ไปแล้ว
  • ลูกค้าร้องขอ tracing บน OTel พร้อมกันเกือบสามเคส และเมื่อจะใช้ tracing เป็น OTel ก็เกิดแนวโน้มที่จะย้าย logs และ metrics ไปด้วยพร้อมกัน
  • logs และ metrics จัดการได้ค่อนข้างง่ายด้วย appender ใหม่และการตั้งค่าเพิ่มเติม แต่ tracing ยังเป็นอีกปัญหาหนึ่งต่างหาก

ทำไม distributed tracing ถึงต้องมี context propagation

  • ในระบบ distributed หากต้องการติดตามทรานแซกชันเดียวตลอดทั้งระบบ จำเป็นต้องส่งต่อข้อมูลที่ใช้เชื่อมการเรียกต่าง ๆ เข้ากับทรานแซกชันนั้น ซึ่งเรียกว่า context propagation
  • trace คือโครงสร้างแม่ที่ครอบหน่วยงานย่อยหลายตัวที่เรียกว่า span และ span สามารถมีหลายตัวและซ้อนกันได้
  • ในเว็บอีคอมเมิร์ซ กระบวนการกดปุ่ม “buy now” สามารถถูกรวมเป็น trace เดียวได้
    • span ที่ frontend ติดต่อกับ backend
    • child span ที่ backend ติดต่อกับบริการชำระเงิน จัดส่ง และจัดการคำสั่งซื้อ
    • child span เพิ่มเติมที่บริการ downstream แต่ละตัวใช้สื่อสารกับระบบอื่นต่ออีกทอดหนึ่ง
  • เมื่อใช้ OTel แต่ละบริการแบบ distributed จะรายงานส่วนของ trace ของตัวเองไปยัง OTel Collector และ OTel จะประกอบภาพรวมทั้งหมดไว้ใต้ trace ID เดียว

มาตรฐาน context propagation 4 แบบ และร่องรอยของ OpenTracing

  • การติดตั้ง context propagation ที่ OTel ต้องรองรับมี 4 แบบ
  • ฝั่ง Akka ของ IPF รองรับ OpenTracing ที่ประกาศเลิกใช้แล้วอยู่ก่อน และใช้งาน การรองรับ OpenTracing ของ Lightbend Telemetry
  • Lightbend Telemetry 2.20.0 เพิ่มการรองรับ OpenTelemetry logs/events และ metrics แล้ว แต่ ยังไม่มี tracing support ที่สำคัญ
  • ในทางทฤษฎี OTel tracing ดูคล้ายกับการรีแบรนด์ของ OpenTracing และ OpenTracing ของ Lightbend Telemetry ก็รองรับรูปแบบการส่งต่อทั้ง 4 แบบ จึงดูเหมือนน่าจะทำงานร่วมกันได้ แต่ผลลัพธ์จริงไม่เป็นเช่นนั้น

trace ที่ขาดตอนระหว่าง Spring และ Akka

  • IPF ใช้ทั้ง Spring และ Akka ร่วมกัน
    • Spring Boot และ Spring IoC: ใช้สำหรับ bootstrap แอปพลิเคชัน การตั้งค่า และการประกอบ dependency
    • Akka: ใช้สำหรับ event sourcing, scheduling, clustering, sharding, integration เป็นต้น
  • ลูกค้าสามารถเริ่ม payment flow ได้จาก Spring REST controller หรือเมธอดที่ติด @KafkaListener
  • ก่อน OTel ทั้งสามด้านทำงานร่วมกันได้โดยแทบไม่มีปัญหาใหญ่
    • Metrics: Spring และ Akka เปิดเผย Prometheus endpoint คนละชุด
    • Logs: ทั้งสองเฟรมเวิร์กใช้ SLF4J และ Logback
    • Traces: ทั้งคู่ใช้ OpenTracing
  • เมื่อใช้ OTel นั้น metrics และ logs ยังส่งออกได้ปกติ เพราะ Spring และ Akka ทำงานแยกจากกันได้ แต่ tracing จำเป็นต้องแชร์ trace context เดียวกันภายใน JVM
  • ปัญหาจริงคือภายในแอปเดียวกัน แนวคิดการติดตามเดียวกันถูกแทนด้วย API คนละชุดสองชุด และ API ทั้งสองไม่ได้สื่อสารกัน
  • พฤติกรรมที่ถูกต้องคือ Akka HTTP client ควร reuse trace ID เดิม 123 และสร้างแค่ span ID ใหม่ เพื่อให้เป็นหน่วยงานย่อยใหม่ภายใต้ trace เดิม แต่สิ่งที่เกิดขึ้นจริงคือมีการสร้างสอง trace ที่ไม่เกี่ยวข้องกัน

opentracing-shim และการชนกันของ implementation

  • ฝั่ง OTel มี opentracing-shim ที่ทำให้ OTel Tracer ดูเหมือนเป็น OpenTracing Tracer
  • แต่ Lightbend Telemetry ใช้ implementation ของ Tracer แบบ custom จึงทำให้ทั้ง shim และ Jaeger ใช้งานล้มเหลว
  • ใน log มี error ดังนี้
    • Expected to have an OpenTelemetry Span but got cinnamon.opentracing.TraceLocal$ContextOnlySpan
    • Expected to have a JaegerSpanContext but got io.opentelemetry.opentracingshim.SpanContextShim
  • Jaeger ล้มเหลวเมื่อได้รับ context จาก OTel shim และ OTel ก็ล้มเหลวเมื่อได้รับ context จาก Lightbend

การตรวจสอบ Java Agent และ tracing ภายในของ Lightbend

  • ทั้ง OTel และ instrumentation ของ Lightbend Telemetry ต่างก็ใช้ Java Agent เพื่อ hook การเรียกเมธอดบางตัวในบางคลาส แล้วรายงานกิจกรรมไปยัง tracer
  • instrumentation ของ Lightbend Telemetry ไม่ได้เป็นโอเพนซอร์ส จึงต้องตรวจสอบโค้ด tracer ที่ decompile แล้วเพื่อทำความเข้าใจการทำงาน
  • มีการสร้างตัวอย่างย่อสำหรับทำซ้ำปัญหา เพื่อตรวจสอบว่าจุดใดที่ OTel trace context จากฝั่ง Spring ขาดหายไปก่อนถึงฝั่ง Akka
  • จุดที่ Jaeger ล้มเหลวคือโค้ดใน JaegerTracer.java บรรทัดนี้
  • ตัวเรียกคือคลาส instrumentation แบบ proprietary ของ Lightbend Telemetry ชื่อ OpenTracingAkkaPersistenceActorInstrumentation.class
  • ในโค้ดที่ decompile แล้ว เงื่อนไขสำคัญอยู่ที่สถานะของ var5
    • ถ้า var5 ซึ่งเป็น OpenTracing SpanContext ไม่เป็น null ก็จะผูก span ใหม่ให้เป็นลูกของ active span ปัจจุบัน
    • ถ้า var5 เป็น null span นั้นก็จะไม่เกี่ยวข้องกับ trace เดิม
    • ถ้า var5 ไม่ใช่ Jaeger SpanContext ก็จะทำให้ Jaeger ล้มเหลว

เชื่อม OTel กับ OpenTracing ด้วยการแปลงแบบแมนนวล

  • SpanContext ถูกดึงมาจาก this.traceLocal.currentContext() และ traceLocal ถูก initialize จาก ExtendedTracer ของ Lightbend
  • ในเอกสารของ Lightbend มีวิธีเข้าถึง ExtendedTracer แบบ global
    • GlobalExtendedTracer.get()
  • GlobalExtendedTracer.get() จะคืนค่า ExtendedTracer และ local() จะคืนค่า TraceLocal ตัวเดียวกับที่ไลบรารี instrumentation ใช้อยู่
  • TraceLocal นี้มีเมธอด activateContext ที่รับ OpenTracing SpanContext
  • แนวทางแก้มี 4 ขั้นตอน
    • ไม่ใช้ OTel shim
    • inject OpenTelemetry Context ปัจจุบันลงใน Java Map
    • ใช้ค่าจาก Map นั้น extract ออกมาเป็น Jaeger SpanContext
    • activate Jaeger SpanContext ใน TraceLocal ของ Lightbend ก่อนเข้าสู่ Akka
  • งานใน OTel propagators API ที่ใช้คือ inject and extract operations
  • โค้ดแกนหลักทำตามลำดับดังนี้
    • สร้าง HashMap ว่าง
    • inject OTel context ลงใน map ด้วย GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().inject(...)
    • สร้าง JaegerSpanContext ด้วย new TextMapCodec(false).extract(new TextMapAdapter(otelContext))
    • activate context ก่อนเข้า Akka ด้วย GlobalExtendedTracer.get().local().activateContext(openTracingContext)

พฤติกรรมที่ยืนยันแล้วและคำแนะนำในการใช้งานจริง

  • หลังเชื่อมแบบแมนนวลแล้ว trace ต่อเนื่องได้ตามคาด
    • ตั้งแต่ Spring REST call /submit เป็นต้นไป ทั้ง flow เชื่อมกันเป็น trace เดียว
    • สามารถใช้งานส่วนที่ instrument ด้วย OTel API ปนกับส่วนที่ instrument ด้วย OpenTracing API ได้
    • trace ถูกส่งต่อข้ามขอบเขต HTTP ได้
  • ในตัวอย่างมีการใช้ชื่อต่างกันเพื่อแยก instrumentation สองชนิด แต่ในสภาพแวดล้อมลูกค้าจริง แนะนำให้ตั้ง otel.service.name และ cinnamon.application ให้ตรงกัน เพื่อให้จากภายนอกมองเป็นแอปพลิเคชันเดียว

ทำไมมันถึงซับซ้อน และความกังวลที่ยังเหลืออยู่

  • หากจะเขียน Lightbend Telemetry ใหม่บน OTel API ก็อาจเป็นงานใหญ่ เพราะต้องพอร์ต instrumentation จำนวนมากที่ยังผูกอยู่กับ Jaeger API ไปเป็น OTel API
  • OTel Collector รองรับการเก็บ trace แบบ legacy Zipkin อยู่แล้ว ทำให้ Lightbend อาจรองรับ OTel แบบสมบูรณ์ได้ผ่านการพึ่งพา legacy support
  • สาเหตุโดยตรงของความซับซ้อนคือการพยายามผสานระบบ instrumentation สองชุดที่ใช้ไลบรารี tracing คนละตัวเข้าด้วยกัน
  • โครงการ OTel พยายามจัดระเบียบโลก observability ด้วยความพยายามทำมาตรฐาน เช่น semantic conventions แม้จะดูซับซ้อนในช่วงแรกที่ทำความเข้าใจ แต่ก็ถูกประเมินว่าเป็นโครงการ FOSS ที่มีประโยชน์
  • ยังมีความกังวลว่า Akka จะส่ง trace context ระหว่าง thread ภายใน actor model ได้ถูกต้องหรือไม่
    • ในการทดสอบโหลดขนาดเล็ก พบว่าทำงานได้ตามคาด
    • มีการเปิด ticket ที่เกี่ยวข้องไว้ฝั่ง Akka แล้ว

1 ความคิดเห็น

 
GN⁺ 2025-01-11
ความคิดเห็นบน Hacker News
  • ตลอดช่วงที่เรียนรู้และพอร์ต Otel ผมรู้สึกเหมือนกลับไปอยู่ในโลก Java อีกครั้ง ทุกครั้งที่ไล่โค้ดทีละบรรทัดมันเหมือน EnterpriseFizzBuzz ไม่มีทางค้นพบอะไรได้เองเลย และศัพท์เฉพาะของมันก็ดูเหมือนถูกสร้างโดยคนที่เมาอะไรสักอย่าง
    บน NodeJS การใช้ CPU สูงกว่า StatsD ประมาณ 4 เท่า สุดท้ายจึงต้องทำการ aggregate เองเพื่อลดการใช้งานและลดการระเบิดของแท็กด้วย StatsD ไม่เป็นไรแม้หลายโปรเซสจะรายงานแท็กเดียวกัน แต่ OTEL จะเขียนทับไปเลย
    ตอนโหลดสูงสุด CPU หนึ่งตัววิ่งที่การใช้งาน 60–80% และก่อนจะมีอะไรเปลี่ยนแปลงก็ยังขยายแนวตั้งไม่ได้ด้วย สำหรับภาษาที่ใช้หนึ่งโปรเซสต่อหนึ่งคอร์ OTEL เหมือนตั้งตัวเป็นศัตรูอย่างจริงจังจนเหมือนเป็นเรื่องตลก ใช้ Prometheus ไปเลยดีกว่า และในทางปฏิบัติก็ไม่มีคู่แข่งอื่นด้วย

    • ผมมองว่า OTEL ค่อนข้างใกล้เคียงกับภาวะ vendor lock-in แบบหนึ่ง เพราะถ้าจะสร้างมาตรฐาน ก็มีแต่ต้องประนีประนอมความต้องการของบริษัทใหญ่หลายแห่งกับสตาร์ทอัพหลวม ๆ แล้วเอาปืนกาวมาแปะรวมกัน
      ตอนลองตั้งค่า otel ง่าย ๆ บน .NET ผมอ่านเอกสารของเวนเดอร์ที่องค์กรเลือกอยู่หลายชั่วโมงก็ยังไม่เข้าใจ เลยเข้า Discord ที่เพื่อนร่วมงานดูแลอยู่ โมเดลธุรกิจส่วนหนึ่งของที่นั่นคือ “ถ้าอยากใช้ otel ที่ใช้ได้จริงในผลิตภัณฑ์โอเพนซอร์ส ก็ต้องจ่ายเงิน” และผมก็รู้สึกทันทีว่าต่อให้ค่าใช้จ่ายเท่าไรก็คุ้ม
      แทนที่จะทำ OTEL implementation ผมยอมสร้างไลบรารี event/pub-sub ที่เสถียรขึ้นมาอีกตัวโดยไม่มีประสบการณ์ล่วงหน้าดีกว่า
    • สำหรับคำว่า “ไม่มีคู่แข่งอื่นด้วย” แล้วแต่กรณี Apache SkyWalking ก็น่าดู ไม่กินทรัพยากรมาก และการตั้งค่ากับการดูแลค่อนข้างเรียบง่าย แต่ UI กับเอกสารยังไม่ขัดเกลาเท่าไร: https://skywalking.apache.org/
      คอนฟิกขั้นต่ำจริง ๆ ก็เล็กพอสมควร มีแค่เว็บ UI, อินสแตนซ์เซิร์ฟเวอร์ และ DB ที่คุณรู้อยู่แล้วก็พอ: https://skywalking.apache.org/docs/main/latest/en/setup/back...
      มันมีด้านที่คล้าย Zabbix ในวงการมอนิเตอร์ริ่งอยู่ ทั้งสองอย่างคงไม่ได้ทำให้ใครว้าว แต่ความใช้งานได้จริงนั้นเพียงพอ
    • ข้อสรุปของผมก็เหมือนกัน ใช้แค่ Prometheus กับ client library ของภาษาที่ต้องการก็พอ และมันง่ายกว่าเรื่อง OTEL เป็น 1000 เท่า
    • ผมใช้ otel ฝั่ง C++ อยู่ และเพื่อให้ได้เมตริกสะสมจากหลายแอปพลิเคชัน ผมสร้างแอตทริบิวต์จำนวนเต็มที่มี cardinality ค่อนข้างต่ำอย่าง process.vpid ขึ้นมา สามารถประสานให้ค่านั้นไม่ซ้ำกันระหว่างที่แอปยังมีชีวิตอยู่ได้ด้วยอะไรทำนอง global object
      จากนั้นก็วางบางอย่างไว้เพื่อรวมค่าและลบแอตทริบิวต์นั้นออก ใน statsd/delta ถ้าสูญเสียการส่งสัญญาณไปหนึ่งครั้ง ข้อมูลทั้งหมดจะบิดเบี้ยว แต่ในวิธีสะสมจะเสียแค่ความแม่นยำ
      กรณีใช้งานของผมคือ เมตริกแบบ push-based จากเครื่องมือ “batch” ไม่ใช่โปรเซสระยะยาวที่ scrape ได้
    • ตรงกับประสบการณ์ของผมเหมือนกัน เข้าใจได้ยากมากว่าต้องมีอะไรบ้างถึงจะได้ผลลัพธ์ที่ต้องการ
  • เหตุผลที่ Otel ดูซับซ้อน เป็นเพราะเวนเดอร์ด้าน observability หลายรายทำให้การทำ observability ง่ายมากด้วย SDK, เอเจนต์ และ API เฉพาะของตัวเอง Otel พยายามแก้ปัญหานี้ และผมคิดว่าคนที่สร้างมันทำได้ยอดเยี่ยม
    Grafana ก็ควรได้รับคำชมที่ยอมรับ OpenTelemetry ให้เป็นองค์ประกอบชั้นหนึ่งของ ecosystem
    ผมผลักดันการใช้ Datadog มาหลายปี แต่สำหรับขนาดระหว่างบริษัทระดับกลางกับองค์กรขนาดใหญ่ ราคากลายเป็นสิ่งที่รับไม่ไหว เมื่อเวลาผ่านไป OpenTelemetry API และ SDK เสถียรขึ้น จึงนำมาใช้เป็นมาตรฐาน observability ของแอปพลิเคชัน
    อย่างไรก็ตาม เอกสารโดยรวมยังปรับปรุงได้อีก และการที่เอกสาร onboarding แตกต่างกันตามภาษาไม่ใช่เรื่องที่เหมาะนัก
    ตอนนี้ทีมใช้สแตก NodeJS/Typescript และได้ทำชุดแพ็กเกจกับตัวอย่างสแตก Grafana เพื่อให้เริ่มใช้ OpenTelemetry ได้เร็ว: https://github.com/zonneplan/open-telemetry-js

    • หมายความว่าปัญหาที่เกิดจากทุกคนทำให้มันง่ายมาก ผลิตภัณฑ์นี้แก้ด้วยการทำให้มัน ซับซ้อน งั้นเหรอ? ;P
    • ผมชอบการเก็บเมตริกดี ๆ แบบเรียบง่ายมากกว่า Datadog ในทางกลับกัน ผมเข้าใจในเชิงทฤษฎีว่า OTEL มีคุณค่ามากกว่าวิธีที่ผมชอบ
      ปัญหาใหญ่ที่สุดของเวนเดอร์ APM ทุกเจ้าคือทันทีที่เอเจนต์เวทมนตร์มี kernel hook ก็จะเกิดสารพัดเรื่องที่นักพัฒนาอธิบายไม่ได้
      สมัยก่อนที่บริษัทอื่นมีการนำ Dynatrace มาใช้ โชคดีที่แอปมีเมตริกในตัวเพียงพออยู่แล้ว ถึงขั้นที่ lead SRE มองว่าเป็น “ตัวอย่างที่ดี” ของการทำ instrumentation แต่ทันทีที่ติดตั้งเอเจนต์ Dynatrace บนโฮสต์ของแอป ก็เกิด Heisenbug หลายอย่างที่ต้องรีสตาร์ตโหนด และยังวัด performance degradation ได้โดยตรงด้วย
      น่าขันที่เมตริกช่วยให้หลีกเลี่ยงความเจ็บปวดได้ แต่ไม่มีใครรู้วิธีแก้ ที่แย่ที่สุดคือกรณีที่ตอนอัปเดต MSSQL แล้ว failover ทำให้ connection pool ของ ADO.NET ปนเปื้อนอย่างประหลาด
  • เป็นโครงสร้างที่ซับซ้อนเท่าที่จำเป็นเท่านั้น ควรหลีกเลี่ยงฟีเจอร์ที่เหมือนเวทมนตร์ และใช้แค่ subset ที่มีคุณค่าที่สุดตามบริบทและเข้าใจง่ายก็พอ
    ในทีมของเราเรียบง่ายมาก ใช้ ไลบรารีที่ส่งเฉพาะ trace และ trace ก็ให้คุณค่ามากที่สุดต่อการสังเกตแอปพลิเคชัน อีกทั้งยังใส่ข้อมูลประเภทอื่นได้ด้วย โดยพื้นฐานแล้วก็เหมือนใช้ hashmap แทน string หรือ float
    เราใช้ manual instrumentation แทน auto instrumentation ตั้งใจเลือกว่าจะสังเกตอะไร และเข้าใจดีว่าโค้ดส่วนไหนปล่อย span ออกมา นอกจากนี้ยังมีกฎการตั้งชื่อที่สอดคล้องกับโครงสร้างโค้ดด้วย
    ฝั่ง backend ใช้ทั้งบริการ third-party ราคาถูกและการติดตั้ง all-in-one Jaeger สำหรับพัฒนาในเครื่อง อย่างหลังแค่รันไฟล์ executable หนึ่งไฟล์หรือ Docker container หนึ่งตัวก็พอ และไม่บันทึก span ลงดิสก์ ส่วนใหญ่ใช้เพื่อให้ทีมสบายใจว่าจะไม่ทำให้บริการ third-party ล้น
    การ monitor infrastructure มีการตั้งค่าเดิมอยู่แล้ว และในกรณีของเราไม่รู้สึกว่าการรวบรวม log กับ metric ของ infrastructure ทั้งหมดมีคุณค่ามากนัก มองว่า OTEL metric และ log ยังอยู่ช่วงเริ่มต้น แต่ vendor ไม่ได้พูดแบบนั้น

    • มันซับซ้อนได้เท่าที่ต้องการก็จริง แต่ไม่ได้ง่ายเท่าที่ผมอยากให้เป็น ระดับความยากขั้นต่ำ ค่อนข้างสูง
      ตอนนี้กำลังหา endpoint สำหรับส่ง metric แบบง่าย ๆ ครั้งเดียวจากบางส่วนของ infrastructure ที่ยัง scrape ไม่ได้
    • ผมไม่ได้รู้สึกว่า manual instrumentation ง่ายกว่า มันเหมือนเป็นการแลกเปลี่ยนมากกว่า: แทนที่ learning curve จะเริ่มก่อนจะเห็นผลลัพธ์อยู่นาน ก็ทำให้เข้าใจชัดขึ้นว่า เครื่องจักร Rube Goldberg ตัวนี้สร้างต้นทุนด้าน performance อะไรบ้าง
      Otel อาจโอเคสำหรับโปรเจกต์ใหม่ แต่การเปิดใช้กับบริการ production ที่มี telemetry อยู่แล้วให้ความรู้สึกเหมือนเปลี่ยนยางรถที่กำลังวิ่งอยู่
    • แชร์ได้ไหมว่าบริการ third-party ราคาถูกที่ว่านั้นคืออะไร?
    • เป็นคำแนะนำที่สมเหตุสมผลมาก ส่วนใหญ่น่าจะมีอะไรสักอย่างสำหรับ metric และ log อยู่แล้ว และถ้าไม่มี ผลตอบแทนต่อการลงทุน จากการเปลี่ยน ก็ไม่มีเหตุผลต้องทำ
    • การบอกว่า “หลีกเลี่ยงเวทมนตร์แล้วใช้แค่ subset ก็พอ” จะเป็นไปได้ก็ต่อเมื่อไลบรารีทั้งหมดที่ใช้ก็ใช้แค่ subset นั้นด้วย จากประสบการณ์ของผม ส่วนใหญ่ไม่เป็นแบบนั้นเลย และบทความก็แสดงตัวอย่างที่เป็นรูปธรรมได้ดีว่าทำไม
      ผมเชื่อว่าอาจดีสำหรับ greenfield project ที่ใช้แต่ otel และไม่มี framework ที่ไม่ใช่ otel แต่ผมยังไม่ได้อยู่ในโลกแบบนั้น
  • หนึ่งในปัญหาใหญ่ที่สุดคือ ประสบการณ์พัฒนาในเครื่อง อยากรองรับ log, trace, metric ในเครื่อง แต่ไม่อยากต้องเปิด Docker image จำนวนมากเพื่อสิ่งนั้น อยากดูผ่าน log ก่อน deploy ว่า metric, trace, baggage และ activity span จะหน้าตาเป็นอย่างไร
    ล่าสุดทีม .NET ออก .NET Aspire มา ซึ่งดีมาก ทำให้ visualize ทุกอย่างในที่เดียวได้ง่ายใน local development stack และทำงานเหมือน orchestrator ที่เขียนเป็นโค้ด
    เวลานำไป deploy บน k8s ก็ชี้ OTEL endpoint ไปที่ DataDog Agent แล้วทุกอย่างก็ทำงานเลย เราหลีกเลี่ยง custom trace library และ SDK ของ DataDog แล้วใช้แต่ OTEL
    ตอนนี้ประสบการณ์พัฒนาดีขึ้นพอสมควร
    https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals...
    https://docs.datadoghq.com/opentelemetry/#overview

    • สำหรับกรณีนั้น โปรเจกต์นี้ดีมากจริง ๆ: https://github.com/grafana/docker-otel-lgtm
    • มี https://github.com/CtrlSpice/otel-desktop-viewer ด้วย
    • แค่ใช้ https://github.com/openobserve/openobserve ก็พอ
      ตั้งค่าครั้งแรกบนเครื่องพัฒนาในเครื่องใช้เวลา 5 นาที หลังจากนั้นก็แค่รัน /path/to/openobserve ในแท็บ terminal แยกก็จบ ถ้าอยากหลีกเลี่ยงความซับซ้อนมหึมาของ single statically linked binary ก็มี Docker image สำหรับรันทั้ง local และ remote ให้ด้วย :P
      เป็น all-in-one OpenTelemetry backend ที่มีกราฟสวย ๆ และในโปรเจกต์ของผมยังไม่เคยล้มเหลวแบบที่สังเกตได้เลย
    • ผมยังไม่ถูกโน้มน้าวด้วย .NET Aspire มันแก้ปัญหาเล็ก ๆ เรื่อง service discovery และ orchestration ของหลาย service project ใน local development แต่กลับแก้โดยทำให้มันเป็น ความกังวลระดับแอปพลิเคชัน
      ถ้าใช้ Aspire จะเพิ่มความซับซ้อนที่ไม่จำเป็นในระดับแอป และผูกติดกับ ecosystem ที่แคบ สำหรับ local development มีทางเลือกที่พิสูจน์แล้วมากมายอย่าง docker compose และ Aspire ก็ไม่ได้ง่ายกว่า docker compose กับ environment variable อย่างชัดเจนด้วย
    • มี all-in-one Docker image อย่างเป็นทางการที่รวมทุกอย่างไว้แล้ว
  • ถ้าจะทำ otel ใน Python แนะนำให้ใช้ client ของ Logfire ถึงแม้ไม่ได้ใช้บริการ Logfire ก็ตาม
    เป็นโอเพนซอร์ส และส่งไปยัง endpoint ที่รองรับ otel ใด ๆ ก็ได้ แถม client ที่ทีม pydantic ทำยังดีกว่าและเรียบง่ายกว่าไลบรารี otel อย่างเป็นทางการเป็น 10 เท่า
    มีบทสัมภาษณ์ที่น่าสนใจซึ่ง Samuel Colvin อธิบายว่าเขามาถึงจุดนี้ได้อย่างไร: https://www.bitecode.dev/p/samuel-colvin-on-logfire-mixing-p...

  • ดังนั้นผมจึงเริ่มโปรเจกต์โอเพนซอร์สที่มุ่งทำให้การนำ OpenTelemetry มาใช้เป็นเรื่องง่ายเหมือน คำสั่งบรรทัดเดียว: https://github.com/odigos-io/odigos

  • ทุกวันนี้เว็บเฟรมเวิร์กจำนวนมากช่วยจัดการการทำ instrumentation ส่วนใหญ่ให้แทน เช่น ถ้าใช้ opentelemetry-js แล้วโฮสต์ของอย่าง https://signoz.io เอง ก็สามารถตั้งขึ้นมาได้ในไม่ถึงหนึ่งชั่วโมง และได้ข้อมูลจำนวนมากโดยไม่ต้องเขียนโค้ด custom

    • รีโพซิทอรีของ SigNoz อยู่ที่นี่: https://github.com/signoz/signoz
    • ใน runtime แบบอะซิงโครนัสหลายเธรด การส่งต่อ context ไม่ใช่เรื่องง่าย มีหลายวิธี แต่เหตุผลที่ JVM agent ซึ่งทำ instrumentation ที่ bytecode ได้รับความนิยมก็เพราะมันทำงานได้อย่างโปร่งใส
  • OpenTelemetry เติบโตมาจาก trace แต่ metric และ log ปล่อยให้โซลูชันเฉพาะทางจัดการจะดีกว่ามาก
    รู้สึกเหมือนปัญหา “abstraction ที่รั่ว” หรือ “framework ที่รั่ว” ถ้าอยากเอาทุกอย่างมาไว้ใต้ร่มเดียวกัน ฐานข้อมูล SQL ก็ทำทั้งหมดนี้พร้อมกันได้เหมือนกัน แต่นั่นไม่ได้แปลว่าควรทำ

    • Cramer อยากเอา trace ออกจาก OTel ซึ่งก็น่าขันอยู่เมื่อคิดว่าเขาเป็นหนึ่งในผู้สร้าง OpenTracing
      https://cra.mr/the-problem-with-otel/
    • การกำหนดตำแหน่งภายใน trace ให้กับ metric และ logging นั้นมีประโยชน์จริง ๆ
      ถึงอย่างนั้นทุกครั้งที่ต้องจัดการกับ OTel ก็ยังไม่ชอบอยู่ดี
  • ถ้าอ่านจนจบจะเห็นว่าความเจ็บปวดส่วนใหญ่เป็นสิ่งที่สร้างขึ้นเอง ใน stack มาตรฐานของ Python (mysql, flask, redis, requests ฯลฯ) มันง่ายมาก แค่ใส่ import ไม่กี่ตัวไว้ด้านบนของ service ก็เชื่อมต่ออัตโนมัติและ trace ทุกอย่างได้โดยไม่วุ่นวายอะไร

    • ถูก แต่จะเป็นแบบนั้นก็ต่อเมื่อทุกอย่างใน stack รองรับ auto instrumentation เท่านั้น เช่น aiohttp เวอร์ชันล่าสุดคือ 3.11.X และ auto instrumentation ระบุว่ารองรับ 3.X [0] แต่ผลลัพธ์จริงขึ้นอยู่กับว่า aiohttp ที่ใช้ใหม่แค่ไหน และฝั่ง auto instrumentation อยู่ในสถานะใด
      ถ้าทุกอย่างลงตัวก็ทำงานเหมือนเวทมนตร์ แต่ช่องให้ผ่านนั้นค่อนข้างแคบ
      [0]: https://github.com/open-telemetry/opentelemetry-python-contr...
    • เมื่อไม่นานมานี้ต้องเอาสิ่งนี้ไปผูกกับแอป Flask ที่เรียบง่ายมากตัวหนึ่ง เพราะต้องรันเฉพาะในอินทราเน็ตภายในบริษัท เลยรัน otel-collector-contrib, jaeger-all-in-one, prometheus ด้วย docker compose บนเซิร์ฟเวอร์เดียว
      trace ทำงานแล้ว และตั้งค่า spanmetrics exporter แล้วด้วย ถ้า query prometheus โดยตรงก็เห็น spanmetrics แต่ไม่ว่าจะทำอย่างไรก็ไม่โผล่ในแท็บ “monitor” ของ jaeger
      หลังเสียเวลาไป 3 วัน หัวหน้าก็บอกว่า “แค่ทำ manual instrumentation แล้วส่งทั้งหมดเข้า SQL Server จากนั้นทำ Grafana dashboard ดีไหม” แต่ก็ไม่อยากทำแบบนั้นเหมือนกัน
      เป็น use case ที่ง่ายที่สุดแล้วยังไม่เวิร์ก ต้องเพิ่ม Grafana เข้าไปอีกหรือเปล่า?
    • จะเป็นแบบนั้นแค่จนกว่าจะรันเซิร์ฟเวอร์หลังอะไรอย่าง gunicorn เท่านั้น ทันทีที่ทำแบบนั้น auto import ทั้งหมดก็หยุดลง และต้องทำเองทั้งหมด
  • เหตุผลที่ซับซ้อนก็เพราะมันถูกออกแบบมาเพื่อ บริษัทที่ขายซอฟต์แวร์ที่เข้ากันได้กับ Otel ไม่ใช่เพื่อวิศวกรที่นำไปใช้งาน

    • เรื่องนั้นไม่ค่อยแน่ใจ vendor น่าจะพอใจกับโค้ด agent และ backend แบบเฉพาะของตัวเองมากกว่า เพราะ lock-in ทำให้ต้องเขียนโค้ดใหม่ทั้งหมด ต้นทุนการย้ายจึงสูงมาก
    • จากสิ่งที่เห็นจากผู้ร่วมพัฒนา ไม่ได้เป็นแบบนั้น
      กลับกัน คิดว่า backend ต่าง ๆ ค่อนข้างช้าในการนำไปใช้ด้วยซ้ำ
    • อาจฟังดูจู้จี้ แต่สมัยก่อนคำว่า ‘implement’ หมายถึงกิจกรรมอย่างแรก คือการนั่งเขียนซอฟต์แวร์ให้ตรงตามสเปก ไม่ได้หมายถึงอย่างหลังคือการนำซอฟต์แวร์ที่มีอยู่ไป deploy บนเซิร์ฟเวอร์
      ความหมายของคำเปลี่ยนได้ ไม่เป็นไร แต่ก็น่าสนใจที่ตอนนี้เหมือนคำสำหรับ การเขียนซอฟต์แวร์ให้ตรงตามสเปก หายไป เหลือแต่ “การนำซอฟต์แวร์ที่มีอยู่ไป deploy บนเซิร์ฟเวอร์”