Best Practice

Best Practice #

Semua artikel dalam section ini membahas cara membangun komponen observability — logging, monitoring, alerting, tracing, health check. Artikel ini membahas cara membuatnya bekerja dengan baik di dunia nyata. Observability yang baik bukan sekadar kumpulan tool yang terinstal — ia adalah praktik yang membuat tim bisa memahami sistem, merespons insiden lebih cepat, dan membuat keputusan berbasis data. Ini adalah ringkasan prinsip dan anti-pattern yang paling penting.

Prinsip 1: Observability Harus Otomatis, Bukan Opsional #

# ANTI-PATTERN: observability sebagai langkah manual opsional
# "Nanti kalau sempat kita pasang monitoring..."
# Akibatnya: server baru tidak pernah benar-benar dimonitor

# BENAR: observability sebagai bagian dari provisioning server
# roles/common/tasks/main.yml — dijalankan di SETIAP server baru
- import_tasks: install.yml
- import_tasks: configure.yml
- import_tasks: node-exporter.yml      # Monitoring sistem — selalu
- import_tasks: filebeat.yml           # Logging terpusat — selalu
- import_tasks: health-check.yml       # Health endpoint — selalu

Setiap server baru harus langsung termonitor, log-nya langsung terkirim ke backend terpusat, dan health check-nya langsung berfungsi — tanpa langkah manual tambahan.


Prinsip 2: Tiga Pilar Observability Harus Terhubung #

Log, metrik, dan trace yang berdiri sendiri-sendiri hanya setengah berguna. Nilai sesungguhnya muncul saat ketiganya bisa saling dikaitkan:

Dari sebuah alert Prometheus:
  → Klik ke dashboard Grafana yang menampilkan rentang waktu insiden
  → Dari spike latensi di grafik, klik ke trace di Tempo
  → Dari trace yang lambat, klik ke log di Loki untuk baris yang relevan

Ini hanya mungkin jika:
  ✓ Log, metrik, dan trace menggunakan timestamp yang konsisten
  ✓ Label yang sama digunakan di semua sistem (service, environment, host)
  ✓ Trace ID ditulis di log sehingga bisa di-search
  ✓ Grafana dikonfigurasi dengan derived fields untuk navigasi antar-sistem
# Pastikan semua sistem menggunakan label yang konsisten
# Ini harus didefinisikan sebagai variabel Ansible dan digunakan di semua template

# group_vars/all.yml
observability_labels:
  environment: "{{ env }}"
  cluster: "{{ cluster_name }}"
  service: "{{ app_name | default('unknown') }}"
  host: "{{ inventory_hostname }}"

Prinsip 3: Alert Harus Actionable #

Alert yang tidak jelas harus dilakukan apa tidak berguna — bahkan berbahaya karena menyebabkan alert fatigue:

# ANTI-PATTERN: alert tanpa konteks
- alert: HighMemory
  expr: node_memory_MemAvailable_bytes < 500000000
  annotations:
    summary: "Memory rendah"

# BENAR: alert dengan konteks yang actionable
- alert: CriticallyLowMemory
  expr: >
    (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) < 0.1    
  for: 5m
  labels:
    severity: critical
  annotations:
    summary: "Memory kritis di {{ '{{' }} $labels.instance {{ '}}' }}"
    description: >
      Hanya {{ '{{' }} $value | humanizePercentage {{ '}}' }} memory tersisa.
      Proses dengan memory tertinggi mungkin menyebabkan masalah ini.      
    runbook_url: "https://wiki.company.com/runbooks/low-memory"
    # Runbook berisi: cara diagnosis, cara terminasi proses boros, cara eskalasi

Checklist untuk setiap alert yang dibuat:

Sebelum menambahkan alert baru, tanyakan:
  □ Siapa yang akan menerima alert ini?
  □ Apa yang harus mereka lakukan saat menerima alert ini?
  □ Sudah ada runbook yang menjelaskan langkah-langkahnya?
  □ Apakah severity sudah tepat? (critical = butuh respons segera, warning = bisa tunggu)
  □ Apakah ada durasi 'for' yang mencegah flapping?
  □ Sudah berapa kali alert ini false-alarm dalam sebulan terakhir?

Prinsip 4: Observability Infrastructure juga Harus Dimonitor #

Ini sering terlupakan: siapa yang memonitor Prometheus? Siapa yang memonitor Alertmanager?

# Prometheus yang memonitor dirinya sendiri
- job_name: prometheus
  static_configs:
    - targets: ['localhost:9090']

# Alert jika Alertmanager tidak bisa dijangkau
- alert: AlertmanagerDown
  expr: up{job="alertmanager"} == 0
  for: 1m
  labels:
    severity: critical
  annotations:
    summary: "Alertmanager tidak berjalan — semua alert tidak akan terkirim!"

# Alert jika Prometheus gagal scrape target
- alert: PrometheusTargetMissing
  expr: up == 0
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: "Target scrape tidak terjangkau: {{ '{{' }} $labels.job {{ '}}' }}/{{ '{{' }} $labels.instance {{ '}}' }}"

Anti-Pattern yang Paling Sering Ditemukan #

Anti-pattern 1: Dashboard yang tidak ada yang membaca

Gejala: Puluhan dashboard Grafana, tapi tidak ada yang tahu apa yang perlu dilihat saat insiden.
Solusi: Buat "runbook dashboard" — satu dashboard per service yang menampilkan
        persis apa yang dibutuhkan saat menginvestigasi masalah service tersebut.

Anti-pattern 2: Alert storm saat ada masalah besar

Gejala: Satu masalah memicu 50 alert sekaligus karena semua metrik ikut terdampak.
Solusi: Gunakan inhibit_rules di Alertmanager untuk menekan alert turunan.
        Gunakan error budget alert yang hanya alert sekali, bukan per-metrik.

Anti-pattern 3: Log tanpa struktur yang konsisten

# ANTI-PATTERN: log yang tidak terstruktur dan tidak konsisten
"Error: database connection failed"
"2024-03-15 ERROR db conn fail"
"[ERR] could not connect to postgres"

# BENAR: structured log dengan field yang konsisten
{"timestamp":"2024-03-15T14:30:00Z","level":"error","service":"myapp",
 "trace_id":"abc123","message":"database connection failed",
 "error":"dial tcp: connection refused","host":"app-01"}

Anti-pattern 4: Menyimpan semua trace

Gejala: Storage Tempo/Jaeger habis dalam hitungan hari.
Solusi: Implementasikan tail-based sampling:
        - Selalu simpan trace error dan trace lambat
        - Sample 5-10% trace normal
        - Retention 7-14 hari, bukan sebulan

Checklist Setup Observability untuk Service Baru #

Sebelum deploy service baru ke production:

LOGGING
  □ Log ditulis dalam format JSON terstruktur
  □ Trace ID ditulis di setiap log entry
  □ Log dikirim ke backend terpusat (Loki/Elasticsearch)
  □ Log rotation dikonfigurasi

MONITORING
  □ Node Exporter berjalan di host
  □ Application metrics tersedia di /metrics
  □ Service sudah menjadi scrape target di Prometheus

ALERTING
  □ Alert untuk availability (error rate tinggi)
  □ Alert untuk latency (P99 tinggi)
  □ Alert untuk saturation (resource hampir penuh)
  □ Setiap alert punya runbook

TRACING
  □ Aplikasi instrumentasi dengan OpenTelemetry SDK
  □ Trace ID diteruskan antar service (context propagation)
  □ Trace tersedia di Tempo/Jaeger

HEALTH CHECK
  □ Endpoint /health/live (liveness) tersedia
  □ Endpoint /health/ready (readiness) tersedia
  □ Load balancer dikonfigurasi untuk health check
  □ Kubernetes probe dikonfigurasi (jika di K8s)

SLO
  □ SLI didefinisikan (availability, latency)
  □ SLO target ditetapkan
  □ Recording rules untuk SLI dibuat
  □ Error budget alert dikonfigurasi
  □ Dashboard SLO dibuat

Ringkasan #

  • Observability harus otomatis — integrasikan ke role common sehingga setiap server baru langsung termonitor tanpa langkah manual.
  • Hubungkan tiga pilar: gunakan label yang konsisten (service, environment, host) di log, metrik, dan trace — korelasi antar sistem memberikan nilai yang jauh lebih besar dari masing-masing pilar secara terpisah.
  • Alert harus actionable — setiap alert harus menjawab “apa yang harus dilakukan?” dengan link ke runbook yang relevan. Alert tanpa tindakan yang jelas adalah noise.
  • Monitor infrastruktur monitoring — Alertmanager yang down adalah insiden yang paling berbahaya karena kamu tidak akan tahu ada masalah lain.
  • Sampling agresif untuk trace — simpan semua trace error, sample kecil trace normal. Storage mahal; insight dari error traces jauh lebih berharga.
  • Gunakan checklist observability saat deploy service baru — pastikan semua pilar terpasang sebelum traffic masuk ke production.

← Sebelumnya: SLO & SLA   ← Selanjutnya: Custom Module

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