直接回答:Pod 删除时,K8s 先把它标记为 Terminating 并从 Service 的 Endpoints 摘除,同时向容器发送 SIGTERM,等待 terminationGracePeriodSeconds(默认 30 秒)后发 SIGKILL 强杀。优雅停机的本质是:在收到 SIGTERM 后停止接新请求、处理完存量请求再退出。
丢请求的两个经典原因:一是摘 Endpoints 和发 SIGTERM 是并发的——kube-proxy 更新 iptables/IPVS 规则有传播延迟,期间仍有新流量被转发到正在退出的 Pod;二是应用不处理 SIGTERM,直接被 30 秒后的 SIGKILL 打断,在途请求全部失败。
标准解法:preStop 钩子先睡几秒等 Endpoints 摘除传播完成,再让应用收到退出信号:
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 5"]
terminationGracePeriodSeconds: 60
应用侧要捕获 SIGTERM:HTTP 服务调用 graceful shutdown 停止 accept、排空存量连接;长连接(gRPC、WebSocket)要主动发 GOAWAY 让客户端重连;消费队列的要先停止拉取、处理完手头消息再退出。注意 preStop 的 sleep 也计入 grace period,要留足余量,长请求业务应把 30 秒默认值调大。另外 readiness 探针失败不等于会摘流量——Terminating 状态的 Pod 无论探针结果如何都会被摘除,所以别指望用探针在停机前"先下线"。客户端侧也要配合:连接池要能感知后端变更、失败请求做有限重试,否则上游再优雅也救不了僵死的旧连接。
追问方向:SIGTERM 给的是容器内 1 号进程,shell 启动的应用为什么收不到?node 下线(drain)和滚动更新的停机流程有何差异?
(约 490 字)