Metrics dan Prometheus #

Jika log bertindak sebagai rekaman forensik individual untuk memahami apa yang terjadi pada sebuah request spesifik, maka metrik (metrics) adalah data agregat untuk melihat bagaimana kondisi kesehatan sistem secara keseluruhan. Di dalam kluster Kubernetes berskala besar, kita tidak bisa mengandalkan pembacaan log untuk mengetahui apakah kluster sedang mengalami degradasi performa atau kehabisan kapasitas. Kita membutuhkan indikator numerik time-series yang diukur secara berkala: berapa jumlah request per detik, berapa persentase error status 5xx, berapa latensi p99, dan berapa sisa kapasitas memori node. Prometheus telah menjadi standar industri (de facto standard) untuk pengumpulan metrik di Kubernetes karena dirancang khusus untuk lingkungan cloud-native yang dinamis, dinamis, dan sangat modular.


Arsitektur Pemantauan Prometheus di Kubernetes #

Prometheus menggunakan model pull-based, di mana server Prometheus secara aktif menarik (scrape) metrik dari endpoint HTTP /metrics milik target, alih-alih memaksa target mengirimkan (push) metrik secara berkala. Model ini sangat cocok untuk Kubernetes karena API Server Kubernetes dapat bertindak sebagai direktori dinamis (Service Discovery) yang memberi tahu Prometheus tentang keberadaan Pod baru yang siap dipantau.

flowchart TD
    subgraph PullTargets["Scrape Targets (Pull-Based)"]
        direction TB
        AppPod["App Pods (Instrumented with SDK, Port 9090 /metrics)"]
        cAdvisor["cAdvisor (Kubelet Port 10250)"]
        KSM["kube-state-metrics (State of K8s Objects)"]
        NodeExporter["node-exporter (Node Port 9100)"]
    end
    
    PrometheusServer["Prometheus Server (TSDB Engine)"] -->|"Scrape (Port 9090)"| PullTargets
    
    subgraph OperatorConfig["Prometheus Operator Config"]
        direction TB
        ServiceMonitor["ServiceMonitor / PodMonitor (CRD)"]
        PrometheusRule["PrometheusRule (Alert & Recording Rules)"]
    end
    
    PrometheusServer -.->|"Discover Targets via"| ServiceMonitor
    PrometheusServer -->|"Evaluasi Rules"| PrometheusRule
    
    PrometheusServer -->|"Kirim Alert"| Alertmanager["Alertmanager"]
    Alertmanager -->|"Push Alert"| SlackPager["Alert Channels (Slack / PagerDuty)"]
    
    Grafana["Grafana Dashboard"] -->|"PromQL Queries"| PrometheusServer

Komponen Ekosistem Prometheus #

  1. Prometheus Server: Jantung sistem yang mengeksekusi scraping, menyimpan data di database time-series lokal (TSDB), dan mengevaluasi aturan PromQL.
  2. Prometheus Operator: Operator Kubernetes yang mengotomatisasi deployment Prometheus dan mengizinkan konfigurasi target menggunakan Custom Resource Definitions (CRD) seperti ServiceMonitor dan PodMonitor.
  3. Kube-State-Metrics (KSM): Agen pemantau internal yang mendengarkan perubahan API objek Kubernetes (seperti status Deployment, Pod, PVC) dan mengeksposnya sebagai metrik numerik.
  4. Node-Exporter: Agen tingkat OS yang berjalan di setiap node untuk mengekspos metrik perangkat keras (penggunaan CPU, I/O disk, bandwidth jaringan host).
  5. cAdvisor: Utilitas yang tertanam langsung di dalam Kubelet untuk memantau metrik performa kontainer di tingkat cgroup (penggunaan memori kontainer, CPU throttling).

Memahami Tipe Metrik Prometheus #

Untuk menulis kueri PromQL yang benar, kita harus memahami empat tipe metrik dasar yang dikirim oleh client library Prometheus:

Tipe Metrik Kunci Prometheus:

1. Counter (Kumulatif Hanya Naik):
   - Nilai numerik yang hanya bisa naik (monotonik) atau kembali ke 0 saat aplikasi di-restart.
   - Wajib diakhiri dengan nama suffix '_total'.
   - Contoh: http_requests_total, process_cpu_seconds_total.
   - Aturan: Selalu gunakan fungsi rate() atau increase() saat melakukan kueri Counter.

2. Gauge (Nilai Naik-Turun):
   - Nilai numerik instan yang dapat naik atau turun secara bebas.
   - Contoh: memory_usage_bytes, active_threads, replica_count.
   - Aturan: Jangan pernah gunakan fungsi rate() pada metrik Gauge.

3. Histogram (Distribusi Frekuensi):
   - Mengukur distribusi peristiwa (seperti latensi request atau payload size) ke dalam bucket-bucket numerik yang ditentukan.
   - Menghasilkan tiga deret waktu (time-series) otomatis:
     - '<metric_name>_bucket{le="<upper_limit>"}' (jumlah peristiwa di bawah limit le)
     - '<metric_name>_sum' (jumlah total nilai yang diukur)
     - '<metric_name>_count' (jumlah total peristiwa)
   - Aturan: Gunakan fungsi histogram_quantile() untuk menghitung nilai persentil (p95, p99).

4. Summary (Kuantil Sisi Klien):
   - Mirip dengan Histogram, namun persentil (kuantil) dihitung secara langsung di sisi aplikasi client sebelum dikirim ke Prometheus.
   - Jauh lebih ringan bagi beban CPU server Prometheus, namun data Summary tidak dapat digabungkan (aggregated) secara matematis lintas pod/instance yang berbeda.

Instrumentasi Aplikasi Menggunakan SDK Resmi #

Kita harus menghindari penulisan format metrik secara manual (plaintext manual serialization). Selalu gunakan SDK resmi Prometheus client untuk melakukan registrasi metrik secara global guna memastikan format output /metrics mematuhi standar OpenMetrics.

Berikut adalah perbandingan penanganan instrumen metrik yang tidak aman (sinkron manual) dengan pendekatan standard menggunakan SDK resmi dalam bahasa Go dan Python.

// ANTI-PATTERN: Menulis endpoint metrik secara manual dengan memformat string secara sinkron.
// Pendekatan ini rentan terhadap kesalahan format dan membebani pemrosesan memori aplikasi.
func MetricsHandler(w http.ResponseWriter, r *http.Request) {
    // JANGAN: Membuat kalkulasi secara manual di HTTP thread
    fmt.Fprintf(w, "http_requests_total{status=\"200\"} %d\n", requestCount)
}

// ==============================================================================
// BENAR: Menggunakan SDK Resmi prometheus/client_golang.
// Registrasi metrik dilakukan sekali secara global, dan thread-safe otomatis.
package main

import (
    "net/http"
    "time"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    // Registrasi Counter dengan label dimensi
    httpRequestsTotal = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Jumlah total HTTP request yang diproses.",
        },
        []string{"method", "endpoint", "status_code"},
    )
    
    // Registrasi Histogram untuk Latensi
    httpRequestDuration = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "Durasi pemrosesan HTTP request dalam hitungan detik.",
            Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0},
        },
        []string{"method", "endpoint"},
    )
)

func InstrumentMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        // Bungkus ResponseWriter untuk menangkap status code (misal: 200, 500)
        wrappedWriter := NewResponseWriterWrapper(w)
        
        next.ServeHTTP(wrappedWriter, r)
        
        duration := time.Since(start).Seconds()
        
        // Rekam metrik secara aman dan asinkron
        httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, wrappedWriter.StatusString()).Inc()
        httpRequestDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
    })
}

func main() {
    // Expose /metrics endpoint bawaan SDK
    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":9090", nil)
}

Konfigurasi Target Scraping dengan ServiceMonitor #

Jika kita menggunakan Prometheus Operator, kita tidak perlu mengedit file konfigurasi prometheus.yml secara manual dan me-restart server Prometheus saat ada aplikasi baru. Kita cukup mendeploy manifest ServiceMonitor di tingkat namespace aplikasi.

Prometheus Operator akan memantau keberadaan file Custom Resource ini, mengonfigurasi ulang target scraping secara dinamis, dan menerapkannya ke Prometheus Server secara instan.

# Definisi Target Scraping: ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: billing-service-monitor
  namespace: monitoring       # Namespace tempat Prometheus Operator berjalan
  labels:
    release: prometheus-stack # Label pencocokan agar dikenali oleh instance Prometheus
spec:
  namespaceSelector:
    matchNames:
    - production              # Pantau namespace target
  selector:
    matchLabels:
      app: billing-service    # Cari Service yang memiliki label ini
  endpoints:
  - port: metrics-port        # Nama port Service yang terekspos
    path: /metrics
    interval: 15s             # Evaluasi scraping setiap 15 detik
    scrapeTimeout: 10s        # Timeout jika endpoint lambat merespon

Agar ServiceMonitor di atas dapat mendeteksi pod target, kita harus mengonfigurasi Service aplikasi dengan nama port yang sesuai:

# Service Aplikasi yang Diekspos
apiVersion: v1
kind: Service
metadata:
  name: billing-service
  namespace: production
  labels:
    app: billing-service
spec:
  selector:
    app: billing-app
  ports:
  - name: http-port
    port: 80
    targetPort: 8080
  - name: metrics-port        # Wajib cocok dengan deklarasi port di ServiceMonitor
    port: 9090
    targetPort: 9090

PromQL (Prometheus Query Language) adalah bahasa kueri yang sangat kuat namun membutuhkan pemahaman matematis yang tepat, terutama saat berurusan dengan Histogram.

1. Menghitung Latensi Persentil ke-99 (p99 Latency) #

Untuk menghitung berapa latensi maksimum yang dialami oleh 99% pengguna aplikasi kita, kita harus menggabungkan metrik Histogram dengan kueri berikut:

# Kueri p99 Latency per Endpoint
histogram_quantile(0.99,
  sum by (le, endpoint) (
    rate(http_request_duration_seconds_bucket{namespace="production"}[5m])
  )
)

[!CAUTION] Peringatan Kesalahan Umum: Kita wajib menggunakan operator sum by (le, ...) sebelum kueri histogram_quantile(). Jika kita mengabaikan label le (less or equal) dalam klausa sum, kalkulasi kuantil akan kehilangan referensi bucket dan menghasilkan nilai yang secara matematis salah atau bernilai NaN.

2. Mengukur Persentase Error Status 5xx (Error Rate %) #

Melihat jumlah error absolut (misalnya 10 error) tidaklah informatif jika kita tidak membandingkannya dengan total trafik. Kita harus menghitung rasio persentase kesalahan:

sum(rate(http_requests_total{status_code=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
* 100

3. Deteksi CPU Throttling Kontainer #

Terkadang sebuah aplikasi berjalan lambat meskipun utilisasi CPU rata-ratanya terlihat rendah. Ini biasanya disebabkan oleh CPU throttling akibat batas batas limit yang terlalu ketat.

# Hitung tingkat throttling CPU dalam hitungan detik per Pod
sum by (pod) (
  rate(container_cpu_cfs_throttled_seconds_total{namespace="production", container!=""}[5m])
)

Recording Rules untuk Optimalisasi Performa Dashboard #

Ketika kita memiliki dasbor Grafana yang memuat puluhan grafik PromQL yang kompleks (misalnya menghitung p99 latency di atas jutaan time-series), Prometheus Server harus membaca data disk secara besar-besaran setiap kali dasbor di-refresh. Hal ini dapat menyebabkan penggunaan memori (Out Of Memory / OOM) pada server Prometheus.

Untuk mengatasi ini, kita harus menerapkan Recording Rules untuk melakukan komputasi kueri kompleks tersebut secara berkala di latar belakang dan menyimpan hasilnya ke dalam nama metrik baru yang ringan.

# Definisi Recording Rules: PrometheusRule
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: billing-recording-rules
  namespace: monitoring
spec:
  groups:
  - name: billing-performance.rules
    interval: 30s # Hitung aturan setiap 30 detik
    rules:
    # 1. Pre-compute request rate per detik
    - record: job:http_requests:rate5m
      expr: sum by (job, method) (rate(http_requests_total[5m]))
      
    # 2. Pre-compute p99 latency
    - record: job:http_request_duration_p99:rate5m
      expr: histogram_quantile(0.99, sum by (job, le) (rate(httpRequestDuration_bucket[5m])))

Di tingkat Grafana Dashboard, kita cukup melakukan kueri terhadap nama metrik baru job:http_requests:rate5m yang berjalan sangat cepat karena berupa data instan tanpa operasi agregasi dinamis.


Empat Sinyal Emas (Four Golden Signals) #

Sesuai dengan panduan Google Site Reliability Engineering (SRE), berikut adalah empat sinyal utama yang wajib dipantau dan di-dasbor untuk setiap layanan aplikasi produksi:

Four Golden Signals Pemantauan:

1. Latency (Latensi):
   - Waktu yang dibutuhkan untuk melayani satu request.
   - Kueri p99: histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

2. Traffic (Throughput):
   - Ukuran permintaan atau volume beban pada jaringan.
   - Kueri: sum(rate(http_requests_total[5m])) by (job)

3. Errors (Tingkat Kesalahan):
   - Tingkat request yang gagal, diukur dalam persentase atau rasio.
   - Kueri: sum(rate(http_requests_total{status_code=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

4. Saturation (Kejenuhan):
   - Ukuran seberapa "penuh" sumber daya sistem Anda.
   - Kueri Memori: container_memory_working_set_bytes / container_spec_memory_limit_bytes * 100

Anti-Pattern Keamanan dan Performa Metrik #

Hindari kesalahan umum berikut dalam operasional pemantauan berbasis metrik:

1. Kardinalitas Tinggi (High Cardinality) #

Ini adalah pembunuh memori Prometheus nomor satu. Kardinalitas tinggi terjadi jika kita menyisipkan nilai label yang unik untuk setiap request, seperti user_id, email, transaction_uuid, atau ip_address ke dalam metrik.

// ANTI-PATTERN: Menyertakan ID transaksi unik ke dalam label metrik.
// Ini akan menghasilkan jutaan time-series di database TSDB dan memicu OOM Crash!
httpRequestsTotal.WithLabelValues("GET", "/checkout", "200", transactionUUID).Inc()

// ==============================================================================
// BENAR: Label hanya diisi oleh data kategorikal dengan nilai terbatas (low cardinality).
httpRequestsTotal.WithLabelValues("GET", "/checkout", "200").Inc()

2. Mengabaikan Scraping Timeout dan Interval #

Mengatur interval scraping terlalu pendek (misal < 1s) pada aplikasi Java yang memiliki waktu startup lambat. Hal ini memicu antrean pemrosesan internal (scrape overlap) yang meningkatkan latensi runtime kontainer. Gunakan interval rasional antara 15s hingga 30s.


Checklist Pemantauan Metrik Produksi #

Pastikan seluruh kriteria pemantauan berikut telah dipenuhi sebelum merilis layanan ke produksi:

METRIK & INSTRUMENTASI KODE:
  □ Aplikasi diinstrumentasikan menggunakan SDK resmi Prometheus.
  □ Penamaan metrik mematuhi konvensi standard (misal: Counter diakhiri '_total').
  □ Seluruh label metrik memiliki kardinalitas rendah (tidak ada UID, email, UUID).
  □ Metrik Histogram dikonfigurasi dengan batasan bucket yang sesuai dengan karakteristik aplikasi.
  □ Endpoint '/metrics' dilindungi dari akses publik luar menggunakan otentikasi internal atau pembatasan Ingress.

KONFIGURASI RUNTIME KUBERNETES:
  □ File 'ServiceMonitor' atau 'PodMonitor' dibuat untuk setiap microservice.
  □ Port metrics di Service aplikasi didefinisikan dengan nama yang jelas (misal: 'metrics-port').
  □ Kube-State-Metrics (KSM) aktif untuk memantau status replika dan kestabilan pod.
  □ cAdvisor aktif untuk memantau penggunaan limit memori kontainer.

KUERI & OPTIMALISASI:
  □ Dasbor utama memvisualisasikan 'Four Golden Signals' secara terpusat.
  □ Kueri PromQL histogram_quantile menyertakan klausa 'sum by (le)'.
  □ Recording Rules dikonfigurasi untuk kueri dasbor dengan beban komputasi tinggi.

Ringkasan #

  • Model Pull Memudahkan Auto-Discovery — Model penarikan metrik (pull-based) Prometheus menyederhanakan konfigurasi target; Prometheus secara otomatis menemukan Pod baru melalui integrasi Service Discovery.
  • Kardinalitas Tinggi Membunuh Memori — Jaga label metrik agar tetap kategorikal; menyisipkan data unik seperti ID user atau UUID akan menyebabkan server Prometheus mengalami OOM crash.
  • Recording Rules Mempercepat Grafana — Gunakan PrometheusRule recording rules untuk menghitung secara berkala kueri berat, menjaga dasbor Grafana tetap responsif saat dibuka.
  • Histogram Quantile Wajib Menggunakan le — Saat menghitung persentil latensi (p95/p99), pastikan operator sum Anda tidak membuang label le guna menghindari error matematika.
  • ** kube-state-metrics untuk Visibilitas Objek** — Manfaatkan metrik bawaan KSM untuk mendeteksi deviasi replika pod aplikasi tanpa perlu menulis kode pemantauan manual.
  • Pantau Four Golden Signals — Jadikan metrik Latency, Traffic, Errors, dan Saturation sebagai fondasi dasar setiap dasbor pemantauan layanan produksi.

← Sebelumnya: Logging   Berikutnya: Alerting →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact