Giao diện
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 3mbash
$ 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 restartedPod 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:
- Solution A: Giải pháp đơn giản (Quick Fix)
- 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ểm | Sự kiện | App Status |
|---|---|---|
| 0s | Container start | Starting... |
| 10s | First liveness check | ❌ FAIL (port chưa mở) |
| 15s | Second liveness check | ❌ FAIL |
| 20s | Third liveness check | ❌ FAIL |
| 25s | 💀 RESTART (3 failures) | - |
| 25s | Container start lại... | Starting... |
| 35s | First 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: 3Flow 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
| Aspect | Solution A | Solution B |
|---|---|---|
| Độ phức tạp | Đơn giản | Phứ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
- Hiểu app của bạn - Biết thời gian startup thực tế
- Startup Probe - Dùng cho apps có startup chậm
- Liveness ≠ Startup - Tách biệt hai concerns
- Test locally - Simulate slow startup trước khi deploy
- 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
- Lý thuyết: Module 9: Probes (Health Check)
- Tiếp theo: Module 10: HPA & Metrics Server