คู่มือ Observability แบบรวมศูนย์ - Traces, Metrics, Logs ใน unified pipeline
OpenTelemetry ใช้ Collector เป็นศูนย์กลางในการรับข้อมูลจากแอปพลิเคชันและส่งต่อไปยัง Backend ต่างๆ
OTLP (OpenTelemetry Protocol): Protocol ที่ใช้ส่งข้อมูลระหว่าง Application → Collector และ Collector → Backend
OpenTelemetry คือมาตรฐาน Observability แบบ Open Source จาก CNCF (Cloud Native Computing Foundation) ที่รวม OpenTracing และ OpenCensus เข้าด้วยกัน
ปี 2025, OpenTelemetry ได้กลายเป็น de facto standard สำหรับการมอนิเตอร์ระบบ โดยเฉพาะใน Multi-Cloud Environment เพราะเป็น Vendor Neutral ไม่ต้องผูกติดกับผู้ให้บริการ cloud ใดผู้ให้บริการหนึ่ง
OTel Collector สามารถรันได้หลายแบบ: Docker, Kubernetes, หรือติดตั้งโดยตรงบน Server แนะนำใช้ Docker สำหรับ development และ prototype
# สร้างไฟล์ docker-compose.yml
mkdir otel-demo && cd otel-demo
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
otel-collector:
image: otel/opentelemetry-collector:0.106.1
command: ["--config=/etc/otel-collector-config.yaml", "--feature-gates=-enableOTLPReceiver"]
ports:
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
- "55679:55679" # zPages: diagnostics & debugging
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
- ./data:/data
networks:
- otel-network
# Sample application
sample-app:
image: otel/java-otel-demo-app:latest
depends_on:
- otel-collector
environments:
- OTEL_EXPORTER_OTLPEndpoint=http://otel-collector:4317
- OTEL_EXPORTER_OTLPProtocol=grpc
networks:
- otel-network
networks:
otel-network:
driver: bridge
EOF
- image: ใช้ Image อย่างเป็นทางการจาก OTel รุ่น 0.106.1 (ล่าสุดเมื่อ 2025)
- ports 4317: OTLP gRPC port (แนะนำสำหรับ production)
- ports 4318: OTLP HTTP port (สำหรับ client ที่ไม่รองรับ gRPC)
- ports 55679: zPages port (ดู diagnostics, debugging interface)
- OTEL_EXPORTER_OTLPEndpoint: URL ของ Collector ที่ Application จะส่งข้อมูลไป
Configuration ของ OTel Collector แบ่งเป็น 4 ส่วนหลัก:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
send_batch_size: 10000
timeout: 10s
memory_limiter:
check_interval: 1s
limit_mib: 4000
spike_limit_mib: 500
exporters:
logging:
loglevel: debug
otlp:
endpoint: backend:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [logging, otlp]
metrics:
receivers: [otlp]
processors: [batch, memory_limiter]
exporters: [logging]
logs:
receivers: [otlp]
processors: [batch]
exporters: [logging]
grpc endpoint:4317 - รับ OTLP ผ่าน gRPC (ประสิทธิภาพสูง)http endpoint:4318 - รับ OTLP ผ่าน HTTP (รองรับ client หลายแบบ)batch - รวมข้อมูลหลายชุดก่อนส่ง (ลด network overhead)memory_limiter - ป้องกันCollector ใช้หน่วยความจำมากเกินไป (OOM)send_batch_size:10000 - ส่งข้อมูลทุก 10,000 ชุดtimeout:10s - ส่งข้อมูลทุก 10 วินาที (ไม่ถึง 10,000 ก็ส่ง)limit_mib:4000 - จำกัดหน่วยความจำ 4GBlogging - แสดง log ที่ Console (debug)otlp - ส่งต่อไป Collector อื่นหรือ Backendเพิ่ม Backend services ใน docker-compose.yml เพื่อเก็บข้อมูลที่ Collector ส่งมา
# เพิ่มใน docker-compose.yml (ส่วน exporters)
exporters:
otlp:
endpoint: jaeger:4317
tls:
insecure: true
otlp/prometheus:
endpoint: prometheus:9090
tls:
insecure: true
otlp/loki:
endpoint: loki:3100
tls:
insecure: true
# Backend services
services:
#Jaeger (tracing backend)
jaeger:
image: jaegertracing/all-in-one:1.58
ports:
- "16686:16686" # Jaeger UI
- "14268:14268" # Collector
- "14250:14250" # gRPC
networks:
- otel-network
# Prometheus (metrics backend)
prometheus:
image: prom/prometheus:v2.54.0
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
networks:
- otel-network
# Loki (logs backend)
loki:
image: grafana/loki:3.2.1
ports:
- "3100:3100"
networks:
- otel-network
# Grafana (visualization)
grafana:
image: grafana/grafana:11.4.0
ports:
- "3000:3000"
volumes:
- grafana-storage:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_AUTH_DISABLE_INITIAL_ADMIN_CREATION=true
networks:
- otel-network
depends_on:
- prometheus
- loki
volumes:
grafana-storage:
networks:
otel-network:
| บริการ | พอร์ต | คำอธิบาย |
|---|---|---|
| OTel Collector (gRPC) | 4317 | .Standard รับข้อมูลจาก Application |
| OTel Collector (HTTP) | 4318 | Alternative รองรับ client ที่ไม่-support gRPC |
| Jaeger UI | 16686 | ดู tracing data ใน Web UI |
| Prometheus | 9090 | Query metrics และ alerts |
| Grafana | 3000 | Visualize dashboards |
| Loki | 3100 | Receive logs และ query logs |
เพิ่ม OTel SDK ให้กับ Application เพื่อส่ง tracing, metrics, logs ไปยัง Collector
pip install opentelemetry-distro
pip install opentelemetry-exporter-otlp
import os
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
# Set OTLP endpoint
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://otel-collector:4317"
os.environ["OTEL_EXPORTER_OTLP_PROTOCOL"] = "grpc"
# Initialize tracer
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
# Export to console for debugging
trace.get_tracer_provider().add_span_processor(
SimpleSpanProcessor(ConsoleSpanExporter())
)
npm install @opentelemetry/api
npm install @opentelemetry/sdk-node
npm install @opentelemetry/exporter-trace-otlp-grpc
const { NodeTracerProvider } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const provider = new NodeTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'my-app',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
}),
});
const exporter = new OTLPTraceExporter({
url: 'http://otel-collector:4317', // OTLP/gRPC endpoint
});
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
provider.register();
console.log('OTel initialized! Tracing started...');
OTel มี auto-instrumentation สำหรับ framework ต่างๆ ที่ไม่ต้องแก้โค้ด:
# Node.js - Express auto-instrumentation
node --require/@opentelemetry/instrumentation-express/build/src/index.js app.js
# Python - Flask auto-instrumentation
opentelemetry-instrument --traces_exporter otlp --service_name myapp python app.py
# Java - JAR-based instrumentation
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=my-java-app \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-jar app.jar
สร้าง span แบบ custom เพื่อวัดเวลาของการดำเนินการเฉพาะ:
from flask import Flask, request
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
# Setup tracer
trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
SimpleSpanProcessor(OTLPSpanExporter(endpoint="http://otel-collector:4317"))
)
tracer = trace.get_tracer(__name__)
app = Flask(__name__)
@app.route('/api/process')
def process_data():
with tracer.start_as_current_span("process-data") as span:
# Add attributes to span
span.set_attribute("http.method", request.method)
span.set_attribute("http.url", request.path)
span.set_attribute("user_id", "12345")
# Your business logic here
result = expensive_operation()
# Set status
span.set_status(Status(StatusCode.OK))
return {"status": "success", "result": result}
| Attribute | ประเภท | คำอธิบาย |
|---|---|---|
| span.kind | string | ภายใน:server, client, producer, consumer |
| http.method | string | GET, POST, PUT, DELETE |
| http.status_code | int | 200, 404, 500, etc. |
| error | bool | มี error หรือไม่ (true/false) |
| db.statement | string | SQL query statement |
from flask import Flask, request
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
# Setup meter
metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter(endpoint="http://otel-collector:4317"))
meter_provider = MeterProvider(metric_readers=[metric_reader])
metrics.set_meter_provider(meter_provider)
meter = metrics.get_meter(__name__)
# Create counters
request_counter = meter.create_counter(
name="http_requests_total",
description="Total number of HTTP requests",
unit="1"
)
error_counter = meter.create_counter(
name="http_errors_total",
description="Total number of HTTP errors",
unit="1"
)
# Create gauge for current requests
current_requests_gauge = meter.create_up_down_counter(
name="http_requests_inflight",
description="Number of requests currently in flight",
unit="1"
)
app = Flask(__name__)
@app.route('/api/data')
def get_data():
current_requests_gauge.add(1, {"endpoint": "/api/data"})
try:
request_counter.add(1, {"endpoint": "/api/data", "method": "GET"})
# Process request...
return {"data": "result"}
except Exception as e:
error_counter.add(1, {"endpoint": "/api/data", "error": str(e)})
raise
finally:
current_requests_gauge.add(-1, {"endpoint": "/api/data"})
| Type | ใช้ทำอะไร | คุณสมบัติ |
|---|---|---|
| Counter | นับจำนวน (requests, errors) | เพิ่มได้อย่างเดียว (never decreases) |
| UpDownCounter | ขนาด queue, active connections | ขึ้นหรือลงได้ (both + and -) |
| Histogram | response time, latency | แจกแจงค่าเป็น histogram/bucket |
| Gauge | cpu usage, memory, temperature | ค่าปัจจุบันเฉพาะเวลา |
วัดประสิทธิภาพของระบบสั่งซื้อสินค้า ติดตามเส้นทางการสั่งซื้อจากลูกค้าจนถึงการจัดส่ง
ตรวจสอบความเสถียรของระบบ Hospital Information System (HIS)
มอนิเตอร์ระบบการเงิน ตรวจสอบ transaction แบบ real-time
ใช้ OpenTelemetry เป็น unified pipeline สำหรับ AWS, Azure, GCP
# 1. ตรวจสอบว่า Collector รันอยู่หรือไม่
docker ps | grep otel-collector
# 2. ตรวจสอบ port ที่เปิดอยู่
netstat -tlnp | grep 4317
# หรือ
ss -tlnp | grep 4317
# 3. ทดสอบเชื่อมต่อกับ Collector
telnet localhost 4317
# หรือ
curl -v http://localhost:4318
# 4. ดู logs ของ Collector
docker logs otel-collector
# 5. เพิ่ม logging exporter เพื่อดูว่า Collector ได้ข้อมูลหรือไม่
exporters:
logging:
loglevel: debug
ตรวจสอบว่า port 4317, 4318 ถูก allowed ใน security group/firewall
Application และ Collector อยู่คนละ network (ตรวจสอบ DNS หรือ IP)
Application ส่ง HTTP แต่ Collector รับแต่ gRPC (หรือ ngượcกัน)
Collector ใช้หน่วยความจำสูงจนเกิด OOM (Out of Memory) หรือ slow response
processors:
# Memory Limiter - จำกัดหน่วยความจำ
memory_limiter:
check_interval: 1s
limit_mib: 4000 # จำกัด 4GB (ปรับตาม RAM ที่มี)
spike_limit_mib: 500 # ยอมให้กระโดดสูงสุด 500MB
# Batch Processor - รวมข้อมูลก่อนส่ง
batch:
send_batch_size: 10000 # รวมกี่ชุดก่อนส่ง
timeout: 10s # timeout 10s (ไม่ถึง 10k ก็ส่ง)
คำแนะนำ: ค่า limit_mib ควรตั้งที่ 50-70% ของ RAM ที่มี
เช่น มี RAM 8GB ให้ตั้ง limit_mib: 5000 (5GB)
ข้อมูลส่งไป Collector ช้า หรือมี packet loss
gRPC มี overhead น้อยกว่า HTTP/JSON มาก
เพิ่ม send_batch_size เพื่อลดจำนวน network call
ใช้ sidecar pattern: รัน Collector บน container เดียวกับ Application
Config exporter ให้รีใช้ connection
Collector ไม่ยอมรับข้อมูลจาก application เนื่องจาก authentication
# OTel Collector: เปิด authentication
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
tls:
cert_file: /path/to/cert.pem
key_file: /path/to/key.pem
http:
endpoint: 0.0.0.0:4318
auth:
authenticator:Basicauth
# Client: ส่ง credentials
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic dXNlcm5hbWU6cGFzc3dvcmQ="
# หรือใน Python
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
exporter = OTLPSpanExporter(
endpoint="http://collector:4317",
headers=(("authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ="),)
)
A: OpenTelemetry คือ unified project ที่รวม OpenTracing และ OpenCensus เข้าด้วยกันในปี 2021 ปัจจุบัน OpenTracing และ OpenCensus ไม่มีการพัฒนาแล้ว แนะนำให้ย้ายมาใช้ OpenTelemetry
A: ใช้ SDK ได้โดยตรงโดยไม่ผ่าน Collector (direct export)
แต่ Collector มีข้อดี:
• ลด load ของ application (export บน process แยก)
• สามารถ aggregation และ preprocessing ได้
• รองรับ multiple export targets
• จัดการ authentication ได้ที่ Collector
• เป็น approach มาตรฐานของ CNCF
A: OTLP (OpenTelemetry Protocol) เป็น protocol มาตรฐานของ OpenTelemetry
ที่ใช้ส่งข้อมูลระหว่าง Application → Collector และ Collector → Backend
ข้อดีของ OTLP:
A: ทั้งสามเป็น pillars ของ observability:
OpenTelemetry คือมาตรฐาน Observability ที่มาแรงที่สุดในปี 2025 จาก CNCF ด้วยความเป็น Vendor Neutral ทำให้ไม่ต้องผูกติดกับผู้ให้บริการ cloud ใดผู้ให้บริการหนึ่ง
การเริ่มต้นกับ OpenTelemetry ไม่ยาก ควรเริ่มจาก:
1. ติดตั้ง OTel Collector
2. ส่งข้อมูลจาก Application
3. เลือก Backend ที่ต้องการใช้