ชนิดของ 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,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 ให้ตื่นเฉพาะเรื่องที่ควรตื่น