Skip to content

Challenge: Observability Debug

⚔️ THỰC HÀNH THỰC CHIẾN

Module này là bài tập debug từ góc nhìn Senior SRE On-Call tại HPN. Mục tiêu: Rèn luyện kỹ năng điều tra sự cố production sử dụng Prometheus, Grafana, và Loki.

📋 Kịch Bản: 3:00 AM Production Incident

Tình huống

Bạn đang on-call và nhận được alert lúc 3:00 AM:

text
🚨 FIRING: High Error Rate Detected
   Severity: Critical
   Service: payment-service
   Namespace: production
   
   Error Rate: 15.2% (threshold: 1%)
   Started: 2024-01-15 03:00:12 UTC
   
   Runbook: /runbooks/high-error-rate.md

Triệu chứng

Grafana Dashboard hiển thị:

text
╔══════════════════════════════════════════════════════════════════╗
║  📊 PAYMENT SERVICE - GOLDEN SIGNALS                            ║
╠══════════════════════════════════════════════════════════════════╣
║                                                                  ║
║  ⏱️ Latency P99:  █████████████░░  3.2s  (normal: 200ms) 🔴     ║
║  📈 Traffic:      ████████░░░░░░░  850 RPS                      ║
║  ❌ Error Rate:   ███████████████  15.2% 🔴🔴🔴                  ║
║  🔋 Saturation:   ███████████░░░░  CPU 78%                      ║
║                                                                  ║
╚══════════════════════════════════════════════════════════════════╝

💀 Step 1: Initial Triage

Kiểm tra pods status

bash
$ kubectl get pods -n production -l app=payment-service
NAME                              READY   STATUS    RESTARTS   AGE
payment-service-7d9f8b6c4d-abc1   1/1     Running   0          2d
payment-service-7d9f8b6c4d-def2   1/1     Running   0          2d
payment-service-7d9f8b6c4d-ghi3   1/1     Running   0          2d

Pods đang Running, không có restart. → Vấn đề không phải crash.

Query Prometheus

Query 1: Error rate breakdown by status code

promql
sum(rate(http_requests_total{service="payment-service", status=~"5.."}[5m])) 
by (status)

Kết quả:

StatusRate (req/s)
5008.5
503120.3
5042.1

💡 NHẬN XÉT

503 Service Unavailable chiếm đa số! Đây thường là dấu hiệu của:

  • Upstream service không respond
  • Connection pool exhausted
  • Rate limiting

🧠 Bài Tập

Câu hỏi 1: Tiếp tục điều tra

Với thông tin trên, bạn sẽ query thêm những gì để tìm root cause?

💡 Gợi ý
  • Service payment-service phụ thuộc vào database và Redis
  • 503 thường liên quan đến upstream dependencies
  • Check connection pool metrics

Viết 2-3 PromQL queries tiếp theo trước khi xem đáp án.


Câu hỏi 2: Xác định Root Cause

Sau khi chạy thêm queries, bạn có kết quả sau:

promql
# Database connection pool
pg_pool_active_connections{service="payment-service"} → 50
pg_pool_max_connections{service="payment-service"} → 50  # 💀 MAX!

# Database query latency
histogram_quantile(0.99, rate(pg_query_duration_seconds_bucket[5m])) → 2.8s

Dựa trên dữ liệu này, root cause là gì?


Đáp Án

Step-by-Step Investigation

Investigation Query 1: Check upstream latency

promql
# Latency của calls từ payment-service đến database
histogram_quantile(0.99, 
  rate(http_client_request_duration_seconds_bucket{
    caller="payment-service",
    callee="postgres"
  }[5m])
)
# Kết quả: 2.8 seconds (bình thường: < 50ms)

Investigation Query 2: Check connection pool

promql
# Active connections / Max connections
pg_pool_active_connections / pg_pool_max_connections
# Kết quả: 1.0 (100% - EXHAUSTED!)

Investigation Query 3: Check slow queries

promql
# Top slow queries
topk(5, rate(pg_query_duration_seconds_sum[5m]) / rate(pg_query_duration_seconds_count[5m]))

Root Cause Analysis

text
╔══════════════════════════════════════════════════════════════════════════════╗
║                    🔍 ROOT CAUSE ANALYSIS                                    ║
╠══════════════════════════════════════════════════════════════════════════════╣
║                                                                              ║
║  🎯 PRIMARY CAUSE: Database Connection Pool Exhaustion                      ║
║                                                                              ║
║  ┌─────────────────────────────────────────────────────────────────────┐    ║
║  │  Timeline:                                                          │    ║
║  │                                                                     │    ║
║  │  02:45  - Slow query starts (missing index on new column)          │    ║
║  │  02:50  - Query latency: 50ms → 2.8s                               │    ║
║  │  02:55  - Connection pool fills up (queries waiting)               │    ║
║  │  03:00  - Pool FULL (50/50) → New requests get 503                 │    ║
║  │  03:00  - 🚨 ALERT FIRES                                           │    ║
║  └─────────────────────────────────────────────────────────────────────┘    ║
║                                                                              ║
║  💡 WHY 503?                                                                 ║
║     - Application has connection timeout = 5s                               ║
║     - All 50 connections busy with slow queries                             ║
║     - New request can't get connection → 503 after timeout                  ║
║                                                                              ║
╚══════════════════════════════════════════════════════════════════════════════╝

The Fix

Immediate (Mitigation):

bash
# 1. Kill slow queries
kubectl exec -it postgres-0 -- psql -c "SELECT pg_terminate_backend(pid) 
  FROM pg_stat_activity 
  WHERE duration > interval '10 seconds' 
  AND state = 'active';"

# 2. Increase connection pool temporarily
kubectl set env deployment/payment-service DB_POOL_SIZE=100

Permanent (Resolution):

sql
-- Add missing index
CREATE INDEX CONCURRENTLY idx_transactions_created_at 
ON transactions(created_at);

-- Verify
EXPLAIN ANALYZE SELECT * FROM transactions 
WHERE created_at > NOW() - INTERVAL '1 day';

📊 Observability Pitfalls

⚠️ NHỮNG LỖI THƯỜNG GẶP

1. Missing Labels = Can't Filter

promql
# ❌ Không có label 'endpoint' → không biết endpoint nào gây lỗi
http_requests_total{status="500"} 

# ✅ Có label đầy đủ
http_requests_total{status="500", endpoint="/api/payment", method="POST"}

2. Cardinality Explosion = Prometheus OOM

promql
# ❌ Label với giá trị unique (user_id, request_id)
http_requests_total{user_id="..."} # → Triệu series!

# ✅ Label với bounded values
http_requests_total{user_tier="premium"} # → Vài series

3. Alert Fatigue = Ignored Critical Alerts

yaml
# ❌ SAI: Alert với threshold quá nhạy
- alert: HighLatency
  expr: latency_p99 > 100ms  # Fires 100 lần/ngày → Bị ignore

# ✅ ĐÚNG: Alert có ý nghĩa
- alert: HighLatency
  expr: latency_p99 > 500ms
  for: 5m  # Phải kéo dài 5 phút

4. No Runbook = Panic at 3 AM

yaml
# ✅ ĐÚNG: Alert có runbook
annotations:
  runbook_url: https://wiki/runbooks/high-error-rate.md
  summary: "Error rate {{ $value }}% exceeds threshold"

Production Observability Checklist

✅ CHECKLIST CHO PRODUCTION

Metrics Collection:

  • [ ] Application expose /metrics endpoint
  • [ ] Labels có bounded cardinality (< 100 unique values)
  • [ ] Có metrics cho 4 Golden Signals (Latency, Traffic, Errors, Saturation)
  • [ ] Database connection pool metrics được expose

Alerting:

  • [ ] Mỗi alert có severity (critical, warning, info)
  • [ ] Mỗi alert có runbook link
  • [ ] Alert thresholds được tune (không quá nhạy)
  • [ ] PagerDuty/Slack routing được setup

Dashboards:

  • [ ] Dashboard overview cho mỗi service
  • [ ] Dashboard có time range selector
  • [ ] Dashboard có drill-down links
  • [ ] Critical metrics có annotations cho incidents

On-Call:

  • [ ] Runbooks được viết và test
  • [ ] Escalation policy được define
  • [ ] Post-mortem process established

🎯 Key Takeaways

📝 BÀI HỌC RÚT RA

  1. Golden Signals First - Luôn check Latency, Traffic, Errors, Saturation đầu tiên
  2. Follow the dependency chain - 503 thường là upstream issue
  3. Connection pools are critical - Monitor và alert khi > 80%
  4. Labels are your friend - Đầu tư vào labeling strategy sớm
  5. Runbooks save lives - Viết runbook TRƯỚC khi incident xảy ra

PromQL Cheat Sheet cho On-Call

promql
# Error rate
sum(rate(http_requests_total{status=~"5.."}[5m])) 
/ sum(rate(http_requests_total[5m])) * 100

# Latency P99
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

# Connection pool saturation
pg_pool_active_connections / pg_pool_max_connections * 100

# Memory usage
container_memory_usage_bytes / container_spec_memory_limit_bytes * 100

# CPU throttling
rate(container_cpu_cfs_throttled_seconds_total[5m])

🔗 Liên kết