Họ đang đo gì
Bạn có hiểu vòng đời tiến trình dưới orchestrator không, và có biết vì sao chỉ bắt SIGTERM là chưa đủ.
Trả lời ngắn~30 giây
Vì việc gỡ pod khỏi danh sách endpoint và việc gửi SIGTERM xảy ra SONG SONG, không tuần tự. Trong khoảng vài trăm mili giây, load balancer vẫn còn đẩy request tới một tiến trình đã bắt đầu tắt. Cách sửa: khi nhận SIGTERM thì cho readiness probe fail NGAY, ngủ vài giây để mọi thành phần định tuyến cập nhật, sau đó mới ngừng nhận kết nối mới và chờ request đang chạy kết thúc trong một khoảng thời gian có giới hạn.
Giải thích sâu
Khoảng ngủ đó là phần phản trực giác nhất và cũng là phần hay bị bỏ. Nghe như một hack, nhưng nó phản ánh đúng thực tế: việc lan truyền thay đổi endpoint là bất đồng bộ và đi qua nhiều tầng — kube-proxy trên từng node, ingress controller, có khi cả load balancer của nhà cung cấp đám mây. Không có cách nào biết chắc tất cả đã cập nhật, nên bạn chờ đủ lâu để xác suất còn lại đủ nhỏ. preStop hook với sleep 5 là cách phổ biến nhất.
Phần thứ hai là thời hạn. Orchestrator cho bạn terminationGracePeriodSeconds (mặc định 30) rồi SIGKILL. Nên tổng của khoảng ngủ cộng thời gian drain phải NHỎ HƠN con số đó, nếu không bạn bị giết giữa lúc đang phục vụ — đúng thứ bạn đang cố tránh. Hệ thống có request dài (upload, export) thì phải nâng grace period lên tương ứng, và đó là quyết định phải nói ra chứ không để mặc định.
Còn một nguồn 502 nữa hay bị nhầm với cái trên: keep-alive. Nếu tiến trình đóng một kết nối keep-alive đúng lúc load balancer vừa gửi request lên đó, request đó chết dù cả hai bên đều hành xử đúng. Cách giảm là đặt idle timeout phía server LỚN HƠN phía load balancer, để bên đóng kết nối luôn là load balancer. Chi tiết này gây ra những lỗi 502 lác đác ngay cả khi không deploy, và rất khó tìm nếu không biết trước.
Thứ tự tắt, viết ra thành cấu hình
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 5"] # để endpoint lan truyền xong
terminationGracePeriodSeconds: 45 # > sleep + thời gian drain
readinessProbe:
httpGet: { path: /readyz, port: 8080 }
periodSeconds: 2 # phát hiện nhanh khi /readyz đỏ
livenessProbe:
httpGet: { path: /healthz, port: 8080 } # KHÔNG kiểm tra dependency ở đâyCâu hỏi tiếp theo họ sẽ hỏi
?Readiness và liveness khác nhau chỗ nào?
Readiness quyết định có nhận traffic hay không; liveness quyết định có khởi động lại container hay không. Bẫy kinh điển là kiểm tra database trong liveness: database chậm một nhịp thì toàn bộ pod bị restart hàng loạt, biến một sự cố nhỏ thành sự cố toàn hệ thống. Dependency thuộc về readiness.
?Còn worker xử lý hàng đợi thì sao?
Cùng nguyên tắc, khác cơ chế: nhận SIGTERM thì ngừng nhận job mới, hoàn thành job đang chạy, rồi thoát. Job đang chạy dở mà bị giết thì phải quay lại hàng đợi — nên broker cần ack sau khi xử lý xong, không phải khi nhận.
Trả lời thế này là mất điểm
- Chỉ bắt SIGTERM rồi
process.exit()ngay. Bạn vừa cắt đúng những request đang chạy. - Không biết endpoint update và SIGTERM là song song. Đây là gốc rễ của cả vấn đề.