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
commonsehingga 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.