📡 Obs · Zero→Hero
หน้าแรก/🎯 1 · แนวคิดหลัก/ชนิดของ Metric & Cardinality
📖 บทเรียน⏱ ~13 นาที

ชนิดของ Metric & Cardinality

ก่อนจะลง Prometheus ต้องเข้าใจก่อนว่า metric มี 4 ชนิด และแต่ละชนิดใช้ต่างกัน + กับดักที่ทำให้ระบบ monitoring ล่ม (cardinality explosion) ที่มือใหม่โดนกันทุกคน

กายวิภาคของ metric หนึ่งตัว

metric ใน Prometheus หน้าตาแบบนี้:

http_requests_total{method="POST", path="/checkout", status="200"}  1547
└────────┬────────┘ └──────────────────┬───────────────────────┘  └─┬─┘
      ชื่อ metric                    labels (มิติ)                   ค่า
  • ชื่อ — บอกว่าวัดอะไร
  • labels — มิติที่ใช้แบ่งย่อย/filter (method, path, status)
  • ค่า — ตัวเลข ณ เวลานั้น

แต่ละชุดค่า label ที่ไม่ซ้ำกัน = 1 time series — จำประโยคนี้ไว้ เดี๋ยวมันจะสำคัญมากตอนพูดเรื่อง cardinality

4 ชนิดของ metric

1. Counter — ตัวนับที่มีแต่เพิ่ม

ค่าที่เพิ่มขึ้นอย่างเดียว (หรือ reset เป็น 0 ตอน restart) ใช้กับสิ่งที่ "นับสะสม":

  • จำนวน request ทั้งหมด
  • จำนวน error ทั้งหมด
  • จำนวน byte ที่ส่งไป
http_requests_total           1000, 1005, 1012, ...(ขึ้นเรื่อยๆ)

อย่าดูค่าดิบของ counter — ตัวเลข "1,547,203" ไม่มีความหมาย! สิ่งที่มีความหมายคือ อัตราการเพิ่ม ซึ่งได้จากฟังก์ชัน rate() → "ตอนนี้ 50 request/วินาที" นี่คือเหตุผลที่ counter มักลงท้ายด้วย _total

2. Gauge — ค่าที่ขึ้นลงได้

ค่าที่เพิ่มหรือลดก็ได้ สะท้อนสถานะ ณ ขณะนั้น:

  • อุณหภูมิ, CPU %, memory ที่ใช้
  • จำนวน connection ที่เปิดอยู่ตอนนี้
  • ความยาว queue ตอนนี้
memory_used_bytes            → 500M, 480M, 520M, 495M ...(ขึ้นๆลงๆ)

Counter vs Gauge จำง่ายๆ: counter = "นับมาแล้วทั้งหมด" · gauge = "ตอนนี้เท่าไหร่"

3. Histogram — กระจายค่าเป็นช่วง (สำคัญมากสำหรับ latency)

Histogram แบ่งค่าเป็น bucket (ช่วง) แล้วนับว่ามีกี่ค่าตกในแต่ละช่วง เหมาะกับ latency/ขนาด:

http_request_duration_seconds_bucket{le="0.1"}   950   ← ≤100ms: 950 req
http_request_duration_seconds_bucket{le="0.3"}   990   ← ≤300ms: 990 req
http_request_duration_seconds_bucket{le="1.0"}   998   ← ≤1s:    998 req
http_request_duration_seconds_bucket{le="+Inf"} 1000   ← ทั้งหมด: 1000 req

พลังของมันคือ คำนวณ percentile ได้ฝั่ง query ด้วย histogram_quantile():

# p95 latency จาก histogram
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

ที่ latency ต้องใช้ histogram เพราะเราอยาก aggregate หลาย instance แล้วยังคำนวณ p99 รวมได้ถูกต้อง — ซึ่งทำกับค่าเฉลี่ยไม่ได้ (เฉลี่ยของ p99 ≠ p99 ของทั้งระบบ)

4. Summary — percentile ที่คำนวณฝั่ง client

คล้าย histogram แต่คำนวณ quantile ไว้ตั้งแต่ในแอปเลย ข้อเสียคือ aggregate ข้าม instance ไม่ได้ จึงนิยมน้อยกว่า histogram ในระบบกระจาย

Histogram Summary
คำนวณ percentile ฝั่ง query (ยืดหยุ่น) ฝั่ง client (fix ไว้)
aggregate หลาย instance ✅ ได้ ❌ ไม่ได้
แนะนำ ✅ ใช้ตัวนี้เป็นหลัก เฉพาะกรณี

Cardinality — กับดักที่ทำระบบล่ม

Cardinality = จำนวน time series ที่ไม่ซ้ำกันทั้งหมด และมันคือ ต้นทุนหลัก ของระบบ metric

จำที่บอกไว้ตอนต้น: ทุกชุดค่า label ที่ต่างกัน = 1 time series ดังนั้น cardinality = ผลคูณของจำนวนค่าที่เป็นไปได้ของทุก label:

http_requests_total{method, status, path}

method: 5 ค่า (GET, POST, PUT, DELETE, PATCH)
status: 6 ค่า (200, 201, 400, 404, 500, 503)
path:   20 ค่า
──────────────────────────────────
= 5 × 6 × 20 = 600 time series  ✅ สบายๆ

แต่พอใส่ label ที่มีค่าไม่จำกัด (unbounded) หายนะมาเยือน:

http_requests_total{method, status, path, user_id}

... × user_id: 1,000,000 ค่า
──────────────────────────────────
= 600 × 1,000,000 = 600,000,000 time series  💥 ระเบิด!

ห้ามใส่ label ที่มี cardinality สูง/ไม่จำกัดเด็ดขาด เช่น user_id, email, request_id, trace_id, ip_address, timestamp, full_url การทำแบบนี้เรียก cardinality explosion — มันทำให้ Prometheus กิน RAM จนตาย, query ช้า, และค่า storage พุ่ง นี่เป็นสาเหตุอันดับ 1 ที่ระบบ monitoring ล่ม

กฎเลือก label

ใส่เป็น label ได้ — ค่ามีจำกัดและรู้ล่วงหน้า:

  • method (5-7 ค่า), status_code (กลุ่ม), endpoint (จำกัด), region, environment, service

ห้ามใส่เป็น label — ค่าไม่จำกัด:

  • user_id, session_id, order_id, email, ip, raw url ที่มี query params

ข้อมูล cardinality สูงพวก user_id, trace_id ให้เก็บใน logs หรือ traces แทน — นั่นคือหน้าที่ของสองเสานั้น! metrics เอาไว้ดูภาพรวม (low cardinality), logs/traces เอาไว้เจาะรายตัว (high cardinality) จำหลักนี้ไว้แล้วคุณจะไม่พังระบบ

สรุป

  • 4 ชนิด: Counter (เพิ่มอย่างเดียว, ใช้ rate()), Gauge (ขึ้นลงได้), Histogram (latency/percentile), Summary (percentile ฝั่ง client)
  • latency → ใช้ Histogram เสมอ เพราะ aggregate + คำนวณ p99 ได้
  • Cardinality = จำนวน time series = ผลคูณของค่า label ทั้งหมด = ต้นทุนหลัก
  • ห้ามใส่ label ที่ค่าไม่จำกัด (user_id, trace_id, ip) → cardinality explosion → ระบบล่ม
  • ข้อมูลรายตัว → เก็บใน logs/traces ไม่ใช่ metrics

บทหน้า (บทสุดท้ายของ concept) — ปรัชญาการตั้ง alert ให้ตื่นเฉพาะเรื่องที่ควรตื่น