Alerting adalah aturan yang memantau metric dan memanggil orang saat ada masalah yang butuh tindakan. On-call adalah giliran jaga: siapa yang dipanggil, kapan, dan apa yang dia lakukan. Bayangin alarm asap di dapur. Alarm yang baik gak menjerit tiap kali roti sedikit gosong, tapi pasti menjerit kalau asapnya bertahan. Dan kalau kebakaran sungguhan, pemadam kebakaran punya jadwal piket dan kartu SOP di dinding. Monitoring tanpa alert cuma dashboard yang gak pernah dilihat jam 3 pagi.
Di pelajaran Metrics & Monitoring, Prometheus sudah menyimpan error rate catatan-api tiap 15 detik. Sekarang kita pasang aturan di atasnya.
🧪 Tebakan cepat: waktu deploy, container catatan-api restart dan error rate melonjak ke 7,8% selama 2 menit, lalu normal lagi. Aturannya: error rate di atas 5% dengan
for: 5m. On-call ditelepon atau gak? Jawabannya ada di bagian "Ambang dan for:".
Anatomi alert rule
Aturan Prometheus ditulis di file YAML, dan Prometheus mengevaluasinya tiap menit:
groups:
- name: catatan-api
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{job="catatan-api",status_code=~"5.."}[1m]))
/ sum(rate(http_requests_total{job="catatan-api"}[1m])) * 100 > 5
for: 5m
labels:
severity: critical
annotations:
summary: "Error rate catatan-api di atas 5%"
runbook_url: "https://wiki.example.com/runbooks/catatan-api-error-rate"
expr: request 5xx per detik dibagi semua request per detik, dikali 100, jadi persen. Aturannya "aktif" selama hasilnya di atas 5.for: 5m: kondisinya harus bertahan 5 menit sebelum alert benar-benar dikirim.labels.severity: seberapa gawat, dipakai untuk menentukan ke mana alert dikirim.annotations: teks untuk manusia, termasuk tautan ke runbook.
Prometheus cuma memutuskan "firing" atau gak. Yang mengirim notifikasi adalah Alertmanager: mengelompokkan alert, membuang duplikat, dan meneruskannya ke telepon on-call atau Slack.
Satu aturan, satu nilai
Inti dari tiap evaluasi: bandingkan nilai sekarang dengan ambang memakai operator aturannya.
function evaluateAlert(rule, currentValue) {
let triggered = false;
if (rule.operator === ">") triggered = currentValue > rule.threshold;
if (rule.operator === "<") triggered = currentValue < rule.threshold;
if (rule.operator === ">=") triggered = currentValue >= rule.threshold;
if (rule.operator === "<=") triggered = currentValue <= rule.threshold;
return { triggered, severity: rule.severity, metric: rule.metric, currentValue, threshold: rule.threshold };
}
| Aturan | Nilai sekarang | triggered |
|---|---|---|
error_rate > 0.05, critical |
0.08 |
true (8% di atas 5%) |
cpu >= 90, warning |
85 |
false |
disk < 10 (sisa disk %), critical |
5 |
true |
- Operatornya datang sebagai teks (
">"), jadi tiap teks dipetakan ke perbandingan JavaScript yang sesuai. <dipakai untuk metric yang bahaya kalau terlalu kecil, seperti sisa ruang disk.- Object hasilnya disusun dengan urutan key
triggered,severity,metric,currentValue,threshold. Di latihan urutan ini ikut dicek.
Ambang dan for:
Prometheus mengevaluasi aturan tiap menit, dan alert punya tiga keadaan:
- inactive: kondisinya salah.
- pending: kondisinya benar, tapi belum selama
for:. - firing: kondisinya sudah benar selama
for:. Notifikasi dikirim. Saat kondisinya salah lagi, alert jadi resolved.
Kalau kondisinya salah saat masih pending, alert dibuang tanpa notifikasi. Ini dua seri dari Lab dengan ambang 5% dan for: 5m:
| Seri | Di atas 5% | pending | firing | Notifikasi |
|---|---|---|---|---|
| blip deploy | 10:08 sampai 10:09 | 10:08 sampai 10:09 | gak pernah | 0 |
| insiden | 10:14 sampai 10:23 | 10:14 sampai 10:18 | 10:19 sampai 10:23 | 10:19 firing, 10:24 resolved |
Blip 2 menit gak pernah sampai 5 menit, jadi gak ada yang ditelepon. Itu jawaban tebakannya. Tapi harganya terlihat di baris kedua: insiden sungguhan baru memanggil orang 5 menit setelah dimulai. for: membeli ketenangan dengan keterlambatan; untuk masalah yang dirasakan semua user, 2 sampai 5 menit sudah cukup.
Flapping
Seri ketiga di Lab naik turun di sekitar 5%: 5,4, lalu 4,7, lalu 5,3, lalu 4,6. Dengan for: 0m, alert firing, resolved, firing lagi, 12 kali dalam 30 menit. Ini disebut flapping: satu masalah, belasan telepon. Cara meredamnya:
for:yang cukup panjang, supaya lonjakan sesaat gak dihitung.keep_firing_for: 5m: alert tetap firing 5 menit setelah kondisinya salah, jadi penurunan sesaat gak dianggap selesai.- Ambang di tingkat yang benar-benar kamu pedulikan. Kalau error rate 4,5% juga masalah, ambangnya 4, bukan 5.
Di Lab, for: 2m ditambah keep_firing_for: 5m menghasilkan satu notifikasi pukul 10:13, dan alert tetap firing selama masalahnya ada.
Gejala, bukan penyebab
Telepon orang untuk gejala yang dirasakan user: error rate tinggi, p99 lambat, situs gak bisa dibuka. Jangan untuk penyebab yang mungkin: CPU 90% saat semua request tetap cepat dan berhasil gak merugikan siapa pun. Penyebab tempatnya di dashboard dan tiket, dan dipakai saat menyelidiki gejala.
Setiap alert harus actionable: ada yang harus dilakukan begitu bunyi. Alert yang bunyi terus tapi gak pernah butuh tindakan melahirkan alert fatigue: orang kebal, lalu alert yang penting ikut diabaikan. Obatnya: cuma alert yang actionable, ambang dan for: yang tepat, dan severity yang jelas.
Severity: page atau tiket
| Severity | Contoh di catatan-api | Ke mana |
|---|---|---|
critical |
error rate di atas 5% selama 5 menit, situs down | page: telepon on-call, jam berapa pun |
warning |
sertifikat TLS habis dalam 14 hari, disk 80% | tiket di Slack #catatan-alerts, dikerjakan jam kerja |
Alertmanager membaca label severity dan meneruskan alert:
route:
receiver: slack-tiket # default: tiket di Slack
routes:
- matchers: ['severity="critical"']
receiver: pagerduty-oncall # critical: telepon on-call
Banyak tim juga memakai tingkat insiden SEV1 sampai SEV4: SEV1 layanan mati untuk semua user, SEV4 masalah kosmetik.
Runbook dan on-call
Runbook adalah halaman yang ditautkan dari alert: apa artinya, dashboard mana yang dibuka, langkah pertama (misalnya "cek deploy terakhir, rollback kalau error mulai setelahnya"), dan siapa yang dihubungi. Orang yang dibangunkan jam 3 pagi gak perlu berpikir dari nol.
On-call rotation membagi giliran: misalnya tiap minggu satu orang jadi primary dan satu secondary, bergiliran. Layanan seperti PagerDuty, Opsgenie, atau Grafana OnCall memegang jadwalnya dan escalation policy: kalau primary gak acknowledge dalam 5 menit, alert naik ke secondary. Urutan saat dipanggil: acknowledge (aku pegang), nilai dampaknya, mitigasi dulu (rollback, matikan feature flag), baru cari akar masalahnya. Penulisan postmortem setelahnya dibahas di pelajaran Incident Response & Postmortems.
⚠️ Jebakan yang sering ketemu
- Alert tanpa
for:. Setiap lonjakan sesaat, termasuk restart saat deploy, membangunkan orang. - Page untuk penyebab. CPU, memori, atau jumlah koneksi yang tinggi tanpa gejala di user cukup jadi tiket.
- Alert yang gak actionable. Kalau jawaban yang benar saat alert bunyi adalah "abaikan saja", hapus alert-nya.
- Di latihan: operator yang tertukar atau urutan key berbeda.
">="harus memakai>=, bukan>: CPU 85 dengan aturan>= 90harustriggered: false. Hasilnya{ triggered, severity, metric, currentValue, threshold }persis dengan urutan itu.
TL;DR: Alert rule membandingkan metric dengan ambang (operator >, <, >=, <=) setiap evaluasi, dan for: menahannya di pending sampai kondisinya bertahan, jadi blip 2 menit gak membangunkan orang, dengan harga insiden sungguhan baru dipanggil setelah for: habis. Seri yang naik turun di sekitar ambang membuat flapping; redam dengan for:, keep_firing_for, atau ambang yang jujur. Page untuk gejala yang dirasakan user (error rate, p99), tiket untuk penyebab dan hal yang bisa menunggu, dan hapus alert yang gak actionable, supaya gak terjadi alert fatigue. Severity critical jadi telepon on-call, warning jadi tiket, diatur Alertmanager. Tiap alert menautkan runbook, dan rotasi on-call punya primary, secondary, dan eskalasi kalau gak ada yang acknowledge.