Exporters & Instrumentation
Prometheus จะ scrape ได้ ก็ต้องมีคนเปิด endpoint /metrics ให้ มี 2 วิธีที่ metric เกิดขึ้น:
- Exporter — โปรแกรมแยกที่แปลง metric จากระบบที่ ไม่รู้จัก Prometheus (Linux, MySQL, Redis) → ฟอร์แมตที่ scrape ได้
- Instrumentation — ฝัง library ใน โค้ดแอปเราเอง เพื่อ expose metric ที่เราสนใจ
Exporters — สำหรับของที่แก้โค้ดไม่ได้
ระบบสำเร็จรูป (OS, database) ไม่ได้พูดภาษา Prometheus เราเลยรัน exporter คู่กับมัน exporter จะอ่านสถานะ (จาก /proc, จาก query ฐานข้อมูล ฯลฯ) แล้วแปลเป็น metric ที่ /metrics
| Exporter | ดึง metric ของ | ตัวอย่าง metric |
|---|---|---|
| node_exporter | เครื่อง Linux (CPU/RAM/disk/net) | node_cpu_seconds_total, node_memory_* |
| cAdvisor | container (Docker/k8s) | container_cpu_usage_seconds_total, container_memory_usage_bytes |
| postgres_exporter | PostgreSQL | pg_stat_*, connection, replication lag |
| redis_exporter | Redis | hit rate, memory, connected clients |
| blackbox_exporter | probe ภายนอก (ping/HTTP/TCP) | endpoint up? latency? cert หมดเมื่อไหร่? |
node_exporter — ตัวที่ต้องรู้จัก
เป็น exporter ที่ใช้บ่อยสุด ให้ metric สำหรับ USE method (บทที่ 11) ครบ:
# CPU: counter วินาทีที่ CPU อยู่ในแต่ละ mode
node_cpu_seconds_total{cpu="0",mode="idle"} 123456.7
node_cpu_seconds_total{cpu="0",mode="user"} 8901.2
# Memory: gauge
node_memory_MemTotal_bytes 16_000_000_000
node_memory_MemAvailable_bytes 4_200_000_000
# Disk / Filesystem
node_filesystem_avail_bytes{mountpoint="/"} 50_000_000_000
node_cpu_seconds_totalเป็น counter ต่อ mode คำนวณ CPU busy % ด้วยการดู "เวลาที่ ไม่ได้ idle":100 - avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100
Instrumentation — วัด business logic ในโค้ดเอง
Exporter ให้ metric ระดับ infra แต่สิ่งที่มีค่าที่สุด (RED ของ endpoint, business metric) ต้องฝังในโค้ดเอง ด้วย client library ของภาษานั้น (Go, Java, Python, Node...)
ตัวอย่าง Node.js ด้วย prom-client — สร้าง RED metrics ครบ:
const express = require("express");
const client = require("prom-client");
const app = express();
const register = new client.Registry();
// เก็บ metric พื้นฐานของ process (CPU, memory, event loop) ให้อัตโนมัติ
client.collectDefaultMetrics({ register });
// R + E: Counter นับ request แยกตาม method/route/status
const httpRequests = new client.Counter({
name: "http_requests_total",
help: "Total HTTP requests",
labelNames: ["method", "route", "status"], // ⚠️ label ต้อง low-cardinality
registers: [register],
});
// D: Histogram วัด latency (มี bucket สำหรับ p95/p99)
const httpDuration = new client.Histogram({
name: "http_request_duration_seconds",
help: "HTTP request duration",
labelNames: ["method", "route"],
buckets: [0.01, 0.05, 0.1, 0.3, 0.5, 1, 3, 5], // วินาที
registers: [register],
});
// middleware วัดทุก request
app.use((req, res, next) => {
const end = httpDuration.startTimer({ method: req.method });
res.on("finish", () => {
const route = req.route?.path ?? "unknown"; // ใช้ route pattern ไม่ใช่ raw url!
httpRequests.inc({ method: req.method, route, status: res.statusCode });
end({ route });
});
next();
});
// endpoint ที่ Prometheus จะมา scrape
app.get("/metrics", async (req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
app.listen(3000);
สังเกต
route = req.route?.pathที่ใช้ route pattern (/users/:id) ไม่ใช่ raw URL (/users/12345) — ถ้าใช้ raw URL, label จะกลายเป็น high-cardinality (ทุก id = 1 series ใหม่) → cardinality explosion (บทที่ 13) นี่คือกับดักที่คนพลาดบ่อยที่สุดตอน instrument จริง
แนวทางตั้งชื่อ metric (naming convention)
Prometheus มี convention ที่ควรทำตามเพื่อให้อ่านง่ายและ query ถูก:
- ใช้
snake_case:http_requests_total - Counter ลงท้าย
_total - ใส่หน่วยในชื่อ base unit:
_seconds,_bytes(ไม่ใช่ ms/MB) - นำหน้าด้วย namespace/subsystem:
myapp_http_requests_total
✅ myapp_http_request_duration_seconds
✅ myapp_db_connections_active
❌ requestTime (camelCase, ไม่มีหน่วย)
❌ latency_ms (ควรเป็น seconds)
Push แทน pull: OpenTelemetry & OTLP
โลกกำลังขยับไปหา OpenTelemetry (บทที่ 41) ที่เป็นมาตรฐานกลาง instrument ครั้งเดียวได้ทั้ง metrics/logs/traces แล้ว export ไปหลายปลายทาง — แต่แนวคิด counter/gauge/histogram และเรื่อง cardinality ที่เรียนมายังใช้ได้เหมือนเดิมทุกอย่าง
สรุป
- Exporter = แปลง metric จากระบบสำเร็จรูป (node_exporter สำหรับเครื่อง, cAdvisor สำหรับ container, *_exporter สำหรับ DB)
- Instrumentation = ฝัง client library ในโค้ดเพื่อวัด RED + business metric เอง
- ตอน instrument: ใช้ route pattern ไม่ใช่ raw URL เป็น label — กัน cardinality explosion
- ตั้งชื่อตาม convention:
snake_case,_total, หน่วย base (_seconds,_bytes) - อนาคตขยับไป OpenTelemetry แต่ concept เดิมยังใช้ได้
ต่อไป: เอา metric ที่ได้มาทำ dashboard ด้วย Grafana