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.

← Sebelumnya: Health Check   Berikutnya: Helm →

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