必须用独立HTTP server暴露健康端点,因共用业务路由易受中间件阻塞影响探针响应;livenessProbe与readinessProbe需分路径、分超时、分语义配置;代码中须用原子变量动态控制/readyz状态并返回具体错误信息。

Go服务在Kubernetes里跑不稳,八成不是代码问题,而是健康检查环境没搭对——/healthz端点暴露了,但没配独立端口;livenessProbe和readinessProbe指向同一个 handler;或者探针超时比依赖检查还短。这些配置层面的错位,会让K8s反复重启Pod或持续转发流量到已卡死的服务。
为什么必须用独立 HTTP server 暴露健康端点
把健康检查塞进主业务路由(比如 Gin 的 router.GET("/healthz", ...))看似省事,实则埋雷:中间件 panic、日志钩子阻塞、甚至某个慢中间件的 context timeout 都可能让探针请求卡住。K8s 看不到响应,直接杀 Pod。
- 用
http.NewServeMux()新建一个专用 mux,只注册/healthz和/livez等探针路径 - 启动独立
http.Server,监听如:8081这类非业务端口(避免与主服务争抢) - 在 Deployment YAML 中显式声明该端口:
ports: - name: probe containerPort: 8081 - 主服务的
http.Server和 probe server 完全解耦,哪怕业务路由崩了,健康端点仍可响应
Deployment 中 livenessProbe 和 readinessProbe 怎么配才不打架
两个探针语义不同,但共用一套配置参数就容易互相干扰。常见错误是都设 timeoutSeconds: 1,结果 DB 一次网络抖动就触发 livenessProbe 失败,容器被重启,而 readinessProbe 还没来得及把实例从 Endpoint 摘掉。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
livenessProbe用/healthz(只查进程存活),initialDelaySeconds: 30(给 Go 初始化留足时间),timeoutSeconds: 1,failureThreshold: 3 -
readinessProbe用/readyz(同步检查 DB/Redis/gRPC),initialDelaySeconds: 15(等连接池 warmup),timeoutSeconds: 3(必须 ≥ 依赖检查总耗时) - 务必设置
periodSeconds: 5,避免高频探测压垮服务;failureThreshold: 3可容忍短时抖动 - 绝对不要让两个 probe 指向同一 handler 或同一端口路径
Go 代码里怎么控制 readiness 状态的实时性
K8s 不会“感知”你的服务是否真就绪,它只信探针返回的状态码。所以你得在代码里主动管理就绪信号,而不是等所有初始化完成才启动 probe server。
- 用
atomic.Int32声明全局变量:var isReady atomic.Int32,初始值为0 - 在依赖初始化成功后调用
isReady.Store(1);收到SIGTERM时立即isReady.Store(0) -
/readyzhandler 中先if isReady.Load() == 0 { http.Error(w, "not ready", http.StatusServiceUnavailable); return } - 别在 handler 里做任何新连接操作(如 new
redis.Client),复用服务启动时已初始化的 client 实例
最常被忽略的一点:健康端点的响应体里带具体失败项(比如 {"redis": "i/o timeout"}),不是为了好看,而是让运维能快速区分是 Redis 断连还是 goroutine 泄漏——前者要查网络策略,后者得看 pprof。这个细节决定了故障定位是分钟级还是小时级。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










