Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.
01Bagaimana cara mendesain metrics dan monitoring system?
Desain sebagai pipeline enam tahap: instrumentasi, collection, time-series storage, query, alerting, dan dashboard. Tentukan set label dan scrape interval lebih dulu karena keduanya menentukan biaya storage, lalu pilih retention dan tulis alert pada gejala yang dirasakan user. Estimasi series kali samples per detik kali byte per sample sebelum merilis metric baru.
02Apa perbedaan monitoring pull dan push?
Pada monitoring pull, collector mengunjungi endpoint metrics tiap target secara terjadwal, sehingga scrape yang gagal memberi tahu bahwa target mati. Pada push, aplikasi mengirim metrics ke collector, cocok untuk batch job berumur pendek tetapi membuat keheningan menjadi ambigu. Prometheus memakai pull secara default dan Pushgateway untuk batch job.
03Apa itu cardinality explosion di Prometheus?
Setiap kombinasi unik nilai label adalah satu time series tersendiri, jadi jumlah series adalah hasil kali nilai berbeda dari tiap label. Satu histogram latency dengan 5 method, 40 route, 6 status code, dan 10 instance sudah menghasilkan 168.000 series. Menambah label dengan 10.000 nilai seperti user ID melipatgandakannya menjadi miliaran.
04Berapa storage yang dibutuhkan server Prometheus?
Kebutuhan disk sama dengan retention dalam detik kali samples per detik yang masuk kali byte per sample, dan dokumentasi Prometheus menyebut rata-rata sekitar 1 sampai 2 byte per sample. Sebagai asumsi, 40.000 series yang di-scrape tiap 15 detik dengan 2 byte per sample butuh sekitar 6,4 GiB untuk 15 hari. Menggandakan scrape interval membagi dua angka itu.
05Bagaimana menghindari alert fatigue?
Lakukan page hanya untuk gejala yang dirasakan user, seperti rasio error atau latency, dan taruh penyebab seperti CPU di dashboard. Tambahkan durasi for supaya lonjakan singkat tidak memicu alert, dan hapus alert yang tidak punya aksi untuk orang on-call. Alert yang sedikit dan actionable dipercaya, dan alert yang dipercaya ditanggapi.
Cara Mendesain Metrics dan Monitoring System dengan Prometheus
Cara mendesain metrics dan monitoring system dari ujung ke ujung: instrumentasi, pull vs push, hitungan storage time-series, cardinality, retention, dan alerting.
Untuk mendesain metrics dan monitoring system, bangun sebuah pipeline: instrumentasi service dengan counter, gauge, dan histogram, kumpulkan lewat scraping, simpan di time-series database, query, lalu alert pada gejala yang dirasakan user. Hitung storage sebagai series kali samples per detik kali sekitar dua byte, dan batasi cardinality label sebelum berlipat ganda.
Di satu VPS yang menjalankan NestJS API, Postgres, dan Redis dalam Docker, keputusan monitoring pertama jarang soal tool mana yang dipasang. Jawaban default-nya Prometheus. Yang menentukan apakah setup itu tetap berguna adalah semua hal di sekitar instalasinya: apa yang diukur, berapa banyak kombinasi label yang diizinkan, berapa lama data disimpan, dan apa yang boleh membangunkan seseorang.
Ini pandangan satu sistem utuh. Pipeline ditelusuri dari instrumentasi sampai dashboard, lalu waktu terbanyak dipakai di tiga tempat desain sering salah: hitungan storage, cardinality, dan desain alert. Setiap angka berasal dari dokumentasi Prometheus atau dari script estimator yang inputnya diberi label sebagai asumsi, dan output di bawah adalah hasil run yang sebenarnya.
Apa saja komponen metrics dan monitoring system?
Enam tahap, masing-masing dengan satu tugas dan satu cara gagal yang khas. Memisahkannya penting karena solusi untuk pager yang berisik (tahap lima) tidak pernah ada di tahap tempat orang pertama kali mencari (tahap enam).
Tahap
Tugasnya
Cara gagal yang umum
1. Instrumentasi
Kode mengekspos counter, gauge, dan histogram dengan label
Label dengan nilai tak terbatas membuat series tak terbatas
2. Collection
Scraper melakukan pull (atau client melakukan push) sample tiap interval
Scrape yang terlewat meninggalkan gap; job berumur pendek tidak pernah terlihat
3. Storage
Time-series database menyimpan series, timestamp, dan nilai
Disk tumbuh sebanding jumlah series kali retention
4. Query
Query language menghitung rate, rasio, dan quantile
Query rentang panjang di data mentah lambat
5. Alerting
Rule dievaluasi terjadwal dan diteruskan ke manusia
Alert berbasis penyebab tanpa durasi menimbulkan alert fatigue
6. Dashboard
Grafik untuk diagnosis setelah alert menyala
Empat puluh panel yang tidak pernah dibuka; tidak ada yang tahu harus melihat ke mana dulu
Buku SRE dari Google merumuskan tujuan seluruh pipeline ini sebagai menjawab dua pertanyaan: apa yang rusak, dan kenapa. Pipeline memberi data untuk pertanyaan kedua, tetapi hanya lapisan alerting yang bertanggung jawab atas yang pertama, itulah sebabnya sisa artikel ini terus kembali ke sana.
Metric type apa saja yang dibutuhkan monitoring system?
Dokumentasi Prometheus mendefinisikan empat tipe inti. Tiga di antaranya mencakup hampir semua kebutuhan aplikasi.
Counter: nilai kumulatif yang hanya naik, atau reset ke nol saat restart. Pakai untuk request, error, dan byte terkirim, dan selalu query dengan rate(), jangan membaca angka mentahnya.
Gauge: nilai yang bisa naik dan turun, seperti kedalaman queue, memori terpakai, atau koneksi terbuka. Dibaca langsung.
Histogram: menghitung observasi ke dalam bucket yang bisa dikonfigurasi dan juga mengekspos sum dan count. Ini tipe yang tepat untuk latency karena quantile bisa dihitung darinya dan di-aggregate lintas instance.
Tipe keempat, summary, menghitung quantile di sisi client, dan hasilnya tidak bisa dirata-ratakan secara bermakna lintas instance. Untuk kumpulan replika API itu biasanya alasan memilih histogram. Berikut bentuk instrumentasi dengan prom-client. Perhatikan bahwa label route berisi template route, bukan URL mentah.
import { Counter, Histogram, collectDefaultMetrics, register } from "prom-client";
collectDefaultMetrics(); // process and event-loop gauges for free
// Counter: only goes up. Query it with rate(), never read the raw value.
const requests = new Counter({
name: "http_requests_total",
help: "Requests handled",
labelNames: ["method", "route", "status"], // route is the TEMPLATE "/orders/:id", never the raw URL
});
// Histogram: cumulative buckets, so quantiles can be aggregated across instances later.
const latency = new Histogram({
name: "http_request_duration_seconds",
help: "Request latency",
labelNames: ["route"],
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10], // 11 finite buckets
});
// In the handler:
// const end = latency.startTimer({ route: "/orders/:id" });
// ...
// end(); requests.inc({ method: "GET", route: "/orders/:id", status: "200" });
// The scrape endpoint Prometheus pulls from:
// app.get("/metrics", async (_, res) => res.type(register.contentType).send(await register.metrics()));
Apakah metrics sebaiknya di-pull atau di-push?
Prometheus melakukan pull: ia mengunjungi endpoint metrics tiap target pada suatu interval. FAQ resminya menyebut pull sedikit lebih baik daripada push, sambil mengakui bahwa push punya tempatnya. Trade-off-nya konkret.
Aspek
Pull (scrape)
Push
Apakah target masih hidup?
Scrape yang gagal tercatat sebagai up sama dengan 0, jadi matinya target adalah sinyal tersendiri
Diam itu ambigu: mati, atau hanya sepi?
Batch job berumur pendek
Bisa selesai sebelum scrape berikutnya dan tidak pernah terlihat
Cocok secara alami: push hasilnya sekali di akhir
Bentuk jaringan
Scraper harus bisa menjangkau setiap target
Target hanya butuh akses keluar ke collector
Kontrol laju
Collector menentukan interval secara terpusat
Client yang bermasalah bisa membanjiri collector
Default saya untuk service yang berjalan terus adalah pull, dengan Pushgateway hanya untuk batch job seperti laporan atau backup malam hari. Config di bawah menunjukkan keduanya: dua instance API di-scrape langsung, satu label dibuang saat ingestion, dan Pushgateway di-scrape seperti target lain.
global:
scrape_interval: 15s # every series costs one sample per interval, so this is a storage knob
evaluation_interval: 15s # how often alert and recording rules run
rule_files:
- /etc/prometheus/rules/*.yml
scrape_configs:
# Pull: Prometheus visits each target's /metrics endpoint.
- job_name: api
metrics_path: /metrics
static_configs:
- targets: ["api-1:3000", "api-2:3000"]
metric_relabel_configs:
# Drop a label you should never have shipped before it becomes a series.
- action: labeldrop
regex: user_id
# Short-lived jobs cannot be scraped, so they push to a Pushgateway that Prometheus scrapes.
- job_name: pushgateway
honor_labels: true # keep the job label the batch job set, not "pushgateway"
static_configs:
- targets: ["pushgateway:9091"]
- job_name: node
static_configs:
- targets: ["node-exporter:9100"]
Berapa besar storage yang dibutuhkan metrics system?
Dokumentasi storage Prometheus memberi rumusnya: kebutuhan disk sama dengan retention dalam detik, kali samples per detik yang masuk, kali byte per sample. Dokumentasi itu menyebut Prometheus rata-rata hanya memakai sekitar 1 sampai 2 byte per sample. Saya memakai 2 sebagai angka perencanaan yang konservatif. Samples per detik tidak lain adalah series dibagi scrape interval. Script di bawah menerapkan rumus itu, dan inputnya adalah asumsi yang saya pilih, bukan hasil pengukuran.
// Cardinality and storage estimator. Every input is an assumption, not a measurement.
const BYTES_PER_SAMPLE = 2; // upper end of the "1-2 bytes per sample" in the Prometheus storage docs
const SECONDS_PER_DAY = 86_400;
// Series for one metric = product of label value counts, times series per label set.
const product = (counts: number[]): number => counts.reduce((a, b) => a * b, 1);
// A histogram with 11 finite buckets exposes 11 + 1 (+Inf) bucket series, plus _sum and _count.
const HISTOGRAM_SERIES_PER_LABEL_SET = 11 + 1 + 2;
const labels = { method: 5, route: 40, status: 6, instance: 10 };
const labelSets = product(Object.values(labels));
const histogramSeries = labelSets * HISTOGRAM_SERIES_PER_LABEL_SET;
console.log("label sets (method x route x status x instance):", labelSets);
console.log("histogram series:", histogramSeries);
const withUserId = histogramSeries * 10_000;
console.log("same histogram plus a user_id label (10,000 users):", withUserId);
const storage = (series: number, scrapeSeconds: number, retentionDays: number) => {
const samplesPerSecond = series / scrapeSeconds;
const bytesPerDay = samplesPerSecond * SECONDS_PER_DAY * BYTES_PER_SAMPLE;
const totalGiB = (bytesPerDay * retentionDays) / 1024 ** 3;
return { samplesPerSecond, mbPerDay: bytesPerDay / 1024 ** 2, totalGiB };
};
const scenarios: Array<[string, number, number, number]> = [
["40,000 series, 15 s, 15 d", 40_000, 15, 15],
["40,000 series, 60 s, 15 d", 40_000, 60, 15],
["40,000 series, 15 s, 365 d raw", 40_000, 15, 365],
["histogram + user_id, 15 s, 15 d", withUserId, 15, 15],
];
for (const [name, series, interval, days] of scenarios) {
const r = storage(series, interval, days);
console.log(name.padEnd(34), r.samplesPerSecond.toFixed(0).padStart(10), "samples/s",
r.mbPerDay.toFixed(1).padStart(12), "MiB/day", r.totalGiB.toFixed(1).padStart(10), "GiB");
}
Dijalankan dengan Node, ini output sebenarnya. Empat puluh ribu series adalah ukuran fleet yang diasumsikan, dan baris histogram adalah contoh cardinality dari bagian berikutnya.
$ node metrics-estimator.ts
label sets (method x route x status x instance): 12000
histogram series: 168000
same histogram plus a user_id label (10,000 users): 1680000000
40,000 series, 15 s, 15 d 2667 samples/s 439.5 MiB/day 6.4 GiB
40,000 series, 60 s, 15 d 667 samples/s 109.9 MiB/day 1.6 GiB
40,000 series, 15 s, 365 d raw 2667 samples/s 439.5 MiB/day 156.6 GiB
histogram + user_id, 15 s, 15 d 112000000 samples/s 18457031.3 MiB/day 270366.7 GiB
Tiga hal muncul dari hitungan ini. Pertama, 40.000 series dengan interval 15 detik memakan sekitar 6,4 GiB untuk 15 hari, yang muat dengan mudah di disk VPS kecil. Kedua, pindah ke interval 60 detik memangkasnya jadi seperempat, karena samples per detik berbanding terbalik dengan interval. Ketiga, menyimpan data mentah yang sama selama setahun memakan sekitar 157 GiB, itu sebabnya retention adalah keputusan desain, bukan default. Baris terakhir adalah peringatannya: satu histogram dengan label yang salah akan butuh ratusan terabyte.
Apa itu cardinality explosion dan bagaimana mencegahnya?
Panduan penamaan Prometheus tegas: setiap kombinasi unik pasangan key dan value label adalah satu time series baru, dan sebaiknya jangan menaruh nilai berkardinalitas tinggi seperti user ID atau alamat email di label. Hitungannya menjelaskan alasannya. Jumlah series adalah hasil kali jumlah nilai setiap label, dikalikan dengan series yang dihasilkan tiap label set.
Di estimator, histogram latency dengan 5 method, 40 route, 6 status code, dan 10 instance punya 12.000 label set. Masing-masing menghasilkan 14 series (11 bucket terbatas, satu bucket +Inf, sum, dan count), jadi satu metric berjumlah 168.000 series, sudah lebih besar dari seluruh anggaran fleet 40.000 series. Menambahkan label user_id dengan 10.000 nilai melipatgandakannya jadi 1,68 miliar. Empat pagar pengaman mencegah hal ini terjadi.
Beri label hanya dengan himpunan terbatas: HTTP method, template route, status class. Jangan pernah URL mentah, user ID, nomor order, atau teks bebas.
Estimasi sebelum rilis: kalikan jumlah nilai label di pull request yang menambah metric.
Buang label berbahaya saat ingestion dengan metric_relabel_configs, seperti yang dilakukan scrape config di atas.
Pasang alert pada jumlah series milik server Prometheus sendiri supaya lonjakan cardinality ketahuan dalam hitungan jam, bukan saat halaman disk penuh.
Cardinality bersifat perkalian, bukan penjumlahan. Menambah satu label dengan N nilai tidak menambah N series, tetapi mengalikan jumlah yang ada dengan N. Penyebab paling umum adalah label route yang diisi path mentah, sehingga /orders/1042 dan /orders/1043 menjadi series berbeda.
Berapa lama metrics disimpan, dan apa itu downsampling?
Retention adalah biaya storage yang Anda pilih sendiri. Prometheus menyimpan 15 hari secara default dan menyediakan flag retention time untuk mengubahnya, dan dokumentasi yang sama membolehkan pembatasan berdasarkan ukuran. Sebagian besar pertanyaan operasional (apa yang berubah di deploy terakhir, apakah ini lebih lambat dari kemarin) butuh hitungan hari, bukan tahun. Menyimpan data mentah 15 detik selama setahun, seperti ditunjukkan estimator, kebanyakan hanya membeli tagihan disk.
Downsampling adalah cara menyimpan riwayat panjang dengan murah: simpan agregat kasar, misalnya rata-rata lima menit, untuk data lama sementara resolusi mentah kedaluwarsa. Prometheus sendiri tidak melakukan downsampling, jadi ini tugas long-term store di belakang remote write. Trik yang lebih murah di Prometheus biasa adalah recording rule, yang menghitung agregasi mahal sekali saja sehingga dashboard membaca satu series, bukan ribuan.
Bagaimana mendesain alert yang dipercaya tim?
Panduan alerting Prometheus merangkumnya dalam satu kalimat: jaga alerting tetap sederhana, alert pada gejala, sediakan console yang bagus untuk menemukan penyebab, dan hindari halaman di mana tidak ada yang bisa dilakukan. Gejala adalah yang dirasakan user, seperti rasio error atau latency. Penyebab adalah kondisi internal, seperti CPU atau connection pool penuh. Page untuk yang pertama, tampilkan grafik untuk yang kedua. File rule di bawah memasangkan recording rule dengan alert gejala yang punya durasi for, ditambah alert down, karena target yang mati tidak menghasilkan series sehingga threshold tidak pernah terpicu.
groups:
- name: api-symptoms
rules:
# Recording rule: compute the ratio once, reuse it in alerts and dashboards.
- record: job:http_errors:ratio_rate5m
expr: |
sum by (job) (rate(http_requests_total{job="api", status=~"5.."}[5m]))
/
sum by (job) (rate(http_requests_total{job="api"}[5m]))
# Symptom: users are getting errors. Not "CPU is high", which is a cause.
- alert: ApiHighErrorRatio
expr: job:http_errors:ratio_rate5m{job="api"} > 0.05
for: 10m # a 30-second blip never pages anyone
labels:
severity: page
annotations:
summary: "More than 5% of API requests failing for 10 minutes"
# Absence is also a symptom: a dead target produces no series, so a threshold never fires.
- alert: ApiTargetDown
expr: up{job="api"} == 0
for: 2m
labels:
severity: page
Klausa for mengubah sinyal yang naik turun menjadi sebuah keputusan: kondisi harus bertahan terus-menerus selama sepuluh menit sebelum alert menyala. Kesalahan yang umum adalah pola sebaliknya yang ditunjukkan di sini.
# Wrong: a cause, with no for: clause. It pages on every compile spike and tells nobody what users feel.
- alert: HighCpu
expr: node_cpu_utilisation > 0.8
# Right: put the cause on a dashboard, page on the symptom above, and keep a "for:" on everything.
Sebelum menambah alert, tanyakan: kalau ini menyala jam 3 pagi, apa yang dilakukan orangnya? Kalau jawabannya tidak ada, tempatnya di dashboard. Setiap alert yang melakukan page tanpa aksi mengajari tim untuk mengabaikan alert berikutnya, dan begitulah alert fatigue dimulai.
Bagaimana menskalakan monitoring system melewati satu server?
Satu server Prometheus sanggup menangani jauh lebih banyak dari dugaan, dan untuk satu VPS itu jawaban yang tepat. Ketika tidak cukup lagi, pilihannya memetakan tiga masalah yang berbeda.
Federation: Prometheus tingkat atas men-scrape agregat terpilih dari yang tingkat bawah, memberi pandangan global tanpa menyalin setiap series.
Remote write: tiap server mengalirkan sample ke long-term store yang bisa diskalakan horizontal, yang juga tempat downsampling dan retention bertahun-tahun berada.
Sharding per tenant atau per service: server terpisah untuk tiap tim atau pelanggan, supaya cardinality satu tenant yang berisik tidak menjatuhkan monitoring semua orang.
# Scaling out without changing the instrumentation: ship samples to a long-term store.
remote_write:
- url: https://metrics-store.internal/api/v1/push
queue_config:
max_samples_per_send: 2000 # batch size per request; tune against receiver limits
Remote write paling tidak invasif karena instrumentasi, scrape config, dan alert rule tetap seperti semula. Apa pun pilihannya, pertahankan pagar cardinality: menskalakan storage menaikkan batas atas tetapi tidak mengubah perkalian.
Metrics system adalah pipeline dengan anggaran. Pilih label yang terbatas, estimasi series kali samples kali byte sebelum merilis metric, simpan data mentah hanya sepanjang Anda benar-benar akan meng-query-nya, dan lakukan page hanya untuk gejala dengan durasi for. Dengan begitu satu server Prometheus tetap membosankan untuk waktu yang lama, dan itulah tujuan monitoring.