Go 服务优雅停机:K8s 发 SIGTERM 后不是立刻消失

发布时间:2026/7/3 20:39:54
Go 服务优雅停机:K8s 发 SIGTERM 后不是立刻消失 Go 服务优雅停机K8s 发 SIGTERM 后不是立刻消失Go 服务部署在 K8s 后优雅停机经常被忽略。Pod 被滚动更新时会收到 SIGTERM如果服务没有停止接收新请求、等待进行中请求结束、关闭连接池就可能产生一批莫名其妙的 502 或处理中断。优雅停机不是形式主义它决定发布时用户是否被无辜打断。一、停机链路要清楚flowchart TD A[K8s SIGTERM] -- B[Stop Accepting] B -- C[Drain Requests] C -- D[Close Resources] D -- E[Exit]K8s 不会读心。应用收到信号后需要自己处理生命周期。二、Go HTTP Server 支持 Shutdownsrv : http.Server{Addr: :8080, Handler: router} go func() { if err : srv.ListenAndServe(); err ! http.ErrServerClosed { log.Fatal(err) } }() ctx, cancel : context.WithTimeout(context.Background(), 20*time.Second) defer cancel() _ srv.Shutdown(ctx)Shutdown会停止接收新连接并等待已有请求完成直到 context 超时。三、K8s 配置要配合terminationGracePeriodSeconds: 30 lifecycle: preStop: exec: command: [sh, -c, sleep 5]preStop 可以给负载均衡摘流量一点时间。具体配置要结合网关和服务发现机制测试。四、长请求要有上限如果请求可能跑几分钟停机就会拖很久。服务应该设置请求超时并对长任务异步化。shutdown_policy: max_request_time: 15s async_jobs: resumable force_exit_after: 30s优雅停机不是无限等待。超过窗口仍要退出否则发布会卡住。还要处理 readiness。收到 SIGTERM 后可以先让 readiness 失败让流量入口停止转发再等待已有请求完成。var shuttingDown atomic.Bool func readiness(w http.ResponseWriter, r *http.Request) { if shuttingDown.Load() { w.WriteHeader(http.StatusServiceUnavailable) return } w.WriteHeader(http.StatusOK) }这样服务不是一边准备退出一边继续接新请求。生命周期信号要和探针配合。五、总结Go 服务在 K8s 中要处理 SIGTERM、停止接收新请求、等待已有请求、关闭资源并与 terminationGracePeriod 和 preStop 配合。Pod 不是立刻消失但也不会永远等你。生命周期写清楚滚动发布才真的平滑。上线前可以做一次演练压测中滚动发布观察 5xx、请求耗时和连接关闭情况。优雅停机只有被验证过才不是纸面配置。