Phần trước đo được readinessProbe là thứ tạo ra "cập nhật không gián đoạn". Bài này đo cả ba loại probe và cho thấy chúng làm ba việc hoàn toàn khác nhau.
livenessProbe hỏng → khởi động lại container
Probe trỏ vào một đường dẫn không tồn tại, periodSeconds=2, failureThreshold=3:
lần khởi động lại đầu tiên sau 8 giây
sau 30 giây nữa: Running, RESTARTS=3
Unhealthy: Liveness probe failed: HTTP probe failed with statuscode: 404
Tám giây = 3 lần thất bại × 2 giây, cộng độ trễ phát hiện.
Và nó không bao giờ dừng. Container khởi động lại, probe vẫn hỏng, giết lại. RESTARTS tăng mãi. Kubernetes có CrashLoopBackOff để giãn dần khoảng cách, nhưng vòng lặp không kết thúc.
livenessProbe trả lời đúng một câu hỏi: "tiến trình này còn dùng được không, hay đã kẹt và cần giết đi?"
Nó dành cho tình huống ứng dụng còn sống nhưng không làm việc được nữa — bế tắc luồng, rò rỉ bộ nhớ tới mức đứng hình, vòng lặp vô hạn. Với những trường hợp đó, khởi động lại là cách chữa hợp lý.
readinessProbe hỏng → gỡ khỏi Endpoints, không khởi động lại
Ba pod, probe trỏ vào đường dẫn không tồn tại:
rp-...-kn8qj 0/1 Running RESTARTS=0
rp-...-pd45j 0/1 Running RESTARTS=0
rp-...-wp5qv 0/1 Running RESTARTS=0
số endpoint của service: 0
Cột RESTARTS là 0. Pod vẫn Running, vẫn tốn CPU và RAM, chỉ không nhận lưu lượng.
Tạo tệp mà probe tìm trong một pod:
pod đó -> 1/1 Running
số endpoint: 1
Nó quay lại Endpoints trong vài giây, không cần khởi động lại gì.
readinessProbe trả lời câu hỏi khác: "ngay bây giờ pod này có nhận việc được không?"
Nó dành cho trạng thái tạm thời: đang khởi động, đang nạp bộ nhớ đệm, hàng đợi nội bộ đang đầy, hoặc phụ thuộc bên ngoài đang chậm. Với những trường hợp đó, ngừng gửi việc là đúng — giết container thì không.
startupProbe → bảo hai cái kia chờ
Ứng dụng mất 40 giây mới phục vụ được. livenessProbe với periodSeconds=2, failureThreshold=3 chỉ chịu được 6 giây:
RESTARTS |
|
|---|---|
A) chỉ có livenessProbe |
1 |
B) thêm startupProbe (30 × 2 = 60 giây) |
0 |
Không có startupProbe, ứng dụng khởi động chậm bị giết trước khi kịp sống. Và vì lần khởi động lại cũng mất 40 giây, nó có thể bị giết mãi — một vòng lặp mà từ ngoài trông giống hệt "ứng dụng bị crash".
startupProbe chạy trước, và tạm dừng cả liveness lẫn readiness cho tới khi nó qua. Sau đó nó không bao giờ chạy nữa.
Đây là cách đúng để xử lý ứng dụng khởi động lâu. Cách sai — và rất phổ biến — là nâng initialDelaySeconds của livenessProbe lên 60 giây: khi ấy bạn cũng làm chậm việc phát hiện lỗi thật trong suốt vòng đời container, không chỉ lúc khởi động.
Tóm lại
| Probe | Câu hỏi | Sai thì |
|---|---|---|
liveness |
"tiến trình còn dùng được không" | giết container |
readiness |
"giờ có nhận việc được không" | ngừng gửi việc |
startup |
"đã khởi động xong chưa" | hai cái kia chờ |
Nhầm liveness với readiness là nguyên nhân của phần lớn sự cố probe. Phần sau đo ba kiểu nhầm hay gặp nhất, và một trong số đó biến một trục trặc nhỏ của cơ sở dữ liệu thành vòng lặp khởi động lại toàn bộ.
Bốn kiểu probe
httpGet: {path: /healthz, port: 8080} # mã 200-399 là qua
tcpSocket: {port: 5432} # mở được kết nối là qua
exec: {command: ["sh","-c","..."]} # mã thoát 0 là qua
grpc: {port: 9000} # giao thức health của gRPC
exec đắt hơn nhiều so với ba cái kia — nó tạo một tiến trình mới trong container ở mỗi chu kỳ. Với periodSeconds: 1 và hàng trăm pod, đó là tải thật lên node.
Ưu tiên httpGet khi có thể. tcpSocket chỉ kiểm tra cổng mở, không kiểm tra ứng dụng có trả lời đúng không — nó qua ngay cả khi ứng dụng đã treo hoàn toàn.
Sáu tham số
initialDelaySeconds: 0 # chờ bao lâu trước lần kiểm đầu
periodSeconds: 10 # cách nhau bao lâu
timeoutSeconds: 1 # chờ trả lời bao lâu <- hay bị quên
successThreshold: 1 # bao nhiêu lần qua thì coi là khoẻ
failureThreshold: 3 # bao nhiêu lần hỏng thì coi là hỏng
terminationGracePeriodSeconds: ... # riêng cho liveness
timeoutSeconds mặc định 1 giây là con số hay gây tai nạn nhất — phần sau đo nó.
Thời gian phát hiện thật = periodSeconds × failureThreshold, cộng tối đa một chu kỳ. Với mặc định 10 × 3, một container kẹt mất tới 40 giây mới bị giết.
Ba nguyên tắc
livenessProbe phải rất rẻ và chỉ kiểm tra chính tiến trình. Không gọi cơ sở dữ liệu, không gọi dịch vụ khác. Phần sau đo lý do.
readinessProbe được phép kiểm tra phụ thuộc. Nếu ứng dụng không phục vụ được vì cơ sở dữ liệu chết, ngừng gửi lưu lượng tới nó là đúng.
Ứng dụng khởi động lâu thì dùng startupProbe, đừng nâng initialDelaySeconds.
Thử ba mươi giây
Xem probe của bạn phát hiện lỗi sau bao lâu:
kubectl get deployment <ten> -o jsonpath='{range .spec.template.spec.containers[*]}{.livenessProbe.periodSeconds}{" x "}{.livenessProbe.failureThreshold}{"\n"}{end}'
Nhân hai số. Đó là thời gian tệ nhất một container kẹt vẫn được coi là khoẻ. Nếu bạn để mặc định, con số đó là 30 giây — và trong 30 giây đó nó vẫn nằm trong Endpoints nếu readinessProbe không kiểm tra cùng thứ.
Phần sau đo ba cấu hình probe sai: một cái làm bốn pod khởi động lại vì cơ sở dữ liệu chớp tắt, một cái đưa toàn bộ service về không endpoint.