📡 Obs · Zero→Hero
หน้าแรก/📈 2 · Metrics/PromQL จากศูนย์
📖 บทเรียน⏱ ~16 นาที

PromQL จากศูนย์

PromQL (Prometheus Query Language) คือภาษาที่ใช้ถามข้อมูลจาก Prometheus มันดูแปลกตาตอนแรก แต่พอเข้าใจ 4-5 concept ก็ใช้ได้ 90% ของงานจริง บทนี้ปูจากศูนย์

ทุก query ในบทนี้ลองพิมพ์เล่นได้ที่ Prometheus UI (http://localhost:9090) หลังทำ lab-metrics-01 หรือใน Grafana Explore — เรียน PromQL ต้องลองพิมพ์เอง

4 ชนิดของค่าที่ query คืนมา

  • Instant vector — ค่า ณ เวลาปัจจุบัน 1 ค่าต่อ series (พบบ่อยสุด)
  • Range vector — ค่าในช่วงเวลา เช่น 5 นาทีย้อนหลัง (ใช้กับ rate()) เขียนด้วย [5m]
  • Scalar — เลขตัวเดียว
  • String — ไม่ค่อยใช้

ระดับ 1: เลือก series (selector)

# ทุก series ของ metric นี้
http_requests_total

# filter ด้วย label (= ตรงตัว)
http_requests_total{status="500"}

# หลาย label + regex (=~ คือ match regex, != คือไม่เท่า)
http_requests_total{status=~"5..", method="POST"}

# range vector: ข้อมูล 5 นาทีย้อนหลัง
http_requests_total[5m]
ตัวดำเนินการ ความหมาย
= เท่ากับพอดี
!= ไม่เท่ากับ
=~ match regex
!~ ไม่ match regex

ระดับ 2: rate() — หัวใจของ counter

จำจากบทที่ 13: ค่าดิบของ counter ไม่มีความหมาย เราต้องการ อัตราการเพิ่ม → ใช้ rate()

# request ต่อวินาที เฉลี่ยในช่วง 5 นาที
rate(http_requests_total[5m])

rate() คำนวณ "เพิ่มขึ้นกี่หน่วยต่อวินาที" โดยเฉลี่ยในหน้าต่างเวลาที่ให้ และฉลาดพอที่จะจัดการตอน counter reset (แอป restart) ให้ด้วย

rate() vs irate(): rate() เฉลี่ยทั้งช่วง (กราฟเนียน เหมาะทำ dashboard/alert) · irate() ใช้แค่ 2 จุดล่าสุด (ไวต่อการเปลี่ยนแปลง เหมาะดู spike แต่กราฟกระตุก) — ใช้ rate() เป็นหลัก

หน้าต่างใน [5m] ต้องกว้างกว่า scrape interval อย่างน้อย ~4 เท่า (เช่น scrape 15s → ใช้ [1m] ขึ้นไป) ไม่งั้นข้อมูลในหน้าต่างน้อยเกินจนคำนวณเพี้ยน

ระดับ 3: aggregation — ยุบหลาย series เป็นภาพรวม

ปกติ metric มีหลาย series (แยกตาม instance, path, ...) เรามักอยากรวม:

# รวม request rate ของทุก series เป็นตัวเลขเดียว
sum(rate(http_requests_total[5m]))

# รวมแล้วแยกตาม status (เก็บ label status ไว้ ยุบตัวอื่น)
sum by (status) (rate(http_requests_total[5m]))

# ยุบทุกอย่างยกเว้น instance
sum without (path, method) (rate(http_requests_total[5m]))

operator รวมกลุ่มที่ใช้บ่อย: sum, avg, max, min, count

by (...) = "เก็บ label พวกนี้ไว้ ยุบที่เหลือ" · without (...) = "ยุบ label พวกนี้ เก็บที่เหลือ" — สองคำนี้คือกุญแจของ PromQL ที่คนใหม่งงบ่อยสุด

ระดับ 4: คำนวณ error rate (RED ในชีวิตจริง)

รวมทุกอย่างข้างบนเป็น query ที่ใช้จริงบ่อยที่สุด — สัดส่วน error:

# error rate = (5xx ต่อวิ) / (ทั้งหมดต่อวิ)
sum(rate(http_requests_total{status=~"5.."}[5m]))
  /
sum(rate(http_requests_total[5m]))

ผลลัพธ์ 0.01 = error 1% ตรงกับ SLO ในบทที่ 12 เป๊ะ

ระดับ 5: percentile จาก histogram

จำ histogram_quantile() จากบทที่ 13 — นี่คือวิธีได้ p95/p99 latency:

# p95 latency (วินาที)
histogram_quantile(
  0.95,
  sum by (le) (rate(http_request_duration_seconds_bucket[5m]))
)
  • le คือ label ของ histogram bucket ("less than or equal") — ต้องเก็บ le ไว้ตอน sum เสมอ
  • เปลี่ยน 0.95 เป็น 0.5 (p50), 0.99 (p99) ได้

Recording Rules — precompute query หนัก

query ซับซ้อนที่ dashboard/alert เรียกบ่อย ควรคำนวณไว้ล่วงหน้าเป็น metric ใหม่ ด้วย recording rule — ช่วยให้ dashboard เร็วขึ้นมาก:

groups:
  - name: api-aggregations
    interval: 30s
    rules:
      - record: job:http_error_rate:ratio5m
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
            / sum(rate(http_requests_total[5m]))

หลังจากนี้ใน dashboard เรียกแค่ job:http_error_rate:ratio5m (มี convention ตั้งชื่อ level:metric:operation)

Cheat sheet ที่ใช้จริง 90%

อยากได้ query
request/วิ รวม sum(rate(http_requests_total[5m]))
error rate % sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
p99 latency histogram_quantile(0.99, sum by(le)(rate(http_request_duration_seconds_bucket[5m])))
CPU ใช้ต่อ instance 100 - (avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
memory ใช้ % (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
target ที่ down up == 0

สรุป

  • ค่าที่คืนมามี instant vector (ตอนนี้) และ range vector ([5m], ใช้กับ rate)
  • rate(counter[5m]) = อัตราต่อวินาที — ใช้เสมอกับ counter, อย่าดูค่าดิบ
  • sum by (label) = รวม series แล้วเก็บ label ที่ต้องการ
  • error rate = sum(rate(5xx)) / sum(rate(total))
  • p99 = histogram_quantile(0.99, sum by(le)(rate(..._bucket[5m])))
  • recording rules precompute query หนักให้ dashboard เร็ว

บทหน้า: จะได้ metric มา query ต้องมี exporters & instrumentation — แหล่งที่มาของ metric ทั้งหมด