Dua Jenis Alert Rule
| Grafana-managed | Data source-managed | |
|---|---|---|
| Dievaluasi oleh | Grafana | Prometheus / Mimir / Loki |
| Sumber data | Banyak data source sekaligus | Satu data source |
| Bisa pakai Expression | ✅ | ❌ (hanya PromQL/LogQL) |
| Disimpan di | Basis data Grafana | Berkas rule di data source |
| Bertahan bila Grafana mati | ❌ | ✅ |
Butuh manageAlerts: true | — | ✅ untuk mengedit dari UI |
Alert yang membangunkan orang sebaiknya dievaluasi Prometheus (data source-managed): ia tetap bekerja walaupun Grafana sedang mati atau sedang di-upgrade. Grafana-managed dipakai untuk alert yang perlu menggabungkan beberapa sumber data, memakai Expression, atau berbasis SQL — hal yang tidak mungkin dilakukan Prometheus.
Anatomi Grafana-managed Alert Rule
apiVersion: 1
groups:
- orgId: 1
name: bayarcepat-slo
folder: Peringatan Produksi
interval: 1m # seberapa sering dievaluasi
rules:
- uid: err-rate-ledger
title: Error rate ledger melewati SLO
condition: C # refId mana yang menentukan
for: 5m # harus benar selama ini dulu
noDataState: NoData
execErrState: Alerting
annotations:
summary: 'Error rate ledger {{ humanizePercentage $values.C.Value }}'
description: >
Error 5xx pada layanan ledger melewati 1% selama 5 menit.
Periksa pool koneksi database lebih dulu.
runbook_url: https://wiki.internal/runbook/ledger-error-rate
dashboardUId: bayarcepat-ikhtisar
panelId: '3'
labels:
severity: critical
tim: pembayaran
layanan: ledger
data:
- refId: A
relativeTimeRange: { from: 600, to: 0 }
datasourceUid: prom-utama
model:
expr: sum(rate(http_requests_total{layanan="ledger",status=~"5.."}[5m]))
instant: true
refId: A
- refId: B
relativeTimeRange: { from: 600, to: 0 }
datasourceUid: prom-utama
model:
expr: sum(rate(http_requests_total{layanan="ledger"}[5m]))
instant: true
refId: B
- refId: C
datasourceUid: __expr__
model:
type: math
expression: '$A / $B > 0.01'
refId: CState Machine — Bagian yang Paling Sering Disalahpahami
kondisi benar bertahan selama "for"
Normal ──────────────────> Pending ───────────────────> Alerting
▲ │ │
│ kondisi salah │ │
└──────────────────────────┴────────────────────────────┘
│
kueri gagal ──> Error ─┐ │ setelah resolve
data hilang ──> NoData ─┴──> mengikuti execErrState / noDataState
▼
Normal (Resolved)| State | Arti | Notifikasi terkirim? |
|---|---|---|
| Normal | Kondisi tidak terpenuhi | Tidak |
| Pending | Kondisi benar, tapi belum selama for | Tidak |
| Alerting | Benar dan sudah melewati for | Ya |
| NoData | Kueri tidak menghasilkan series | Sesuai noDataState |
| Error | Kueri gagal / data source mati | Sesuai execErrState |
Pilihan noDataState menentukan apakah hilangnya monitoring dianggap masalah:
NoData— memicu alert khusus NoData. Pilihan yang benar untuk alert kritis: metrik yang hilang biasanya berarti aplikasi mati.Alerting— perlakukan sama seperti kondisi terpenuhi. Agresif, cocok untuk heartbeat.OK— anggap sehat. Berbahaya: aplikasi mati total menjadi "tidak ada masalah" karena tidak ada lagi yang mengekspos metrik.
Untuk metrik yang jarang muncul (misalnya error counter yang biasanya nol), pola yang benar bukan noDataState: OK, melainkan membuat kueri selalu menghasilkan angka — misalnya dengan or vector(0) di PromQL.
Multi-dimensional Alert
Satu rule bisa menghasilkan banyak instance alert, satu per kombinasi label. Ini kekuatan besar sekaligus sumber banjir notifikasi.
sum by (layanan, instance) (
rate(http_requests_total{status=~"5.."}[5m])
) > 1
# Bila 8 layanan × 5 instance melewati batas:
# → 40 instance alert dari SATU rule
# → tanpa grouping yang benar, 40 pesan di SlackSolusinya bukan mengurangi label pada rule, melainkan mengatur grouping di notification policy (bab berikutnya membahasnya bersama contact point).
Contact Point dan Notification Policy
apiVersion: 1
contactPoints:
- orgId: 1
name: slack-pembayaran
receivers:
- uid: cp-slack-bayar
type: slack
settings:
url: $__env{SLACK_WEBHOOK_PEMBAYARAN}
title: '{{ template "slack.title" . }}'
text: '{{ template "slack.text" . }}'
- orgId: 1
name: pagerduty-oncall
receivers:
- uid: cp-pd
type: pagerduty
settings:
integrationKey: $__env{PAGERDUTY_KEY}
severity: critical
policies:
- orgId: 1
receiver: slack-pembayaran # default
group_by: ['alertname', 'layanan']
group_wait: 30s # tunggu, mungkin ada yang menyusul
group_interval: 5m # jeda antar update grup yang sama
repeat_interval: 4h # ulangi bila masih menyala
routes:
- receiver: pagerduty-oncall
object_matchers:
- ['severity', '=', 'critical']
continue: false # berhenti di sini, jangan lanjut
mute_time_intervals: []
- receiver: slack-pembayaran
object_matchers:
- ['tim', '=', 'pembayaran']
group_by: ['alertname', 'layanan', 'instance']
muteTimeIntervals:
- orgId: 1
name: luar-jam-kerja-non-kritis
time_intervals:
- times:
- start_time: '18:00'
end_time: '08:00'
weekdays: ['monday:friday']
location: 'Asia/Jakarta'group_by: ['alertname']
→ 40 instance menjadi 1 pesan berisi 40 baris ✅ enak dibaca
group_by: ['alertname', 'instance']
→ 40 pesan terpisah ❌ banjir
group_by: ['...']
→ grup per setiap kombinasi label unik ❌ paling burukMulailah dari grouping yang kasar. Perhalus hanya bila memang ada yang perlu ditangani terpisah.
Templating Notifikasi
{{ define "slack.title" }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }}
{{ end }}
{{ define "slack.text" }}
{{ range .Alerts -}}
*Layanan:* {{ .Labels.layanan }} · *Severity:* {{ .Labels.severity }}
*Nilai:* {{ .ValueString }}
*Sejak:* {{ .StartsAt.Format "15:04:05 MST" }}
{{ if .Annotations.summary }}*Ringkasan:* {{ .Annotations.summary }}{{ end }}
{{ if .Annotations.runbook_url }}*Runbook:* {{ .Annotations.runbook_url }}{{ end }}
{{ if .DashboardURL }}*Dashboard:* {{ .DashboardURL }}{{ end }}
{{ if .SilenceURL }}*Bisukan:* {{ .SilenceURL }}{{ end }}
---
{{ end }}
{{ end }}Fungsi yang berguna di dalam template: humanize, humanizePercentage, humanizeDuration, toUpper, dan reReplaceAll.
Silence, Mute Timing, dan Pause
| Mekanisme | Efek | Kapan dipakai |
|---|---|---|
| Silence | Rule tetap dievaluasi, notifikasi ditahan | Maintenance terjadwal, insiden yang sudah diketahui |
| Mute timing | Notifikasi ditahan pada jadwal berulang | Alert non-kritis di luar jam kerja |
| Pause rule | Rule berhenti dievaluasi sama sekali | Rule yang sedang diperbaiki |
Silence selalu punya batas waktu — dan itu bagus. Alert yang dibisukan "selamanya" adalah alert yang seharusnya dihapus atau diperbaiki.