Jaeger Setup: Trace Requests ผ่าน Microservices

Jaeger เป็นหนึ่งใน distributed tracing platform ที่ได้รับความนิยมสูงสุดในกลุ่ม cloud-native ด้วยสถาปัตยกรรมที่ยืดหยุ่น รองรับ storage หลายรูปแบบ และมี UI ที่ช่วย visualize trace ได้ดี บทความนี้จะพาไปติดตั้ง Jaeger แบบ all-in-one สำหรับ dev, setup production-grade ด้วย collector แยก, เลือก storage backend ที่เหมาะกับ scale, และเชื่อมต่อ application ผ่าน OpenTelemetry

เนื้อหาครอบคลุมตั้งแต่การรัน docker-compose สำหรับทดสอบ, การ deploy บน Kubernetes ด้วย Operator, ไปจนถึงการตั้ง sampling strategy และ tips สำหรับ operate ระบบใน production จริง

สถาปัตยกรรมของ Jaeger

Jaeger ประกอบด้วย 4 component หลัก: SDK/Instrumentation (ฝังใน application, สร้าง span), Collector (รับ span, validate, index, เขียนเข้า storage), Storage (Cassandra, Elasticsearch, Badger, Kafka buffer), และ Query + UI (ดึงข้อมูลมาแสดง) สำหรับ deployment ขนาดเล็กสามารถรวมทุกอย่างใน binary เดียวแบบ all-in-one ได้

ในรุ่นใหม่ (เริ่มจาก v2) Jaeger ใช้ OpenTelemetry Collector เป็น ingestion pipeline รับ span format ทั้ง OTLP, Jaeger native, และ Zipkin ได้ในตัวเดียว — ลดความซับซ้อนในการ integrate กับ stack ที่หลากหลาย

All-in-One Setup ด้วย Docker

วิธีที่เร็วที่สุดในการเริ่มต้นคือ run image all-in-one ที่รวม collector, storage (in-memory หรือ Badger), query, UI ไว้ใน container เดียว เหมาะสำหรับ dev environment, workshop, หรือการทดสอบ instrumentation:

docker run -d --name jaeger \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -e COLLECTOR_OTLP_ENABLED=true \
  jaegertracing/all-in-one:latest

# เปิด UI ที่ http://localhost:16686

Port ที่ควรรู้: 16686 คือ UI, 4317/4318 คือ OTLP gRPC และ HTTP, 14250 คือ gRPC สำหรับ Jaeger native, 14268 คือ HTTP สำหรับ legacy client การเปิด COLLECTOR_OTLP_ENABLED=true ทำให้รับ trace จาก OpenTelemetry SDK ได้โดยตรง

Production Setup ด้วย docker-compose

สำหรับ production ควรแยก storage ออกจาก Jaeger binary ตัวอย่างการ deploy Jaeger + Elasticsearch ด้วย docker-compose:

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    volumes:
      - es_data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"

  jaeger-collector:
    image: jaegertracing/jaeger-collector:latest
    environment:
      - SPAN_STORAGE_TYPE=elasticsearch
      - ES_SERVER_URLS=http://elasticsearch:9200
      - COLLECTOR_OTLP_ENABLED=true
    ports:
      - "4317:4317"
      - "4318:4318"
    depends_on:
      - elasticsearch

  jaeger-query:
    image: jaegertracing/jaeger-query:latest
    environment:
      - SPAN_STORAGE_TYPE=elasticsearch
      - ES_SERVER_URLS=http://elasticsearch:9200
    ports:
      - "16686:16686"
    depends_on:
      - elasticsearch

volumes:
  es_data:

Collector และ Query ทำงานแยกกัน ทำให้สามารถ scale collector ตาม ingestion throughput ได้โดยไม่กระทบ UI หาก traffic เยอะควรเพิ่ม Kafka เป็น buffer หน้า collector เพื่อกัน spike ที่อาจทำให้ ES ตาม index ไม่ทัน

Deploy บน Kubernetes ด้วย Operator

Jaeger Operator ช่วยให้การ deploy บน Kubernetes ง่ายขึ้นมาก โดยใช้ Custom Resource กำหนด instance ที่ต้องการ:

apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: prod-tracing
  namespace: observability
spec:
  strategy: production
  storage:
    type: elasticsearch
    options:
      es:
        server-urls: http://es-master:9200
  collector:
    replicas: 3
    resources:
      limits:
        cpu: 1
        memory: 2Gi
  query:
    replicas: 2
  sampling:
    options:
      default_strategy:
        type: probabilistic
        param: 0.1

Strategy production จะสร้าง collector และ query เป็น deployment แยก, allInOne ใช้สำหรับ dev, และ streaming จะเพิ่ม Kafka เข้ามาเป็น buffer กรณี workload หนัก

เชื่อมต่อ Application ผ่าน OpenTelemetry

วิธีแนะนำคือใช้ OpenTelemetry SDK ที่รองรับการส่ง span ผ่าน OTLP ไปยัง collector endpoint ของระบบ tracing ตัวอย่าง Node.js application:

// telemetry.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { Resource } = require('@opentelemetry/resources');

const sdk = new NodeSDK({
  resource: new Resource({
    'service.name': 'order-api',
    'service.version': '1.2.0',
  }),
  traceExporter: new OTLPTraceExporter({
    url: 'http://jaeger-collector:4317',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();

auto-instrumentations-node จะติด instrumentation ให้ HTTP, Express, PostgreSQL, Redis, และอื่น ๆ อัตโนมัติ โดยไม่ต้องแก้ code ทุกจุด หลัง run application ไม่กี่วินาทีก็จะเห็น trace บน UI ทันที

Sampling Strategy

Jaeger รองรับ sampling ทั้งฝั่ง client (SDK) และ server (collector) แบบ adaptive — กำหนด rate ต่อ service ได้แยกกัน เช่น service ที่มี traffic สูงเก็บ 1%, service สำคัญที่ traffic ต่ำเก็บ 100% ตัวอย่าง config:

{
  "default_strategy": {
    "type": "probabilistic",
    "param": 0.05
  },
  "per_service_strategies": [
    {
      "service": "payment-service",
      "type": "probabilistic",
      "param": 1.0
    },
    {
      "service": "search-api",
      "type": "ratelimiting",
      "param": 100
    }
  ]
}

Best Practices

  • แยก collector ออกจาก query ในการ deploy เพื่อ scale แยกตาม workload
  • ใช้ Kafka เป็น buffer หน้า storage หาก ingestion rate สูงหรือ storage อาจล่มชั่วคราว
  • ตั้ง retention policy ของ storage ให้เหมาะสม — trace เก่าเกินสัปดาห์มักใช้ประโยชน์น้อย
  • Monitor collector ด้วย Prometheus — metric สำคัญคือ jaeger_collector_spans_received และ jaeger_collector_queue_length
  • ใช้ service.name และ deployment.environment attributes เพื่อแยก production vs staging ใน UI
  • เลือก storage ตาม scale — Badger เหมาะกับ < 1k span/s, Elasticsearch กลาง, Cassandra สำหรับ high throughput
  • ตั้ง alert เมื่อ dropped span rate เกิน threshold — บ่งชี้ collector overload

สรุป

การ setup Jaeger สามารถเริ่มได้จาก all-in-one image ใน 1 คำสั่ง แล้วค่อยขยายเป็นสถาปัตยกรรมแยก component เมื่อ traffic เพิ่มขึ้น การเชื่อมกับ OpenTelemetry SDK ทำให้ instrument ได้ง่ายและไม่ผูกติดกับ vendor เดียว

สำหรับ production ที่มี traffic สูง ควรเน้นการ scale collector, เพิ่ม Kafka buffer, และตั้ง sampling strategy ให้เหมาะกับแต่ละ service เพื่อควบคุมต้นทุน storage พร้อมยังคงได้ข้อมูลที่มีประโยชน์ต่อการ debug