Golang中的优雅停机与容器信号处理 Go语言处理SIGTERM实现无损发布

9次阅读

go程序收不到sigterm的根本原因是未运行在pid 1,docker默认shell启动导致信号被拦截;需用exec格式entrypoint、goroutine监听signal channel、shutdown配timeout context、waitgroup等待后台任务、显式关闭第三方资源,并确保k8s terminationgraceperiodseconds足够。

Golang中的优雅停机与容器信号处理 Go语言处理SIGTERM实现无损发布

Go 程序收不到 SIGTERM?检查是否被容器 runtime 拦截了

很多 Go 服务在 Docker/K8s 里无法响应 SIGTERM,根本原因不是代码写错了,而是进程没跑在 PID 1。Docker 默认用 shell 启动(/bin/sh -c "your-command"),此时 SIGTERM 发给 shell,不会透传到你的 Go 进程。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • Dockerfile 中改用 exec 形式启动:ENTRYPOINT ["/app/myserver"],避免 shell wrapper
  • 验证是否在 PID 1:进容器执行 ps -o pid,comm,确认 myserver 的 PID 是 1
  • K8s 中若用了 livenessProbepreStop,注意它们不替代信号处理,只是辅助手段

signal.Notify 没生效?别漏掉 goroutine 和 channel 阻塞

signal.Notify 本身不阻塞,但常见错误是注册完就直接退出 main,或者把 signal channel 放在 select 里却没做非阻塞读取,导致信号“丢了”。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • 必须用 goroutine 启一个监听循环,不能只注册不消费:go func() { for range sigChan { /* handle */ } }()
  • channel 容量至少为 1:sigChan := make(chan os.Signal, 1),否则并发发多个 SIGTERM 可能丢信号
  • 不要在 select 里只等 signal channel 而无 default 或超时,否则 shutdown 逻辑可能卡住

http server Shutdown 超时或 panic?得配对用 context.WithTimeout

http.Server.Shutdown 不会自动设超时,如果请求卡在中间件、DB 查询或外部调用里,它会一直等,最终 hang 住整个停机流程。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • 传入带 timeout 的 context:ctx, cancel := context.WithTimeout(context.background(), 10*time.Second)
  • Shutdown 返回后立刻 cancel(),避免 context 泄漏
  • 注意 Shutdown 本身会关闭 listener,但不会中断正在处理的 request —— 所以务必在 handler 内部也检查 ctx.Done()
  • 若用 net/http.Serve(非 ListenAndServe),需手动 close listener,否则 Shutdown 会报 ErrServerClosed 以外的错误

goroutine 泄漏让停机失败?用 sync.WaitGroup 显式等待关键任务

优雅停机不只是关 HTTP server,还要等后台任务(如消息消费、指标 flush、连接池关闭)完成。靠 sleep 等几秒不可靠,goroutine 可能还在跑。

实操建议:

立即学习go语言免费学习笔记(深入)”;

  • 每个长期运行的 goroutine 启动前 wg.Add(1),退出前 wg.Done()
  • 在 signal 处理函数里调用 wg.Wait(),但要配合 context 控制最大等待时间,避免死等
  • 不要依赖 runtime.NumGoroutine() 判断是否清空 —— 它包含 runtime 自身的 goroutine,数值不稳定
  • 数据库连接池、redis client 等第三方库一般自带 Close() 方法,务必显式调用,且注意它们是否阻塞

最常被忽略的一点:K8s 的 terminationGracePeriodSeconds 必须 ≥ 应用实际 shutdown 耗时,否则 kubelet 会在你还没关完时发 SIGKILL 强杀。这个值不是越长越好,但低于 30 秒对多数服务都偏紧。

text=ZqhQzanResources