livenessProbe 应只检查进程自身存活状态,如原子变量、服务器地址、goroutine 数量等轻量指标,严禁 I/O 操作;/livez handler 需绕过中间件、无副作用、响应 ≤800ms;K8s 配置须设 initialDelaySeconds、timeoutSeconds 等关键参数,并与 readinessProbe 严格分离。

Go 服务在 Kubernetes 中的存活探针(livenessProbe)状态不是由 Go 程序“检测”的,而是由 kubelet 主动发起探测、根据你的 HTTP handler 响应来判定的。你真正要做的,是让 /livez(或 /healthz)端点返回有意义的 200 或非 200,并确保它不被业务逻辑拖垮。
为什么不能在 livenessProbe 里查数据库
查 DB = 发起网络请求 + 等待连接池 + 可能阻塞 goroutine。一旦 DB 暂时抖动,livenessProbe 就失败,kubelet 杀掉容器 → 新 Pod 启动 → 再次连 DB 失败 → 无限重启(CrashLoopBackOff)。这不是健康检查,是自爆循环。
-
livenessProbe只应回答:“这个进程自己还活着吗?” - 典型检查项:
atomic.LoadInt64(&healthy)、http.Server.Addr是否非空、runtime.NumGoroutine()是否未突增到危险值(比如 >5000)、关键信号 channel 是否未阻塞 - 绝对不要触发任何 I/O:不读文件、不 dial 网络、不调用
db.Ping或redis.Ping
Go 怎么写一个靠谱的 /livez handler
核心是快、轻、绕过中间件、无副作用。如果你用的是 net/http,直接注册;如果用 Gin/Echo,必须用原生路由或显式跳过中间件。
- 响应必须 ≤ 800ms(K8s 默认 timeout 是 1s,留缓冲)
- 别打日志(高频调用会撑爆 stderr)
- 避免 panic:handler 内部不 recover,靠
defer func(){if r := recover(); r != nil { ... }}()可能掩盖问题,不如让它 panic 让 kubelet 重启 - 示例(
net/http):http.HandleFunc("/livez", func(w http.ResponseWriter, r *http.Request) { // 快速快照,不等、不锁、不 I/O if atomic.LoadInt64(&appHealthy) == 1 { w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) } else { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("unhealthy")) } })
如何让 livenessProbe 不被框架中间件干扰
Gin 的 logger、recovery、JWT 验证中间件,在每秒一次的探针请求下全是噪音和风险。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 不要把
/livez注册进带中间件的 router group;用独立的http.ServeMux或 Gin 的gin.New()实例单独挂载 - 在 Gin 中,正确写法是:
router := gin.New() // 不加任何中间件 router.GET("/livez", func(c *gin.Context) { c.Status(http.StatusOK) }) - 绝对不用
c.String(200, "ok")—— 它会走 render 流程;用c.Status(200)或c.AbortWithStatus(200) - 确认你的 Kubernetes YAML 中
livenessProbe.httpGet.path确实指向/livez,不是/health或拼错成/live
livenessProbe 配置里最容易漏的关键参数
K8s 默认行为对 Go 服务很不友好:没设 initialDelaySeconds,Pod 启动瞬间就开探,而 Go 的 http.Server 还没真正 ListenAndServe 完,必然失败重启。
-
initialDelaySeconds: 10—— 给 Go 主 goroutine 足够时间完成初始化(DB 连接、gRPC dial、监听启动) -
timeoutSeconds: 1—— 必须设,否则默认是 1s,但 Go handler 若卡住,kubelet 等满 1s 才杀,体验差 -
periodSeconds: 10—— 太高频(如 2s)会放大 handler bug 影响;太低频(如 60s)故障发现慢 -
failureThreshold: 3—— 连续失败 3 次才重启,防偶发抖动误判
最常被忽略的一点:livenessProbe 和 readinessProbe 必须分离,且语义不能混。/livez 返回 200 ≠ 服务可收流量,那只是“进程没卡死”;真要判断能不能接请求,得看 /readyz 里的 DB/Redis/gRPC 状态缓存快照 —— 而那个快照,必须由后台 goroutine 定期刷新,不能每次探针都 real ping。

















