preStop 中仅 sleep 无效,必须主动使 readiness probe 失败(如 /readyz 返回 503)以触发 Endpoint 移除;同时需提前 deregister 注册中心实例,并手动管理长连接生命周期,三者顺序缺一不可。

preStop 里只 sleep 不够,必须先触发 readiness 探针失败
单纯在 preStop 里写 sleep 10,Kubernetes 不会主动把 Pod 从 Service 的 Endpoints 或 Ingress 后端列表中移除。新请求仍会被转发进来,造成“一边关、一边收”的有损切流。
真正起作用的是 readiness probe 状态变化:只有当 probe 返回非 2xx(比如 /readyz 返回 503),Endpoint Controller 才会在下一次同步周期(默认 1–3 秒)中剔除该 Pod。
-
readinessProbe必须指向一个可被主动控制的端点,例如/healthz/ready - 收到
SIGTERM后,Go 应用需立即将该端点返回 503,而不是等preStop执行完才改状态 -
failureThreshold: 1是关键配置,避免 probe 多次失败才摘流 - ALB/NLB 的调谐延迟通常为 5–10 秒,
periodSeconds: 5能确保 5 秒内完成摘流
HTTP Server.Shutdown 不能替代连接排空逻辑
http.Server.Shutdown(ctx) 只会关闭 listener、拒绝新连接,并等待活跃连接自然关闭。但它对 WebSocket、SSE、HTTP/2 流式响应完全无感知——底层 net.Conn 会被强制关闭,客户端立刻收到 RST 或 GOAWAY,导致流中断或重连失败。
- gRPC 服务必须用
server.GracefulStop(),它内部发送 GOAWAY + 等待所有 stream 完成 - HTTP 长连接网关需手动管理每个
net.Conn生命周期,例如记录活跃连接、超时后主动conn.Close() - WebSocket 场景下,要监听
CloseNotify()并在Shutdown前广播下线通知,再等客户端确认断开 - 别设过短的
ctx超时(如 5s),否则未完成的流会被强制终止
HPA 缩容时连接数指标上报必须带延迟
长连接场景下,HPA 如果直接采集 netstat -an | grep ESTABLISHED | wc -l 这类瞬时连接数,会导致缩容过激:Pod 刚开始排空,连接数还没降下来,HPA 就误判“负载低”,继续缩容,加剧流量打到正在关闭的实例上。
- 上报连接数指标时,应使用滑动窗口平均值(如过去 60 秒均值),而非实时值
- 结合 Prometheus 的
rate()或avg_over_time()函数做平滑处理 - KEDA 触发缩容前,建议加
stabilizationWindowSeconds: 120,防止抖动 - 若用自定义指标,确保指标采集器本身不因 Pod 终止而中断上报(例如部署为 DaemonSet)
注册中心 deregister 动作必须早于 Shutdown
如果你的服务注册在 Consul/Nacos/Eureka,仅靠 Kubernetes 的 Endpoint 更新是不够的。上游网关(如 Spring Cloud Gateway)可能直连注册中心拉取实例列表,绕过 K8s Service —— 此时即使 Endpoint 已清空,注册中心里实例还“活着”,新请求照样打过来。
-
preStop中必须先发起curl -X DELETE http://localhost:8500/v1/agent/service/deregister/xxx(以 Consul 为例) - deregister 接口通常是异步的,需加
sleep等待其生效(Consul 默认 deregister 延迟约 1–2 秒) - 再执行
kill -TERM 1或启动 Go 内部 shutdown 流程 - 不要依赖注册中心的 TTL 自动下线,它无法与 K8s 终止流程对齐,容易残留


















