PromQL (Prometheus Query Language) เป็นหัวใจของการใช้ Prometheus อย่างมีประสิทธิภาพ — ความสามารถในการเขียน query ที่ตรงจุดและมีประสิทธิภาพจะเปลี่ยนกองข้อมูล metric จำนวนมหาศาลให้กลายเป็น insight ที่ใช้ตัดสินใจได้ บทความนี้จะพาทำความเข้าใจ PromQL ตั้งแต่พื้นฐานของ data model, ประเภทข้อมูลที่รองรับ, selector, จนถึงฟังก์ชันที่ใช้บ่อยในงานจริง
ก่อนเริ่มเขียน query ต้องเข้าใจว่า Prometheus เก็บข้อมูลเป็น time series แต่ละ series ระบุด้วยชื่อ metric และ label set ที่แตกต่างกัน การ query คือการเลือก series ที่สนใจและประมวลผลกับข้อมูลย้อนหลัง
Data Types ใน PromQL
PromQL มีประเภทข้อมูล 4 แบบหลัก ซึ่งฟังก์ชันและ operator แต่ละตัวจะรับและคืนค่าประเภทต่างกัน:
- Instant vector: ชุดของ time series ที่มีค่า 1 ค่า ณ จุดเวลาเดียว เช่น
up - Range vector: ชุดของ time series ที่มีค่าหลายค่าในช่วงเวลา เช่น
http_requests_total[5m] - Scalar: ตัวเลข floating-point ตัวเดียว เช่น
3.14 - String: สตริง ใช้น้อยมากใน query ส่วนใหญ่เจอในฟังก์ชัน label manipulation
Selector พื้นฐาน
Selector คือวิธีเลือก time series ที่ต้องการ โดยระบุชื่อ metric พร้อมเงื่อนไข label matcher:
# เลือกทุก series ของ metric http_requests_total
http_requests_total
# filter ด้วย label equal
http_requests_total{job="api", status="200"}
# label not equal
http_requests_total{status!="500"}
# regex match
http_requests_total{path=~"/api/.*"}
# regex not match
http_requests_total{path!~"/healthz"}
Matcher มี 4 แบบ: =, !=, =~, !~ — สองตัวหลังใช้ RE2 regex เช่นเดียวกับภาษา Go ที่ Prometheus เขียน
Range Vector และ Offset
# ข้อมูล 5 นาทีที่ผ่านมา
http_requests_total[5m]
# ข้อมูล 1 ชั่วโมงก่อนหน้า จากจุดปัจจุบัน
http_requests_total offset 1h
# รวมกัน — 5m range, ย้อนหลัง 1h
http_requests_total[5m] offset 1h
# หน่วยที่รองรับ: ms, s, m, h, d, w, y
rate(http_requests_total[1h])
ฟังก์ชัน rate และ irate
rate() เป็นฟังก์ชันที่ใช้บ่อยที่สุดสำหรับ counter — คำนวณอัตราการเพิ่มขึ้นต่อวินาที ในช่วงเวลาที่กำหนด และจัดการ counter reset ให้อัตโนมัติ:
# อัตราคำขอต่อวินาที เฉลี่ยใน 5 นาที
rate(http_requests_total[5m])
# irate ใช้ 2 point ล่าสุด — sensitive ต่อ spike
irate(http_requests_total[5m])
# increase คือจำนวนที่เพิ่มขึ้นรวม (ไม่หารเวลา)
increase(http_requests_total[1h])
กฎสำคัญ: rate() ต้องใช้กับ counter เท่านั้น (ค่าเพิ่มอย่างเดียว) ห้ามใช้กับ gauge และ range ควรมีอย่างน้อย 4 sample เพื่อให้ผลแม่นยำ — โดยทั่วไปใช้ 2-4 เท่าของ scrape interval
Aggregation Operators
Aggregation ใช้รวมค่าข้าม time series หลายตัว โดยแบ่งกลุ่มตาม label:
# ผลรวม request rate ทั้งหมด
sum(rate(http_requests_total[5m]))
# group by job
sum by (job) (rate(http_requests_total[5m]))
# without — group ทุก label ยกเว้น instance
sum without (instance) (rate(http_requests_total[5m]))
# operator อื่น ๆ
avg by (job) (rate(http_requests_total[5m]))
max by (region) (node_memory_MemFree_bytes)
min(up == 1)
count(up == 1)
stddev(rate(http_requests_total[5m]))
topk(3, rate(http_requests_total[5m]))
bottomk(3, node_load5)
quantile(0.95, rate(http_requests_total[5m]))
by และ without เป็นสองวิธีที่ทำงานตรงข้ามกัน — by คือเก็บเฉพาะ label ที่ระบุ ส่วน without คือตัด label ที่ระบุทิ้ง
CPU Usage
# CPU usage % (100 - idle%)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
Memory Usage
# Memory used %
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
Disk Free
# Disk free % root partition
(node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100
HTTP Error Rate
# 5xx error rate
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
Latency p95
# 95th percentile latency from histogram
histogram_quantile(0.95,
sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
Operator ทางคณิตศาสตร์
- เลขคณิต:
+,-,*,/,%,^ - เปรียบเทียบ:
==,!=,>,<,>=,<= - ตรรกะ:
and,or,unless
# หา instance ที่ RAM เหลือน้อยกว่า 10%
(node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 < 10
# filter series ที่ up แต่ไม่มี disk พอ
up == 1 unless node_filesystem_avail_bytes < 1e9
ฟังก์ชันที่มีประโยชน์
| Function | ประโยชน์ |
|---|---|
rate(), irate(), increase() | คำนวณอัตรา counter |
delta(), idelta() | คำนวณผลต่างของ gauge |
deriv(), predict_linear() | หาอัตราเปลี่ยนและทำนายค่าอนาคต |
abs(), ceil(), floor(), round() | คำนวณทางคณิตศาสตร์ |
histogram_quantile() | หา percentile จาก histogram |
time(), timestamp() | ใช้กับ time-based query |
label_replace(), label_join() | แก้ไข label ใน runtime |
absent() | ตรวจ metric ที่หายไป (alert series missing) |
Best Practices
- Range ใน
rate()ควรเป็น 2-4 เท่าของ scrape interval เช่น scrape ทุก 15s ใช้[1m]ถึง[2m] - หลีกเลี่ยง query ที่ต้อง aggregate ข้าม time series จำนวนมากโดยไม่มี filter — ช้าและกิน memory
- ใช้
rate()ก่อน aggregation เสมอ —sum(rate(...))ไม่ใช่rate(sum(...)) - ทดสอบ query บน
/graphUI ก่อนเอาไปใส่ dashboard หรือ alert rule - ถ้า query ใช้บ่อยมาก เช่นใน alert rule ควรใช้ recording rule ประมวลผลล่วงหน้า
- อย่าใช้
by (instance)กับ counter ที่รีเซ็ตบ่อย (เช่น pod restart) จะทำให้ rate ผิดเพี้ยน
สรุป
PromQL เป็นภาษาที่ทรงพลังสำหรับการ query metric ใน Prometheus การเข้าใจ instant vector, range vector, selector, และ aggregation operator เป็นพื้นฐานที่นำไปสู่การเขียน query ที่แม่นยำและมีประสิทธิภาพ — ช่วยให้ dashboard และ alert ให้ข้อมูลที่ถูกต้องและทันต่อเหตุการณ์
เมื่อเชี่ยวชาญพื้นฐานแล้ว ขั้นต่อไปคือการเขียน query ขั้นสูง เช่น subquery, join ข้าม metric, และเทคนิคจัดการ high-cardinality ที่จะช่วยให้ใช้ Prometheus ได้ลึกซึ้งยิ่งขึ้น

