Có hai nguyên nhân riêng biệt, thường xảy ra cùng lúc.
1. Đóng quá sớm so với việc cập nhật routing. Khi pod bị xóa, Kubernetes gửi SIGTERM và gỡ pod khỏi Endpoints song song, không tuần tự. Các kube-proxy/ingress cần thời gian để cập nhật. Nếu tiến trình đóng listener ngay, request vẫn được định tuyến tới pod đã ngừng nghe → 502.
Xử lý: khi nhận SIGTERM, cho readiness probe fail trước, đợi vài giây (hoặc dùng preStop hook có sleep) rồi mới server.close().
2. Keep-alive giữ server.close() treo. server.close() chỉ ngừng nhận kết nối mới và chờ mọi kết nối hiện có đóng lại. Kết nối keep-alive đang rảnh vẫn mở, nên callback không bao giờ chạy, tới hạn terminationGracePeriodSeconds thì bị SIGKILL và cắt ngang request đang xử lý.
Xử lý: gọi server.closeIdleConnections() để đóng kết nối rảnh, và server.closeAllConnections() khi hết thời gian ân hạn.
process.on('SIGTERM', async () => {
isShuttingDown = true // readiness probe now returns 503
await sleep(5000) // let endpoints propagate
server.close(() => process.exit(0))
server.closeIdleConnections()
setTimeout(() => {
server.closeAllConnections()
process.exit(1)
}, 15000).unref()
})Sau khi listener đóng, mới đóng DB pool, consumer message queue rồi thoát.
Quy tắc cấu hình: terminationGracePeriodSeconds phải lớn hơn tổng thời gian chờ của ứng dụng.