Go滚动更新依赖K8s调度器拉起新进程+程序优雅退出:需监听SIGTERM调用server.Shutdown(),配context超时(小于terminationGracePeriodSeconds),关闭DB连接池等后台资源,并合理配置Deployment的maxSurge/maxUnavailable。

Go 本身不支持“热替换二进制”,所谓滚动更新,本质是靠外部调度器(如 Kubernetes)拉起新进程 + Go 进程自己配合优雅退出来实现零中断。关键不在代码里“重启”,而在信号响应、连接 draining 和部署链路协同。
Go 程序必须监听 SIGTERM 并调用 server.Shutdown()
容器环境(Docker/K8s)终止 Pod 时默认发 SIGTERM,不是 SIGKILL。若 Go 不捕获它,进程立即退出,所有活跃 HTTP 连接被硬断开。
-
http.Server.Close()是暴力关闭,会中断所有连接;server.Shutdown()才是标准做法:拒绝新请求、等待已有 handler 返回 - 必须配
context.WithTimeout(),超时时间要小于 K8s 的terminationGracePeriodSeconds(比如设 30s,K8s 配 45s) - 别在 handler 里启动 goroutine 后立刻返回——
Shutdown()不等后台 goroutine,会导致请求实际未完成就退出
Kubernetes Deployment 必须配好 maxSurge 和 maxUnavailable
光 Go 代码优雅关不等于滚动更新成功。K8s 层若配置不当,流量会在新 Pod 尚未就绪时切过去,或旧 Pod 被强杀。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
maxSurge: 1+maxUnavailable: 0更安全:先启一个新 Pod,等它通过readinessProbe后再删旧 Pod -
maxSurge: 0是无效配置,会导致 rollout 卡住——新 Pod 根本起不来 - 低副本数(如 replicas=2)下,
maxSurge: 25%实际为 0,可能触发“先删后启”,造成秒级不可用
不要依赖 Go 自己 fork 新进程传 listener
Linux 下 socket 文件描述符(FD)无法跨 fork 安全继承,尤其在容器中 PID 1 进程常被 shell 包裹,SIGUSR2 信号会被拦截。生产环境应交由编排系统管理生命周期。
立即学习“go语言免费学习笔记(深入)”;
- 想用
SIGUSR2做热升级?得确保进程是 PID 1、没被 sh/bash 包裹、且 FD 传递逻辑完整(listener.(*net.TCPListener).File()→ 子进程net.FileListener()) - K8s 场景下,preStop hook 发
kill -USR2 $PID是可行的,但远不如直接用 Deployment 更新镜像 +terminationGracePeriodSeconds可靠 - 第三方 graceful 库(如
tylerb/graceful)已归档,且易与自定义信号处理冲突,推荐手写Shutdown()+ 信号监听
最容易被忽略的不是 HTTP server,而是数据库连接池、日志文件句柄、长轮询 goroutine —— 它们都要在 Shutdown() 之后、进程退出前显式关闭或等待。否则,看似服务停了,后台资源还在泄漏。

















