Masalah: Metrik Terpisah, Butuh Digabung
Anda punya http_requests_total{instance} dan ingin menampilkan versi aplikasi yang hanya ada di metrik app_build_info{instance, versi}. Ini pekerjaan untuk vector matching many-to-one.
Pola "Info Metric"
Konvensi umum: buat satu metrik gauge bernilai 1 yang isinya hanya metadata.
app_build_info{versi="2.4.1", commit="a3f21c", instance="10.0.0.5:8080"} 1
kube_pod_labels{pod="api-7d4f", label_tim="checkout", label_tier="backend"} 1
node_uname_info{instance="10.0.0.5:9100", release="6.8.0-51-generic"} 1group_left — Menempelkan Label
# Laju request, DITAMBAH label versi dari app_build_info
sum by (instance) (rate(http_requests_total[5m]))
* on (instance) group_left(versi)
app_build_info
# Baca dari kiri ke kanan:
# * on (instance) -> cocokkan berdasarkan label instance
# group_left(versi) -> sisi KIRI boleh punya banyak series per
# satu series di kanan; ambil label 'versi'
# dari sisi kanan| Modifier | Artinya |
|---|---|
group_left | Sisi kiri "banyak", sisi kanan "satu". Label diambil dari kanan. |
group_right | Kebalikannya: sisi kanan "banyak", kiri "satu". |
on (a, b) | Cocokkan hanya berdasarkan label a dan b. |
ignoring (c) | Cocokkan berdasarkan semua label kecuali c. |
found duplicate series for the match groupIni error yang paling sering muncul saat memakai join. Artinya: sisi "satu" ternyata punya lebih dari satu series untuk kunci pencocokan yang sama.
Penyebab umum: aplikasi mengekspos dua app_build_info (misalnya setelah deploy versi baru, keduanya sempat ada bersamaan).
Solusi: paksa menjadi satu dengan agregasi:
... * on (instance) group_left(versi)
max by (instance, versi) (app_build_info)Contoh Nyata: Menghubungkan Pod ke Tim Pemilik
# CPU per pod, ditambah label tim dari kube_pod_labels
sum by (pod, namespace) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
* on (pod, namespace) group_left(label_tim)
max by (pod, namespace, label_tim) (kube_pod_labels)
# Lalu bisa diagregasi per tim -> laporan biaya per tim
sum by (label_tim) (
sum by (pod, namespace) (rate(container_cpu_usage_seconds_total{container!=""}[5m]))
* on (pod, namespace) group_left(label_tim)
max by (pod, namespace, label_tim) (kube_pod_labels)
)Subquery — Query di Atas Query
Menjalankan sebuah ekspresi berulang kali sepanjang rentang waktu, menghasilkan range vector baru.
# Sintaks: <ekspresi>[rentang_total:langkah]
# Laju request tertinggi dalam 1 jam terakhir,
# dihitung dari rate 5 menit setiap 1 menit
max_over_time( rate(http_requests_total[5m])[1h:1m] )
# Apakah error rate pernah melebihi 5% dalam 6 jam terakhir?
max_over_time(
(
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))
)[6h:5m]
) > 0.05
# Berapa lama (detik) CPU berada di atas 90% dalam 24 jam?
sum_over_time( (avg(rate(node_cpu_seconds_total{mode!="idle"}[5m])) > bool 0.9)[24h:1m] ) * 60[24h:1m] berarti Prometheus menjalankan ekspresi bagian dalam 1.440 kali. Bila ekspresi itu sendiri berat, satu query bisa memakan puluhan detik dan menghabiskan memori.
Jangan pernah menaruh subquery di dalam alerting rule. Ganti dengan recording rule yang menghitung nilai dasarnya secara berkala.
Manipulasi Label
# Ekstrak hostname dari "10.0.0.5:9100" menjadi "10.0.0.5"
label_replace(up, "host", "$1", "instance", "([^:]+):.*")
# Gabungkan beberapa label menjadi satu
label_join(up, "kunci", "/", "job", "instance")
# Menyamakan nama label agar dua metrik dari sumber berbeda bisa di-join
label_replace(metrik_a, "instance", "$1", "node", "(.*)")