Skip to content

Challenge: Resiliency Debugging

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

Module này là bài tập debug từ góc nhìn Senior SRE tại HPN. Mục tiêu: Rèn luyện kỹ năng phân tích và khắc phục sự cố về Probes.

📋 Kịch Bản

Tình huống

Bạn vừa deploy một ứng dụng Java Spring Boot lên Kubernetes. Ứng dụng này có đặc điểm:

  • Thời gian khởi động: 60 giây (load config, warm up JIT, connect DB)
  • Framework: Spring Boot with Actuator

Triệu chứng

bash
$ kubectl get pods
NAME                     READY   STATUS             RESTARTS   AGE
myapp-7d9f8b6c4d-x2k9j   0/1     CrashLoopBackOff   5          3m
bash
$ kubectl describe pod myapp-7d9f8b6c4d-x2k9j

Events:
  Warning  Unhealthy  2m30s  kubelet  Liveness probe failed: Get "http://10.244.1.5:8080/healthz": dial tcp 10.244.1.5:8080: connect: connection refused
  Warning  Unhealthy  2m20s  kubelet  Liveness probe failed: Get "http://10.244.1.5:8080/healthz": dial tcp 10.244.1.5:8080: connect: connection refused
  Warning  Unhealthy  2m10s  kubelet  Liveness probe failed: Get "http://10.244.1.5:8080/healthz": dial tcp 10.244.1.5:8080: connect: connection refused
  Normal   Killing    2m10s  kubelet  Container myapp failed liveness probe, will be restarted

Pod liên tục bị RESTART và rơi vào trạng thái CrashLoopBackOff!


💀 The Deadly YAML

Đây là YAML file đang được sử dụng:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: mycompany/java-app:1.0
        ports:
        - containerPort: 8080
        
        # 💀 THE DEADLY PROBE
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10  # ⚠️ Chỉ đợi 10 giây
          periodSeconds: 5          # Kiểm tra mỗi 5 giây
          failureThreshold: 3       # 3 lần fail = restart

🧠 Bài Tập

Câu hỏi 1: Phân tích nguyên nhân

💡 Gợi ý

So sánh:

  • Thời gian app cần để khởi động: 60 giây
  • initialDelaySeconds: 10 giây
  • Khi nào liveness probe bắt đầu check?
  • Khi nào liveness probe sẽ fail đủ 3 lần?

Hãy vẽ timeline và giải thích tại sao Pod bị restart.


Câu hỏi 2: Đề xuất giải pháp

Đề xuất 2 giải pháp để khắc phục vấn đề này:

  1. Solution A: Giải pháp đơn giản (Quick Fix)
  2. Solution B: Giải pháp tốt hơn (Best Practice)

Đáp Án

Phân tích nguyên nhân

text
╔══════════════════════════════════════════════════════════════════════════════╗
║               ⏱️  TIMELINE: TẠI SAO POD BỊ RESTART?                          ║
╠══════════════════════════════════════════════════════════════════════════════╣
║                                                                              ║
║  APP CẦN: 60 GIÂY ĐỂ KHỞI ĐỘNG                                              ║
║  ══════════════════════════════════════════════════════►                    ║
║                                                                              ║
║  0s         10s        15s        20s        25s                            ║
║  │          │          │          │          │                              ║
║  ▼          ▼          ▼          ▼          ▼                              ║
║  ┌──────┐   ┌──────┐   ┌──────┐   ┌──────┐   ┌──────────┐                   ║
║  │START │ → │❌ #1 │ → │❌ #2 │ → │❌ #3 │ → │💀 RESTART│                   ║
║  └──────┘   └──────┘   └──────┘   └──────┘   └──────────┘                   ║
║                                                                              ║
║  📌 initialDelaySeconds = 10s (chờ 10s rồi bắt đầu check)                   ║
║  📌 periodSeconds = 5s (check mỗi 5 giây)                                   ║
║  📌 failureThreshold = 3 (3 lần fail = restart)                             ║
║                                                                              ║
║  ⚡ TÍNH TOÁN: 10s + (3 × 5s) = 25s → Pod bị KILL sau 25 giây               ║
║  ⚡ APP CẦN: 60s để ready                                                   ║
║  ⚡ KẾT QUẢ: RESTART LOOP VÔ HẠN! ♾️                                         ║
║                                                                              ║
╚══════════════════════════════════════════════════════════════════════════════╝

Timeline chi tiết:

Thời điểmSự kiệnApp Status
0sContainer startStarting...
10sFirst liveness check❌ FAIL (port chưa mở)
15sSecond liveness check❌ FAIL
20sThird liveness check❌ FAIL
25s💀 RESTART (3 failures)-
25sContainer start lại...Starting...
35sFirst liveness check❌ FAIL
...RESTART LOOP♾️

🔑 KẾT LUẬN

Liveness probe KILL container trước khi app kịp khởi động!

  • App cần: 60 giây
  • Liveness kill sau: 25 giây (10s delay + 3×5s checks)
  • Kết quả: Infinite restart loop

Solution A: Quick Fix - Tăng initialDelaySeconds

yaml
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 90  # ✅ Đợi 90 giây (> 60s startup)
  periodSeconds: 10
  failureThreshold: 3

Ưu điểm:

  • ✅ Đơn giản, nhanh chóng
  • ✅ Giải quyết được vấn đề ngay

Nhược điểm:

  • ⚠️ Nếu app deadlock trong 90s đầu → không phát hiện được
  • ⚠️ Phải "đoán" thời gian startup
  • ⚠️ Không linh hoạt khi hardware khác nhau (slow node = startup lâu hơn)

Solution B: Best Practice - Sử dụng startupProbe

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: mycompany/java-app:1.0
        ports:
        - containerPort: 8080
        
        # 🐢 STARTUP PROBE: Bảo vệ slow startup
        startupProbe:
          httpGet:
            path: /healthz
            port: 8080
          # Cho phép tối đa 300s (5 phút) để startup
          # failureThreshold × periodSeconds = 30 × 10 = 300s
          failureThreshold: 30
          periodSeconds: 10
        
        # 💓 LIVENESS PROBE: Chỉ chạy SAU KHI startup xong
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 0  # Không cần delay - đã có startupProbe
          periodSeconds: 10
          failureThreshold: 3
          timeoutSeconds: 5
        
        # 🛡️ READINESS PROBE: Kiểm soát traffic
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 0
          periodSeconds: 5
          failureThreshold: 3

Flow hoạt động:

Ưu điểm:

  • ✅ Startup probe cho phép app có đủ thời gian khởi động
  • ✅ Liveness probe vẫn nhạy (detect deadlock nhanh sau startup)
  • ✅ Linh hoạt - không cần "đoán" thời gian startup chính xác
  • ✅ Best practice theo Kubernetes documentation

📊 So Sánh Hai Giải Pháp

AspectSolution ASolution B
Độ phức tạpĐơn giảnPhức tạp hơn
Startup protection⚠️ Fixed delay✅ Dynamic check
Post-startup sensitivity⚠️ Chậm (90s delay)✅ Nhạy (immediate)
Detect deadlock⚠️ Sau 90s mới bắt đầu✅ Ngay khi startup xong
Production-ready⚠️ Quick fix✅ Best practice

🎯 Key Takeaways

📝 BÀI HỌC RÚT RA

  1. Hiểu app của bạn - Biết thời gian startup thực tế
  2. Startup Probe - Dùng cho apps có startup chậm
  3. Liveness ≠ Startup - Tách biệt hai concerns
  4. Test locally - Simulate slow startup trước khi deploy
  5. Monitor - Theo dõi restart count trong Prometheus
promql
# Query để phát hiện restart loop
increase(kube_pod_container_status_restarts_total[1h]) > 5

🔗 Liên kết