Anti-Pattern Observability #
Observability yang buruk adalah silent killer operasional — kamu tidak tahu ada masalah sampai pengguna mengeluh, kamu tidak tahu di mana masalahnya saat investigasi, dan kamu tidak tahu apakah fix yang kamu deploy sudah berhasil. Ironinya, banyak dari anti-pattern ini terasa seperti “sudah cukup” — ada logging, ada monitoring — tapi saat insiden nyata terjadi, infrastruktur observability yang ada tidak bisa memberikan jawaban yang dibutuhkan.
Anti-Pattern 1: Log Tanpa Struktur dan Tanpa Context #
# ANTI-PATTERN: log teks biasa tanpa context
import logging
def process_order(order_id):
try:
result = charge_payment(order_id)
logging.info("Order processed") # ← tidak ada order_id
return result
except Exception as e:
logging.error(f"Error: {e}") # ← tidak ada context apa pun
raise
Saat insiden jam 3 pagi:
Grep log untuk "Error" → ribuan baris dengan "Error: Connection timeout"
Tidak tahu order mana, tidak tahu user mana, tidak tahu dari Pod mana
→ Investigasi butuh jam hanya untuk menentukan scope masalah
# BENAR: structured log dengan context lengkap
import structlog
log = structlog.get_logger().bind(
service="order-service",
pod=os.getenv("POD_NAME"),
namespace=os.getenv("POD_NAMESPACE")
)
def process_order(order_id, user_id):
order_log = log.bind(order_id=order_id, user_id=user_id)
try:
result = charge_payment(order_id)
order_log.info("order_processed",
payment_amount=result.amount,
duration_ms=elapsed_ms)
return result
except PaymentError as e:
order_log.error("payment_failed",
error_code=e.code,
error_message=str(e))
raise
Anti-Pattern 2: Alert yang Terlalu Banyak dan Tidak Actionable #
# ANTI-PATTERN: alert untuk setiap metrik yang ada
- alert: CpuHigh
expr: container_cpu_usage > 50 # ← 50% CPU bukan masalah!
- alert: MemoryHigh
expr: container_memory_usage > 512Mi # ← tidak ada context: limit berapa?
- alert: PodRestartedOnce
expr: increase(restarts[5m]) > 0 # ← setiap restart = alert!
- alert: DiskUsage30Percent
expr: disk_usage > 0.3 # ← 30% bukan masalah sama sekali
Konsekuensi: alert fatigue
Tim menerima 200+ alert per hari
Engineer mulai mengabaikan alert karena kebanyakan false positive
Saat ada insiden nyata, alert real tenggelam dalam noise
→ Mean-time-to-detect meningkat drastis
# BENAR: alert yang meaningful dan actionable
- alert: HighErrorRate
expr: error_rate > 0.05 # 5% error rate berdampak pada pengguna
for: 3m # bukan spike sementara
annotations:
runbook_url: "..." # ada tindakan yang jelas
- alert: ContainerApproachingMemoryLimit
expr: memory_usage / memory_limit > 0.90 # 90% dari limit = hampir OOMKilled
for: 5m
# Ini actionable: scale up atau increase limit
Anti-Pattern 3: Tidak Ada Distributed Tracing #
Gejala: insiden di sistem microservice yang sulit debug
"API lambat" → cek log api-gateway → tidak ada error
Cek log auth-service → tidak ada error
Cek log order-service → ada beberapa warning
Cek log payment-service → tidak ada error
Cek log notification-service → tidak ada error
Bottleneck ada di database call di order-service yang tidak terdokumentasi
di log manapun. Investigasi: 3 jam. Root cause: 5 menit untuk fix.
Dengan distributed tracing:
Buka trace untuk request yang lambat
Immediately visible: payment-service DB query 800ms dari total 850ms
Root cause: 10 menit, fix: 5 menit
Anti-Pattern 4: Dashboard Tanpa Baseline dan Threshold #
ANTI-PATTERN: dashboard dengan angka mentah tanpa konteks
Panel: "CPU Usage: 2.3 cores"
→ Apakah ini normal? Tinggi? Rendah? Tidak ada yang tahu.
Panel: "Request count: 15,432"
→ Lebih banyak dari biasanya? Lebih sedikit? Tidak ada cara tahu.
Panel: "Error count: 23"
→ Hari ini 23, kemarin biasanya berapa?
BENAR: dashboard dengan konteks yang bermakna
Panel: CPU Usage vs Limit
Query: (cpu_usage / cpu_limit) * 100
Gauge: 0-100%, threshold: 60% kuning, 85% merah
→ Langsung terlihat seberapa dekat dengan batas
Panel: Error Rate (dibandingkan kemarin)
→ Tampilkan garis hari ini dan hari kemarin di chart yang sama
→ Anomaly langsung terlihat
Panel: Request Rate (per 5 menit)
→ Bukan total, tapi rate — lebih mudah dibandingkan antar waktu
Anti-Pattern 5: Health Check yang Tidak Representatif #
# ANTI-PATTERN: health endpoint yang terlalu simpel
@app.get("/health")
async def health():
return {"status": "ok"} # ← selalu return 200, bahkan jika database mati
Konsekuensi:
Database mati → Pod tetap "healthy" dari perspektif Kubernetes
Traffic terus dikirim ke Pod → semua request gagal 500
Kubernetes tidak akan restart atau hapus dari Endpoints
Operator tidak tahu ada masalah sampai pengguna mengeluh
# ANTI-PATTERN lain: liveness probe yang cek terlalu banyak
@app.get("/health/live")
async def liveness():
db.execute("SELECT 1") # ← tidak tepat di liveness!
redis.ping() # ← tidak tepat di liveness!
check_third_party_api() # ← pasti tidak tepat
return {"status": "ok"}
# Jika database atau Redis down → semua Pod restart → cascade failure
Anti-Pattern 6: Retensi Log yang Tidak Memadai untuk Investigasi #
ANTI-PATTERN: log hanya disimpan 24 jam
Skenario nyata:
Senin pagi tim menemukan data corruption yang terjadi hari Jumat malam
Ingin investigasi: siapa yang mengubah data, kapan, apa yang terjadi
Log Jumat malam sudah dihapus
→ Investigasi tidak bisa dilakukan, root cause tidak diketahui
→ Harus bertindak tanpa informasi lengkap
Kebijakan retensi yang masuk akal:
Hot storage (akses cepat): 14-30 hari
Audit trail (compliance): 90 hari - 1 tahun
Security incident investigation: minimal 90 hari
Anti-Pattern 7: Metrics yang Tidak Mencerminkan Pengalaman Pengguna #
ANTI-PATTERN: hanya monitor internal metrics tanpa synthetic monitoring
Cluster terlihat sehat:
CPU: 30% ✓
Memory: 40% ✓
Pod count: semua running ✓
Error rate di Prometheus: 0% ✓
Tapi pengguna tidak bisa login karena:
→ Load balancer misconfigured (traffic tidak sampai ke cluster)
→ SSL certificate expired
→ CDN yang duduk di depan sedang bermasalah
→ DNS propagation belum selesai setelah perubahan
Internal metrics semua green, tapi pengguna melihat error.
Solusi: synthetic monitoring
→ Jalankan "canary request" dari external ke endpoint production setiap N detik
→ Alert jika canary gagal, bahkan jika internal metrics OK
→ Blackbox monitoring: test dari perspektif pengguna
Ringkasan #
- Log tanpa context tidak berguna saat insiden — selalu sertakan request ID, user ID, order ID, dan Pod metadata; log harus bisa menjawab “siapa, apa, kapan, di mana” tanpa query tambahan.
- Alert fatigue lebih berbahaya dari tidak ada alert — lebih baik 5 alert yang selalu actionable daripada 200 alert yang sering false positive; review dan prune alert secara berkala.
- Distributed tracing bukan luxury untuk microservice — tanpa tracing, investigasi latensi di sistem multi-service bisa butuh jam; dengan tracing, butuh menit.
- Dashboard butuh threshold dan konteks — angka mentah tanpa baseline tidak bermakna; tampilkan persentase dari limit, bandingkan dengan periode sebelumnya, berikan threshold warna.
- Liveness probe hanya cek kondisi internal — liveness yang cek database akan membuat cascade failure saat dependency down; bedakan dengan readiness yang boleh cek external.
- Synthetic monitoring untuk perspektif pengguna — internal metrics bisa semua green sementara pengguna tidak bisa mengakses; test dari luar cluster untuk menutup blind spot ini.