Setiap operator Prometheus pada akhirnya menghadapi tembok yang sama: dashboard yang tadinya langsung tampil mulai timeout, grafik memori server naik lurus terus, dan pada suatu malam prosesnya di-OOM-kill. Penyebab utamanya hampir selalu kardinalitas, bukan volume trafik mentah. Satu nama metrik dengan segelintir label bisa diam-diam meledak menjadi jutaan time series yang berbeda, dan Prometheus harus menyimpan setiap series itu di memori selama series tersebut masih aktif.
Tulisan ini adalah panduan praktis tentang bagaimana kardinalitas benar-benar menghabiskan memori, cara mengukurnya sebelum Anda mulai menerka-nerka, serta dua alat yang saling melengkapi untuk menjaganya di produksi: recording rules yang melakukan pra-agregasi query yang mahal, dan relabeling yang menghentikan label tak terbatas sebelum sampai ke storage. Di akhir tulisan ini Anda akan punya checklist yang bisa langsung dijalankan pada deployment Prometheus Anda minggu ini.
Mengapa Kardinalitas Adalah Metrik yang Diam-Diam Membunuh Server Anda
Kardinalitas adalah jumlah time series unik yang dihasilkan satu nama metrik setelah memperhitungkan setiap kombinasi nilai label. Sebuah counter bernama http_requests_total dengan label method dan status mungkin hanya menghasilkan belasan series. Namun tambahkan path request mentah atau identifier pelanggan sebagai label, dan counter yang sama itu bisa menghasilkan satu series baru untuk setiap path unik atau setiap pelanggan unik, selamanya. Setiap series aktif duduk di head block dalam memori, dan berbagai tulisan teknis tentang internal Prometheus menyebut biayanya sekitar satu hingga tiga kibibyte RAM per series, sehingga server yang menyimpan beberapa juta series aktif butuh puluhan gigabyte hanya untuk tetap hidup. Begitu melewati beberapa juta series, latensi query, waktu evaluasi rule, dan waktu replay WAL saat restart semuanya ikut memburuk bersamaan.
- Identifier bebas sebagai label: user_id, request_id, trace_id, session token, atau nilai apa pun yang dihasilkan per request bukan per tipe resource.
- Path HTTP mentah dengan parameter path yang dibiarkan apa adanya, misalnya garis miring orders garis miring 48213, bukan route yang sudah diberi template seperti garis miring orders garis miring titik dua id.
- Nama infrastruktur yang berumur pendek: nama pod, hash instance container, atau label hostname berumur pendek yang berubah setiap kali Kubernetes menjadwalkan ulang sebuah workload.
Sebuah metrik tidak perlu terlihat berbahaya untuk benar-benar berbahaya. Dua label yang tampak biasa saja, masing-masing dengan 1.000 nilai, akan bergabung secara perkalian menjadi satu juta series begitu keduanya muncul pada metrik yang sama. Risiko kardinalitas adalah hasil perkalian jumlah nilai label, bukan penjumlahan.
Ukur Dulu, Baru Optimasi
Menerka-nerka metrik mana yang jadi masalah membuang waktu dan sering berujung memangkas label yang sebenarnya bukan sumber masalah. Prometheus sudah menyediakan halaman diagnostik bawaan di endpoint slash tsdb-status pada server Anda yang mengurutkan nama metrik dan pasangan label berdasarkan berapa banyak series yang mereka sebabkan. Padukan ini dengan metrik prometheus_rule_evaluation_duration_seconds, yang memberi tahu Anda saat recording rules Anda sendiri mulai tertinggal dari interval evaluasinya, sinyal dini yang kuat bahwa pertumbuhan kardinalitas sudah melampaui strategi agregasi Anda.
| Sinyal diagnostik | Di mana menemukannya | Apa yang ditunjukkannya |
|---|---|---|
| Jumlah total series aktif | Bagian Head Stats pada halaman tsdb-status | Tekanan memori keseluruhan di seluruh server, dan seberapa dekat Anda dengan zona bahaya |
| Pasangan label diurutkan berdasarkan jumlah series | Tabel kardinalitas label pada halaman tsdb-status yang sama | Label mana pada metrik mana yang paling bertanggung jawab atas sebagian besar series Anda, sehingga Anda tahu apa yang harus diperbaiki lebih dulu |
| prometheus_rule_evaluation_duration_seconds | Endpoint slash metrics milik Prometheus sendiri, di-scrape seperti target lainnya | Apakah recording rules Anda masih mengikuti interval evaluasi yang dikonfigurasi atau diam-diam mulai tertinggal |
Recording Rules: Bayar Biayanya Sekali, Query Hasilnya Selamanya
Recording rule menjalankan sebuah ekspresi PromQL pada jadwal tetap dan menyimpan hasilnya sebagai time series baru sepenuhnya. Trik yang membuatnya efektif melawan kardinalitas adalah agregasi: daripada menampilkan detail per pod, per path, per status pada setiap query, Anda menghitung series yang sudah diagregasi sekali per interval evaluasi, lalu setiap dashboard dan alert membaca series murah yang sudah pra-agregasi itu, bukan menghitung ulang query mahal dari data mentah setiap kali. Konvensi penamaan resminya adalah level titik dua metric titik dua operations, di mana level menggambarkan label agregasi yang tersisa, metric menjaga nama metrik asli tetap tidak berubah, dan operations mendaftarkan fungsi yang diterapkan dengan yang terbaru di depan. Menjaga nama metrik tetap utuh dilakukan dengan sengaja: siapa pun yang membaca job_path titik dua http_request_duration_seconds titik dua p99_5m langsung tahu itu berasal dari http_request_duration_seconds.
# groups/http-slo.rules.yml
groups:
- name: http_slo_aggregations
interval: 30s
rules:
# level:metric:operations naming convention
- record: instance_path:http_requests:rate5m
expr: |
sum without (pod, replica) (
rate(http_requests_total[5m])
)
- record: job_path:http_request_duration_seconds:p99_5m
expr: |
histogram_quantile(
0.99,
sum without (pod, replica) (
rate(http_request_duration_seconds_bucket[5m])
)
)
- record: job:http_errors:ratio_rate5m
expr: |
sum without (pod, replica) (rate(http_requests_total{status=~"5.."}[5m]))
/
sum without (pod, replica) (rate(http_requests_total[5m]))
Selalu pasangkan agregasi dengan klausa without eksplisit yang mendaftarkan label yang Anda buang, bukan mendaftarkan label yang Anda pertahankan. Dengan begitu label baru yang ditambahkan ke metrik sumber di kemudian hari otomatis ikut terjaga. Dan jangan pernah merata-ratakan sebuah rasio atau merata-ratakan rata-rata antar instance, itu tidak valid secara statistik; agregasikan pembilang dan penyebut secara terpisah dulu, baru kemudian bagi.
Relabeling: Hentikan Kardinalitas Sebelum Sampai ke Storage
Recording rules berjalan pada data yang sudah di-scrape dan disimpan Prometheus, artinya series mentah berkardinalitas tinggi tetap membebani ingestion, WAL, dan memori head block sebelum proses roll-up itu terjadi. Jika sebuah label benar-benar tak terbatas dan Anda tidak pernah membutuhkan detail mentahnya, perbaikan yang lebih baik adalah menghentikannya saat waktu scrape menggunakan metric_relabel_configs. Ini berjalan setelah target di-scrape tapi sebelum sampel ditulis ke TSDB, sehingga Anda bisa membuang seluruh metrik yang tidak lagi diperlukan, atau menulis ulang nilai label menjadi sekumpulan bucket kecil yang terbatas.
scrape_configs:
- job_name: checkout-service
metric_relabel_configs:
# drop the raw histogram once the recording rule covers it
- source_labels: [__name__]
regex: "http_request_duration_seconds_bucket"
action: drop
# collapse unbounded path label into a bounded set
- source_labels: [path]
regex: "/orders/[0-9]+"
target_label: path
replacement: "/orders/:id"
# cap total series this target can expose per scrape
sample_limit: 10000
- Temukan metrik dan label yang bermasalah menggunakan halaman tsdb-status atau exporter khusus kardinalitas, dan pastikan label itu benar-benar tidak memiliki nilai analitis pada resolusi penuh.
- Tambahkan rule metric_relabel_configs pada job scrape_configs terkait: gunakan action drop untuk metrik yang tidak lagi diperlukan, atau penggantian regex untuk merangkum parameter path menjadi route yang sudah diberi template.
- Tetapkan sample_limit per target sebagai batas keras. Ini tidak memperbaiki akar masalah, tapi mencegah satu target yang bermasalah menjatuhkan seluruh server sementara Anda menerapkan perbaikan sesungguhnya.
- Reload Prometheus, tunggu satu interval scrape penuh, lalu periksa kembali halaman tsdb-status untuk memastikan jumlah series untuk metrik itu benar-benar turun sebelum lanjut ke masalah berikutnya.
Di Mana Recording Rules Tidak Cukup
Recording rules bukan solusi ajaib, dan penting untuk jujur tentang batasannya sebelum Anda mengandalkannya sebagai satu-satunya pertahanan. Karena beroperasi pada data yang sudah di-scrape dan ditulis ke TSDB, recording rules mengurangi biaya query dan dashboarding, tapi tidak melakukan apa pun untuk biaya ingestion dari series mentah. Agregasi streaming saat ingest, pada tool yang memang dibuat untuk tujuan itu, menghindari masalah ini dengan tidak pernah membiarkan series mentah berkardinalitas tinggi sampai ke storage jangka panjang sama sekali.
- Biaya ingestion tidak berubah: series mentah masih harus di-scrape, ditambahkan ke WAL, dan disimpan di head block sebelum rule Anda bisa mengagregasinya.
- Rule yang basi menumpuk: recording rule yang merujuk label yang sudah tidak lagi diekspos oleh metrik sumber akan diam-diam menghasilkan data kosong bukannya error yang jelas, jadi peninjauan berkala itu penting.
- Keterlambatan evaluasi adalah mode kegagalan yang nyata: jika prometheus_rule_evaluation_duration_seconds mulai merambat melewati interval evaluasi yang dikonfigurasi, series hasil roll-up Anda mulai tertinggal dari kenyataan, yang diam-diam merusak ambang batas alerting.
Jika kombinasinya tepat, hasilnya dramatis: dashboard yang tadinya butuh beberapa detik untuk merender dari histogram mentah kini tampil dalam hitungan milidetik dari series yang sudah pra-agregasi, dan Prometheus produksi berskala menengah yang sehat biasanya menetap di antara 100.000 hingga 2.000.000 total series aktif, jauh di bawah rentang di mana operator mulai melaporkan ketidakstabilan.
Checklist Rollout yang Bisa Dijalankan Minggu Ini
Mulailah dengan membuka halaman tsdb-status di server Anda sendiri dan catat lima pasangan label teratas berdasarkan jumlah series, daftar itulah urutan prioritas sesungguhnya, bukan tebakan. Untuk setiap masalah, tentukan apakah detail tersebut adalah sesuatu yang akan di-query manusia pada resolusi penuh saat insiden, yang berarti recording rule yang mengagregasinya adalah alat yang tepat, atau apakah itu benar-benar tak terbatas dan tidak pernah dibutuhkan pada resolusi penuh, yang berarti relabeling untuk membuangnya saat scrape adalah alat yang tepat. Kebanyakan perbaikan di produksi akhirnya memakai keduanya: relabel label yang benar-benar tak terbatas untuk menghentikan pendarahan, lalu tambahkan recording rules untuk agregasi yang paling sering di-query oleh dashboard dan alert Anda.
Sebelum mengirim grup recording rule baru ke produksi, validasi dengan perintah rule test milik promtool terhadap file fixture kecil. Ini menangkap kesalahan sintaks dan agregasi yang jelas-jelas salah dalam hitungan detik, jauh sebelum rule yang rusak diam-diam mengirimkan angka yang salah ke sistem alerting on-call Anda.
Masalah kardinalitas jarang terasa dramatis sampai saatnya tiba: memori yang merambat naik pelan-pelan selama berminggu-minggu, lalu OOM kill di saat yang paling buruk. Mengukur dulu dengan halaman tsdb-status, lalu menerapkan relabeling untuk menghentikan label tak terbatas di pintu masuk dan recording rules untuk membuat query yang benar-benar Anda jalankan jadi murah, mengubah rambatan pelan itu menjadi anggaran yang terkelola dan terpantau, bukan insiden yang berulang.

