1. PromQL β Query Dasar
π‘ Tips
PromQL adalah bahasa query untuk Prometheus. Gunakan Grafana Explore untuk test query sebelum masuk ke dashboard.
Instant Vector (nilai saat ini)
# Semua nilai metrik
http_requests_total
# Filter label
http_requests_total{job="api-server"}
http_requests_total{method="GET", handler="/api/v1"}
http_requests_total{status=~"5.."} # Regex match
http_requests_total{handler!~"/admin.*"} # Regex not match
Range Vector (banyak nilai dalam rentang waktu)
# Rate: per detik (counter)
rate(http_requests_total[5m])
# Per-second rate dari gauge
deriv(cpu_temperature[5m])
# Increase: total kenaikan dalam range
increase(http_requests_total[1h])
Fungsi Aggregasi
# Sum total requests
sum(http_requests_total)
# Sum per label
sum by (method) (rate(http_requests_total[5m]))
sum without (instance) (http_requests_total)
# Rata-rata
avg(node_cpu_seconds_total)
avg by (mode) (rate(node_cpu_seconds_total[5m]))
# Min / Max
min(node_memory_MemFree_bytes)
max by (instance) (rate(http_requests_total[5m]))
# Count
count(up == 1)
count by (job) (up)
# Top/Bottom N
topk(5, rate(http_requests_total[5m]))
bottomk(3, node_memory_MemFree_bytes)
2. PromQL β Query Lanjutan
Histogram Quantile
# P95 latency
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# P99 latency per handler
histogram_quantile(0.99,
sum by (handler, le) (rate(http_request_duration_seconds_bucket[5m]))
)
# P50 (median)
histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))
Math Operations
# CPU usage percentage
100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))
# Memory usage percentage
100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)
# Disk usage percentage
100 * (1 - node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})
# Error rate
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) * 100
# Request per second by service
sum by (job) (rate(http_requests_total[5m]))
Fungsi Waktu
# Offset: bandingkan dengan sebelumnya
http_requests_total offset 1h # 1 jam lalu
rate(http_requests_total[5m] offset 1d) # 1 hari lalu
# Time-based functions
changes(node_boot_time_seconds[24h]) # Berapa kali berubah
resets(http_requests_total[1h]) # Counter resets
absent(up{job="api"}) # Cek metrik hilang
absent_over_time(up{job="api"}[5m]) # Cek hilang dalam range
# Clamp & clamp_min / clamp_max
clamp_min(node_temperature, 0)
clamp_max(node_temperature, 100)
# Predict linear (prediksi 1 jam ke depan)
predict_linear(node_filesystem_avail_bytes[6h], 3600)
Tabel PromQL Populer
| Kebutuhan | Query PromQL |
| Request/detik | sum(rate(http_requests_total[5m])) |
| Error rate (%) | sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 |
| P99 latency | histogram_quantile(0.99, sum by(le)(rate(http_request_duration_seconds_bucket[5m]))) |
| CPU usage % | 100*(1-avg by(instance)(rate(node_cpu_seconds_total{mode="idle"}[5m]))) |
| Memory usage % | 100*(1-node_memory_MemAvailable_bytes/node_memory_MemTotal_bytes) |
| Disk usage % | 100*(1-node_filesystem_avail_bytes/node_filesystem_size_bytes) |
| Uptime (jam) | (time() - node_boot_time_seconds) / 3600 |
| Container restart | sum by(name)(increase(container_restart_count[1h])) |
3. Grafana Variables
Tipe Variable
| Tipe | Kegunaan | Contoh Query |
| Query | Ambil dari data source | label_values(up, job) |
| Custom | Daftar manual | prod,staging,dev |
| Interval | Auto interval | auto,1m,5m,1h,1d |
| Data Source | Pilih data source | prometheus |
| Constant | Nilai tetap | production |
| Ad hoc filters | Filter dinamis | β |
Variable Functions
# Label values (dari metrik)
label_values(up, job)
label_values(http_requests_total{job="api"}, handler)
label_values(node_uname_info, nodename)
# Query result
query_result(topk(5, http_requests_total))
# Regex filter
label_values(http_requests_total{job=~"$job"}, status)
# Multi-value & All
# Aktifkan "Multi-value" dan "Include All option"
# Gunakan di query: {job=~"$job"}
# Regex match (~=) otomatis handle multi-select
Penggunaan Variable dalam Query
# Variable $job
sum by (handler) (rate(http_requests_total{job="$job"}[5m]))
# Variable $interval
rate(http_requests_total{job="$job"}[$interval])
# Multi-value $status
http_requests_total{status=~"$status"}
# Variable chaining
label_values(http_requests_total{job="$job"}, handler)
4. Grafana Panel Types
| Panel | Kegunaan | Kapan Dipakai |
| Time Series | Grafik waktu | Default untuk sebagian besar metrik |
| Stat | Angka besar dengan warna | KPI, counter, uptime |
| Gauge | Pengukur analog | CPU %, memory %, disk % |
| Bar Gauge | Bar horizontal/vertikal | Perbandingan antar service |
| Table | Tabel data | Daftar host, top-N, log |
| Heatmap | Densitas warna | Histogram latency, distribusi |
| Pie Chart | Diagram lingkaran | Distribusi status code, traffic |
| Bar Chart | Diagram batang | Perbandingan kategori |
| Logs | Log viewer | Loki/ES log exploration |
| Node Graph | Graph topology | Service dependency |
| Alert List | Daftar alert aktif | Monitoring dashboard |
| Text | Markdown/HTML | Dokumentasi, judul section |
Panel Options Umum
// Override satuan (Panel β Overrides)
// CPU: Unit = percent (0-100)
// Memory: Unit = bytes (IEC)
// Latency: Unit = seconds (s)
// Requests: Unit = requests per second (reqps)
// Thresholds
// Green: < 70% | Yellow: 70-90% | Red: > 90%
// Value mapping
// 0 = "Down" (red) | 1 = "Up" (green)
5. Grafana Alerting Rules
# Contoh alert rule (Unified Alerting)
# Alert: High CPU Usage
# Condition: avg() of query(A, 5m, now) > 80
# PromQL:
100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))
# Alert: High Error Rate
# Condition: sum() of query(A, 5m, now) > 5
# PromQL:
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m])) * 100
# Alert: Disk Almost Full (< 10% free)
# PromQL:
100 * (1 - node_filesystem_avail_bytes{mountpoint="/"}
/ node_filesystem_size_bytes{mountpoint="/"}) > 90
# Alert: Service Down
# PromQL:
up == 0
# Alert: High Memory Usage (> 90%)
# PromQL:
100 * (1 - node_memory_MemAvailable_bytes
/ node_memory_MemTotal_bytes) > 90
Notification Channels
| Channel | Contoh Config |
| Email | smtp.server: mail.example.com:587 |
| Slack | Webhook URL: https://hooks.slack.com/... |
| Telegram | Bot Token + Chat ID |
| Discord | Webhook URL |
| PagerDuty | Integration Key |
| Webhook | Custom HTTP endpoint |
6. Node-RED Node Types
Input Nodes
| Node | Fungsi | Output |
| inject | Trigger manual/timer | Timestamp atau payload |
| http in | HTTP endpoint | Request object |
| mqtt in | Subscribe MQTT | Payload message |
| tcp in | TCP connection | Buffer/string |
| websocket in | WebSocket server/client | Message |
| file in | Baca file | String/buffer |
| csv | Parse CSV | Array/object |
| json | Parse JSON | Object |
Output Nodes
| Node | Fungsi |
| debug | Tampilkan di sidebar debug |
| http response | Kirim HTTP response |
| mqtt out | Publish ke MQTT broker |
| file | Tulis/append ke file |
| email | Kirim email (SMTP) |
Logic & Processing
| Node | Fungsi |
| function | Custom JavaScript logic |
| switch | Routing berdasarkan kondisi |
| change | Set/change/delete property |
| filter | Filter pesan (debounce, dedupe) |
| delay | Delay/rate limit pesan |
| split | Split array/object |
| join | Join messages |
| batch | Batch messages |
| template | Mustache template |
| exec | Run shell command |
| html | Parse HTML (cheerio) |
7. Node-RED MQTT
MQTT In Node (Subscribe)
// Config: Server = broker.hivemq.com:1883
// Topic = sensor/+/suhu
// QoS = 1
// Output = auto-detect (string/parsed JSON)
// Wildcard:
// sensor/+/suhu β single level (sensor/ruang1/suhu)
// sensor/# β multi level (sensor/ruang1/suhu, sensor/ruang1/lembab)
MQTT Out Node (Publish)
// Topic: actuator/relay/1
// QoS: 1
// Retain: true/false
// Inject node β Change (set msg.payload = "ON") β MQTT Out
// Publish JSON:
// msg.payload = JSON.stringify({ relay: 1, state: "ON", ts: Date.now() })
// msg.topic = "actuator/relay/1"
Contoh Flow: Sensor β MQTT β Dashboard
// Flow:
// [inject 5s] β [function: baca sensor] β [mqtt out: sensor/suhu]
// [mqtt in: sensor/suhu] β [json] β [chart β dashboard]
// Function node "baca sensor":
msg.payload = {
suhu: (Math.random() * 15 + 20).toFixed(1),
kelembaban: (Math.random() * 30 + 50).toFixed(1),
timestamp: new Date().toISOString()
};
msg.topic = "sensor/ruang1/data";
return msg;
8. Function Node JavaScript
// Basic: ubah payload
msg.payload = msg.payload.toUpperCase();
return msg;
// Akses context (penyimpanan antar eksekusi)
let counter = flow.get("counter") || 0;
counter++;
flow.set("counter", counter);
msg.payload = counter;
return msg;
// Global context
global.set("lastUpdate", new Date());
// Conditional output (multi output)
if (msg.payload > 30) {
return [msg, null]; // Output 1: panas
} else {
return [null, msg]; // Output 2: normal
}
// Multiple messages
let messages = [null, null];
messages[0] = { payload: "Alert: Suhu tinggi!" };
messages[1] = { payload: msg.payload };
return messages;
// Array manipulation
let data = msg.payload;
let avg = data.reduce((a, b) => a + b, 0) / data.length;
msg.payload = { average: avg.toFixed(2), count: data.length };
return msg;
// HTTP request (pakai node "http request" lebih mudah)
// Tapi bisa juga di function:
const got = global.get("got"); // Perlu install got library
Error Handling di Function Node
// Validasi payload
if (!msg.payload || typeof msg.payload !== 'object') {
node.error("Invalid payload", msg);
return null; // Hentikan flow
}
// Try-catch
try {
let data = JSON.parse(msg.payload);
msg.payload = data;
return msg;
} catch (e) {
node.warn("Parse error: " + e.message);
return null;
}
// Send to catch node
if (msg.payload.error) {
node.error("Data error", msg);
return null;
}
9. Dashboard JSON Structure
Node-RED Dashboard (node-red-dashboard)
// Tab config (ui_base)
// Set tab name di dashboard config
// Widget types:
// ui_button β Tombol aksi
// ui_slider β Input slider (range)
// ui_switch β Toggle on/off
// ui_numeric β Input angka
// ui_text_inputβ Input teks
// ui_dropdown β Dropdown select
// ui_colour_picker β Pilih warna
// ui_chart β Grafik (line/bar/scatter)
// ui_gauge β Gauge analog
// ui_text β Tampilkan teks
// ui_notification β Toast notification
// ui_spacer β Spacer/separator
// Chart configuration (ui_chart node):
{
"group": "group1",
"order": 0,
"width": 6,
"height": 4,
"label": "Suhu",
"chartType": "line",
"legend": true,
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "Tidak ada data",
"options": {
"yaxis": { "min": 0, "max": 50 }
}
}
Grafana Dashboard JSON (Export/Import)
// Struktur dasar dashboard JSON Grafana:
{
"dashboard": {
"title": "Monitoring IoT",
"tags": ["iot", "monitoring"],
"timezone": "browser",
"panels": [
{
"id": 1,
"type": "timeseries",
"title": "Suhu Sensor",
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
"targets": [
{
"expr": "sensor_temperature{location=\"ruang1\"}",
"legendFormat": "{{sensor}}",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "celsius",
"thresholds": {
"steps": [
{ "value": null, "color": "green" },
{ "value": 35, "color": "yellow" },
{ "value": 40, "color": "red" }
]
}
}
}
}
],
"templating": {
"list": [
{
"name": "location",
"type": "query",
"query": "label_values(sensor_temperature, location)"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"refresh": "30s"
},
"overwrite": false
}
10. Docker Compose Setup
# docker-compose.yml β Full Stack Monitoring
version: "3.8"
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana-storage:/var/lib/grafana
nodered:
image: nodered/node-red:latest
ports:
- "1880:1880"
volumes:
- nodered-data:/data
mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
- "9001:9001"
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
pid: host
volumes:
grafana-storage:
nodered-data:
Prometheus Config (prometheus.yml)
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'grafana'
static_configs:
- targets: ['grafana:3000']