最稳的Kubernetes健康检查是独立监听/healthz端点:用net/http原生启动,绕开所有中间件和业务逻辑,仅检查进程存活(如atomic变量、goroutine数、监听器状态),响应严格≤1秒,禁用任何I/O操作。

直接暴露 /healthz 端点,用 net/http 原生启动一个独立监听器,不走业务框架中间件、不查数据库、响应严格控制在 1 秒内——这是最轻量、最稳、Kubernetes 最认的方式。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
为什么不能复用主 HTTP 服务的路由和中间件
业务服务常带 JWT 鉴权、日志、CORS、panic recover 等中间件,而 /healthz 被每秒探测一次,中间件会带来三重风险:
- 鉴权中间件可能因缺失 token 或远程校验失败返回 401,Kubernetes 直接判为不健康
- 日志中间件高频写入撑爆 /var/log 卷或 stdout 流控超限
- recover 中间件捕获 panic 是好意,但健康端点本不该 panic;一旦它自己 panic,http.Serve 可能静默挂掉整个 mux
正确做法是绕开所有业务逻辑链:
- 用 http.NewServeMux() 创建独立实例,不碰 http.DefaultServeMux
- http.HandleFunc("/healthz", ...) 直接注册裸函数,不 import 任何业务包
- 启动时另起 goroutine 跑健康监听,端口可与主服务分离(如 :8081),避免主服务卡死时健康端点也失联
/healthz 里到底该检查什么
只回答一个问题:“这个进程自己还活着吗?” 不涉及任何外部依赖。常见有效检查项:
- atomic.LoadInt32(&isHealthy):由主 goroutine 在 panic 或关键错误时置 0
- runtime.NumGoroutine() 是否突增(如 > 1000),暗示 goroutine 泄漏或死锁
- http.Server 的 Listener.Addr() 是否非 nil,确认监听器未被关闭
- 主事件循环是否仍在运行(用带超时的 select 检测 channel 是否阻塞)
绝对禁止:
- db.PingContext、redis.Ping、http.Get
- os.ReadFile、time.Sleep、sync.Mutex.Lock(可能死锁)
- 任何带网络、磁盘、锁、GC 敏感的操作
Kubernetes 探针配置必须匹配代码行为
探针不是摆设,参数错配会导致误重启或流量误入:
- livenessProbe.httpGet.path 必须是 /healthz,不能写成 /health 或 /status
- timeoutSeconds 设为 1,periodSeconds 至少为 5,否则高频超时会触发 CrashLoopBackOff
- initialDelaySeconds 给足启动时间(Go 服务加载配置/初始化 gRPC client 常需 10–30 秒)
- failureThreshold 设为 3,容忍短时 GC STW 或调度抖动
响应体也要守规矩:
- 必须写非空 body:w.Write([]byte("ok")),某些旧版 kubelet 把空 body 当失败
- 显式设 Content-Type:w.Header().Set("Content-Type", "text/plain; charset=utf-8"),防 Ingress 截断
- 不要打 info 级日志,error 级日志只在真出问题时写 stderr
怎么安全加 /readyz 而不拖垮数据库
/readyz 才负责检查 DB、Redis、下游 gRPC 等依赖,但它绝不能每次请求都实时连——连接池会被打爆,DB 也会被轮询压垮。
正确姿势是“缓存 + 快照”:
- 启动后另起 goroutine,每 5 秒执行一次 db.PingContext(ctx, 500*time.Millisecond),结果存进 sync.RWMutex 保护的结构体
- /readyz handler 只读快照,不做任何 I/O
- 所有依赖检查必须并行:ctx, cancel := context.WithTimeout(r.Context(), 800*time.Millisecond),然后 db.PingContext、redis.Ping、grpcClient.Check 全部 go routine 并发跑,任一失败就 http.Error(..., 503)
注意:
- /readyz 和 /healthz 必须用不同路径,且在 Kubernetes 配置中分别指向 readinessProbe 和 livenessProbe
- 不要在 /readyz 里做耗时操作(如全量配置 reload),超时设为 ≤1 秒,否则 K8s 直接标记 Unhealthy
真正容易被忽略的是:健康检查不是“加个接口就完事”,而是要把状态判断从主业务流里解耦出来——用原子变量、独立监听、后台轮询快照,才能让 /healthz 像心跳一样稳定,也让 /readyz 在依赖抖动时不把整个服务拖进重启漩涡。

















