OpenTelemetry (OTel) เป็นมาตรฐาน Observability ที่รวม Traces, Metrics และ Logs ไว้ในเครื่องมือเดียว ก่อนหน้านี้ผู้พัฒนาต้องใช้ SDK หลายตัว เช่น Prometheus client สำหรับ metrics, Jaeger client สำหรับ traces, Fluentd สำหรับ logs — แต่ละตัวมี API และ format ต่างกัน ทำให้ต้อง instrument code ซ้ำซ้อนและผูกติดกับ vendor เฉพาะราย
OTel แก้ปัญหานี้ด้วยการสร้าง API และ SDK กลางที่ทุกภาษาใช้ร่วมกัน เขียน code ครั้งเดียวแล้วส่งข้อมูลไปยัง backend ใดก็ได้ เช่น Jaeger, Zipkin, Prometheus, Grafana Tempo, Datadog หรือ New Relic บทความนี้จะอธิบายสถาปัตยกรรม วิธีการ instrument แอปพลิเคชัน และการติดตั้ง Collector เพื่อใช้งานจริง
OpenTelemetry คืออะไร
OTel เป็นโครงการ open-source ภายใต้ CNCF (Cloud Native Computing Foundation) เกิดจากการรวม OpenTracing และ OpenCensus เข้าด้วยกันในปี 2019 เป้าหมายหลักคือสร้างมาตรฐานเดียวสำหรับการเก็บ telemetry data ทุกประเภท ลดการผูกติดกับ vendor และช่วยให้ทีม DevOps สามารถเปลี่ยน backend ได้โดยไม่ต้องแก้โค้ดแอปพลิเคชัน
3 เสาหลักของ Observability
- Traces — ติดตาม request ที่เดินทางผ่านหลาย service พร้อม latency แต่ละจุด
- Metrics — ข้อมูลตัวเลขที่วัดได้ เช่น request rate, error rate, CPU, memory
- Logs — ข้อความบันทึกเหตุการณ์พร้อม timestamp ที่ใช้ debug ปัญหาเฉพาะจุด
สถาปัตยกรรม OpenTelemetry
ระบบนี้แบ่งออกเป็น 3 ส่วนหลัก ทำงานประสานกันเพื่อส่งข้อมูลจากแอปพลิเคชันไปยัง observability backend
- API — interface ที่นักพัฒนาเรียกใช้ในโค้ด เช่น
tracer.startSpan()ไม่ผูกติดกับ implementation ใดๆ - SDK — implementation ของ API พร้อมความสามารถเสริม เช่น sampling, batching, exporter
- Collector — service กลางที่รับข้อมูลจากแอป แปลง format และส่งต่อไป backend ต่างๆ
OTLP Protocol
OTLP (OpenTelemetry Protocol) คือ protocol มาตรฐานสำหรับส่งข้อมูล telemetry ระหว่าง SDK, Collector และ backend รองรับทั้ง gRPC (port 4317) และ HTTP (port 4318) ออกแบบให้มีประสิทธิภาพสูงและ schema ยืดหยุ่น backend ใหม่ๆ ส่วนใหญ่รองรับ OTLP โดยตรง เช่น Grafana Tempo, Honeycomb และ Datadog
ติดตั้ง OpenTelemetry SDK ใน Node.js
การ instrument แอปพลิเคชัน Node.js เริ่มจากการติดตั้ง SDK และ auto-instrumentation package ซึ่งจะทำ instrument library ยอดนิยมอัตโนมัติ เช่น Express, HTTP, gRPC, PostgreSQL
npm install @opentelemetry/api @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/exporter-metrics-otlp-http
สร้างไฟล์ tracing.js เพื่อ initialize SDK ก่อน require module อื่นๆ
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const sdk = new NodeSDK({
serviceName: 'order-service',
traceExporter: new OTLPTraceExporter({
url: 'http://otel-collector:4318/v1/traces',
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: 'http://otel-collector:4318/v1/metrics',
}),
exportIntervalMillis: 10000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
รันแอปพลิเคชันพร้อม preload tracing.js ด้วย -r flag
node -r ./tracing.js app.js
Manual Instrumentation
สำหรับโค้ดเฉพาะทางที่ auto-instrumentation ไม่ครอบคลุม สามารถสร้าง span เองเพื่อวัด latency ของ business logic เช่น การคำนวณราคา หรือการเรียก external API ที่ไม่ได้ใช้ HTTP client มาตรฐาน
const { trace, SpanStatusCode } = require('@opentelemetry/api');
const tracer = trace.getTracer('order-service');
async function processOrder(orderId) {
return tracer.startActiveSpan('processOrder', async (span) => {
try {
span.setAttribute('order.id', orderId);
const result = await calculateTotal(orderId);
span.setAttribute('order.total', result.total);
return result;
} catch (err) {
span.recordException(err);
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
throw err;
} finally {
span.end();
}
});
}
Python Instrumentation
Python รองรับ OpenTelemetry เช่นเดียวกับ Node.js ใช้ opentelemetry-instrument สำหรับ auto-instrumentation ไม่ต้องแก้โค้ดเลย
pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install
opentelemetry-instrument \
--traces_exporter otlp \
--metrics_exporter otlp \
--service_name payment-service \
--exporter_otlp_endpoint http://otel-collector:4317 \
python app.py
ติดตั้ง OpenTelemetry Collector
Collector เป็นตัวกลางที่รับข้อมูลจากแอปแล้วส่งต่อไป backend ช่วยลดการเชื่อมต่อโดยตรงระหว่างแอปและ backend ให้ประโยชน์ทั้งในด้าน performance (batching, compression) และความยืดหยุ่น (เปลี่ยน backend ได้โดยไม่ต้อง restart แอป)
ไฟล์ otel-collector-config.yaml ตัวอย่างที่รับ OTLP แล้วส่งต่อ Jaeger (traces) และ Prometheus (metrics)
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 10s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
logging:
loglevel: info
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/jaeger, logging]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus, logging]
รัน Collector ด้วย Docker
docker run -d --name otel-collector \
-p 4317:4317 -p 4318:4318 -p 8889:8889 \
-v $(pwd)/otel-collector-config.yaml:/etc/otel/config.yaml \
otel/opentelemetry-collector-contrib:latest \
--config=/etc/otel/config.yaml
Context Propagation
การส่ง trace context ข้าม service ต้องใช้ HTTP header มาตรฐาน เช่น traceparent ตาม W3C Trace Context Specification ซึ่ง SDK จะใส่และอ่าน header เหล่านี้อัตโนมัติเมื่อใช้ auto-instrumentation ผลลัพธ์คือ span ของ service A และ service B จะเชื่อมกันเป็น trace เดียว แม้จะเป็นคนละ process
Best Practices
- Service Name ต้องชัดเจน — ใช้ชื่อที่สั้น เช่น
payment-apiไม่ใช่server - Attribute ระวัง cardinality — ห้ามใส่ user_id หรือ request_id เป็น metric label เพราะจะทำให้ backend overload
- Sampling ก่อนส่ง — ตั้ง head-based sampling ที่ SDK เพื่อลด traffic ไป Collector ใช้ tail-based ที่ Collector เพื่อตัดสินใจจาก trace ทั้งก้อน
- Resource Attributes — ระบุ
service.version,deployment.environmentเสมอ ช่วย filter ได้ละเอียดขึ้น - Collector มี 2 tier — Agent Collector อยู่ข้างแอป (sidecar/DaemonSet), Gateway Collector เป็น central service ที่ส่งต่อ backend
สรุป
OTel เป็นมาตรฐาน Observability ที่รวม traces, metrics และ logs เข้าด้วยกัน ลดการผูกติดกับ vendor ทำให้เปลี่ยน backend ได้ง่าย การ instrument ด้วย auto-instrumentation ช่วยให้เริ่มใช้งานได้รวดเร็วโดยไม่ต้องแก้โค้ดมาก ส่วนการติดตั้ง Collector เป็นตัวกลางช่วยจัดการ routing, batching และ buffering อย่างมีประสิทธิภาพ
แนะนำให้เริ่มจาก traces ก่อนเพราะเห็นผลลัพธ์ชัดเจนที่สุด แล้วค่อยขยายไป metrics และ logs ตามลำดับ การใช้ OTLP protocol และ W3C Trace Context จะช่วยให้ระบบ compatible กับ backend ในอนาคตได้โดยไม่ต้องเปลี่ยนโค้ด

