microservice ต้องมี observability แค่ไหน ถึงจะคุ้ม
มีเรื่องเล่าในทีมที่ผมเคยทำงานด้วย:
ระบบล่มตอนตี 2 — pager ดัง — dev ตื่นมาเปิด laptop
ใช้เวลา 45 นาทีแกะว่า error เกิดจาก service ไหน
พอรู้ว่าเป็นpayment-service— ใช้เวลาอีก 20 นาทีไล่ว่า request นี้ผ่านauth-service→order-service→payment-service→ แล้วพังที่payment-serviceเพราะinventory-serviceตอบกลับมา 5001 ชั่วโมง 5 นาที — เพื่อรู้ว่า “พังที่ inventory”
ถ้ามี tracing — รู้ใน 2 นาที
นี่คือเหตุผลที่ observability เกิด — แต่มันก็มีราคาที่ต้องจ่าย
observability คืออะไร
ไม่ใช่แค่ monitoring — monitoring บอกว่า “CPU 90%” — observability บอกว่า “CPU 90% เพราะเมื่อกี้มี request จาก order-service มา 10,000 requests ใน 1 นาที — และ 30% ของ requests พังที่ SELECT * FROM orders WHERE...“
3 เสาหลัก:
ถามอะไร ตัวอย่าง 📊 Metrics “เกิดอะไรขึ้น?” CPU 90%, latency 2s, error rate 5% 📜 Logs “เกิดเพราะอะไร?”EROR: connection refused dial tcp 10.0.1.5:5432
🔍 Traces
“ผ่านมาทางไหน?”
gateway → auth → order → payment → inventory
monitoring = metrics — รู้ว่ามีปัญหา
observability = metrics + logs + traces — รู้ว่าปัญหาคืออะไร และเกิดที่ไหน
ทำไม microservice ถึงต้องการ observability มากกว่า monolith
เพราะ request เดียวผ่าน service เยอะ
Monolith:
request → [app] → response
log อยู่ที่เดียว — เปิดไฟล์เดียวเจอ
Microservice:
request → gateway → auth → user → order → payment → inventory → ...
log กระจาย 7 services — ไม่มีทางไล่เองไหว
Enter fullscreen mode Exit fullscreen mode
จำนวน service กับเวลา debug:
จำนวน service เวลาที่ใช้แกะว่า error อยู่ตรงไหน (เฉลี่ย) 1 (monolith) 5-10 นาที 5 15-30 นาที 10 30-60 นาที 20+ “ขอไปชงกาแฟก่อน”นี่คือ exponential growth ของความซับซ้อน — ไม่ใช่ linear
เพราะไม่มีใคร “เห็นภาพรวม”
dev-A: "ผมดู log ของ payment-service แล้ว — มันพังที่ call external API"
dev-B: "แต่ external API มันรับ request จาก payment-service ปกตินะ"
dev-C: "เดี๋ยวนะ — payment-service ใช้ connection pool เก่าที่ถูก reset โดย auth-service"
Enter fullscreen mode Exit fullscreen mode
ทุกคนเห็นแค่สิ่งที่ตัวเองดูแล — ไม่มีใครเห็น flow ทั้งหมด — tracing คือเครื่องมือเดียวที่แก้ปัญหานี้ได้
ต้องแลกด้วยอะไร
เงิน
Datadog ทีม 20 services: ~$2,500/เดือน
Grafana self-host: ~$250/เดือน + engineer time
แต่เงินไม่ใช่ต้นทุนเดียว
เวลาตั้งค่า — และ maintain
สิ่งที่ต้องทำ:
1. ติดตั้ง OpenTelemetry SDK ในทุก service
2. ตั้งค่า collector — รับ data → ส่งไป backend
3. ตั้งค่า backend — Grafana/Prometheus/Datadog
4. สร้าง dashboard — อันนี้ใช้เวลาเยอะที่สุด
5. ตั้ง alert — ไม่งั้นมี dashboard แต่ไม่มีใครดู
6. onboard ทีม — สอนให้ทุกคนใช้เป็น
Enter fullscreen mode Exit fullscreen mode
ขั้นตอน 1-3 ใช้เวลาเป็นวัน ขั้นตอน 4-6 ใช้เวลาเป็นสัปดาห์ ถึงเดือน
Cognitive load
ทุก tool ที่เพิ่ม = ทุกอย่างที่ทีมต้องเรียนรู้และ maintain
ก่อน: รู้แค่ Go + Postgres + Docker
หลัง: รู้ Go + Postgres + Docker + Prometheus + Grafana + Loki + Tempo + OTel + ...
Enter fullscreen mode Exit fullscreen mode
ถ้าทีมมี 3 คน — เพิ่ม observability = เพิ่มภาระคนละ 20-30% — อาจไม่คุ้ม
Performance overhead
OTel SDK เพิ่ม latency ~1-5ms ต่อ request — และกิน memory ~10-50MB ขึ้นอยู่กับ sampling rate
Sampling 100% = เห็นทุก request แต่ performance hit สูง
Sampling 10% = เห็นแค่บางส่วน แต่แทบไม่มี overhead
Storage — ต้นทุนที่คนลืมคิด
traces กับ logs ใช้ storage มหาศาล — และ storage ไม่ได้ฟรี
ตัวอย่างจริง — 20 services, 1,000 req/s, 8 ชม/วัน:
Traces (sampling 100%, เก็บ 7 วัน):
1,000 req/s × 3,600s × 8h = 28.8M requests/วัน
เฉลี่ย 10 spans/request × ~2KB/span = ~576 GB/วัน
เก็บ 7 วัน = ~4 TB
ค่า storage (AWS S3): ~$90/เดือน
ค่า storage (Grafana Cloud): ~$500/เดือน
ค่า storage (Datadog): ~$2,000+/เดือน (คิดตาม ingestion + retention)
Enter fullscreen mode Exit fullscreen mode
และนี่คือ sampling 100% — ทีมส่วนใหญ่ sampling แค่ 10-20% — แต่ก็ยังเป็นตัวเลขที่มีนัยสำคัญ
Traces โตเร็วกว่าที่คิด (self-hosted storage — S3-compatible @ ~$22.50/TB):
เดือนที่ 1: 20 services, เก็บ 7 วัน → 4 TB → $90
เดือนที่ 6: เพิ่ม 10 services, เก็บ 30 วัน → 30 TB → $675
เดือนที่ 12: trace ละเอียดขึ้น, เก็บ 90 วัน → 120 TB → $2,700
Enter fullscreen mode Exit fullscreen mode
traces โตตาม services × retention × detail — ทั้งสามตัวมีแต่เพิ่ม ไม่เคยลด
วิธีคุม cost:
1. Sampling แบบหัว-ท้าย:
- 100% เก็บเฉพาะ entry span (request เข้า)
- 10% เก็บเต็มทุก span
- error 100% เก็บหมด
2. Tail-based sampling:
- ส่งทุก trace เข้า collector
- collector ตัดสินใจเก็บเฉพาะ trace ที่ผิดปกติ (error, latency สูง)
3. ตั้ง retention policy:
- traces: 3-7 วัน (พอ debug incident ล่าสุด)
- metrics: 30-90 วัน (ดู trend)
- logs: 7-14 วัน (debug)
Enter fullscreen mode Exit fullscreen mode
💡 กฏง่าย ๆ: storage cost ของ traces มักจะมากกว่า CPU cost ของ services ที่คุณ monitor — ถ้า traces cost > infra cost — คุณทำอะไรผิดซักอย่าง
แล้วเมื่อไหร่ถึงคุ้ม
คุ้ม — เมื่อ
1. ใช้เวลาดีบั๊ก production เกิน 30 นาทีต่อครั้ง
ถามตัวเอง: incident ครั้งล่าสุด — ใช้เวลาเท่าไหร่กว่าจะรู้ว่าเกิดอะไรขึ้น?
ถ้าตอบว่า “ไม่รู้” หรือ “เกิน 30 นาที” — observability คุ้มแน่
2. มี service >10 ตัว หรือทีม >5 คน
เส้นแบ่งคร่าว ๆ: monolith → observability “nice to have” — 5+ services → “ควรมี” — 10+ services → “ต้องมี”
3. downtime มีผลต่อ revenue
ถ้าทุกนาทีที่ระบบล่ม = เสียเงิน — observability คือประกัน — จ่าย $2K/เดือน ดีกว่าเสีย $50K/นาทีตอนระบบล่ม
ไม่คุ้ม — เมื่อ
1. มี service <5 ตัว — และทีม <3 คน
เปิด log ทีละ service ก็ยังไหว — observability อาจ overkill
2. ยังไม่มี incident บ่อย
ถ้า production ยังนิ่ง — เงินก้อนนั้นไปเพิ่ม feature ดีกว่า
3. กำลัง validate product-market fit
startup ระยะแรก — metrics สำคัญกว่า observability — รู้ว่า revenue เท่าไหร่สำคัญกว่ารู้ว่า request ผ่านกี่ hop
เริ่มยังไง — แบบไม่เจ็บ
อย่าเริ่มด้วย “ลง observability platform” — เริ่มด้วยการถาม:
“incident ครั้งล่าสุด — อะไรคือคำถามที่เราใช้เวลาหาคำตอบนานที่สุด?”
ระดับ 1: Structured Logging (ฟรี ใช้เวลา 1 วัน)
// แทนที่จะ log แบบนี้
log.Printf("user %s login failed", userID)
// log แบบนี้
slog.Error("login failed",
slog.String("user_id", userID),
slog.String("reason", "invalid_password"),
slog.String("trace_id", traceID),
)
Enter fullscreen mode Exit fullscreen mode
trace_id คือกุญแจ — มันเชื่อม log ทุก service ที่ request นี้ผ่าน
💡 Level 1 ใช้ request-scoped ID (เช่น UUID จาก middleware) — ยังไม่ใช่ OTel trace ID จริง — Level 2 จะแทนที่ด้วย trace_id จาก span context ที่เชื่อมข้าม service ได้จริง
ระดับ 2: Tracing (ฟรี ค่อยเป็นค่อยไป)
เริ่มจากเพิ่ม span ในแต่ละ function:
import "go.opentelemetry.io/otel"
func GetUser(ctx context.Context, id string) (*User, error) {
ctx, span := otel.Tracer("user-service").Start(ctx, "GetUser")
defer span.End()
// ... โค้ดเดิม
}
Enter fullscreen mode Exit fullscreen mode
แต่นั่นทำให้เห็นแค่ span ภายใน process เดียว — ยังไม่เห็น flow ข้าม service
สิ่งที่ขาด — trace context propagation:
// HTTP client — inject traceparent header ให้เรียกข้าม service
import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
client := &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}
resp, _ := client.Get("http://payment-service/charge") // trace_id ตามไปอัตโนมัติ
// HTTP server — extract traceparent จาก request ขาเข้า
handler := otelhttp.NewHandler(mux, "order-service")
Enter fullscreen mode Exit fullscreen mode
ถ้า order-service ใช้ otelhttp.NewTransport ตอนเรียก payment-service — span ของทั้งสอง service จะถูก link เป็น trace เดียวกัน — นี่คือสิ่งที่ทำให้ “รู้ใน 2 นาที” จากบทนำเป็นจริง
การเพิ่ม span ใช้ ~3 บรรทัดต่อ function — otelhttp.NewTransport/NewHandler ใช้ 1 บรรทัด — ได้ tracing ทั้งระบบ โดยไม่ต้องเขียน propagation logic เอง
💡 หลักการเดียวกับ PII ใน log — ใช้กับ span attributes ด้วย: ห้ามใส่ email, user_id, order_id เป็น span tag — ใช้ trace_id + log correlation แทน
ระดับ 3: Dashboard + Alert (เริ่มมีค่าใช้จ่าย)
เริ่มจาก Grafana Cloud Free → ดู metrics + traces → ตั้ง alert เฉพาะ critical path
สรุป — คุ้มไหม
monolith → observability = nice to have
microservice → observability = requirement ไม่ใช่ feature
Enter fullscreen mode Exit fullscreen mode
แต่ requirement ไม่ได้แปลว่าต้องทำเต็มรูปแบบ — เริ่มจาก structured logging + tracing (ฟรี, ใช้เวลาไม่นาน) — แล้วค่อยขยับไป dashboard + alert เมื่อเริ่มรู้สึกว่า “หา bug นานเกินไป”
และที่สำคัญ: อย่าตั้ง budget observability จากการ copy-paste plan ของทีม 300 คน — ทีม 5 คนต้องการ observability คนละแบบกับทีม 50 คน
📚 อ่านต่อ:
- OpenTelemetry Go — tracing SDK
- Grafana Cloud Free — เริ่มต้น observability ฟรี
답글 남기기