健康检查接口必须使用符合func(http.ResponseWriter, *http.Request)签名的http.HandlerFunc实现,不可用普通函数;Kubernetes探针仅依据HTTP状态码判断,需对/healthz返回200(不查依赖),对/readyz同步检查DB等依赖并超时控制,失败返回503。

健康检查接口必须用 http.HandlerFunc 实现,别写成普通函数
Go 的 HTTP 服务要求健康检查端点是符合 http.HandlerFunc 签名的函数(func(http.ResponseWriter, *http.Request)),不是返回 bool 或 error 的任意函数。Kubernetes 的 livenessProbe 和 readinessProbe 只认 HTTP 状态码,不解析响应体内容。
常见错误是写成这样:
func checkDB() error { ... }
然后试图直接注册到路由里——这会编译失败或 panic。必须包装成 handler:
- 用
http.HandleFunc("/healthz", healthzHandler)注册 -
healthzHandler内部做实际检查(如 DB 连通性、依赖服务 ping) - 成功时写
w.WriteHeader(http.StatusOK),失败时写http.StatusServiceUnavailable(503)或其他非2xx码 - 避免在 handler 里调用
log.Fatal或os.Exit,否则整个进程退出,K8s 会反复重启 Pod
Probe 超时和重试参数必须匹配 Go HTTP server 的 ReadTimeout 和 WriteTimeout
Kubernetes 的 timeoutSeconds(默认 1 秒)必须 ≤ Go http.Server 的 ReadTimeout 和 WriteTimeout,否则 probe 请求可能被 Go server 主动断开,导致误判为失败。
例如,在 Pod spec 中设了:
livenessProbe:
httpGet:
path: /healthz
port: 8080
timeoutSeconds: 3
但 Go server 没配超时:
srv := &http.Server{Addr: ":8080", Handler: mux}
此时默认 ReadTimeout 是 0(无限),但底层 TCP 层或 kubelet 的连接管理可能触发意外中断。更稳妥的做法是显式对齐:
- Go server 设置
ReadTimeout: 5 * time.Second、WriteTimeout: 5 * time.Second - K8s probe 的
timeoutSeconds设为 ≤ 4,留出缓冲 - 同时注意
initialDelaySeconds要大于应用冷启动耗时,比如加载配置、建 DB 连接池等
区分 /healthz 和 /readyz:前者只看进程存活,后者必须检查所有依赖
livenessProbe 对应 /healthz,只要进程能响应即可(甚至可只 return 200);readinessProbe 对应 /readyz,必须真实验证下游依赖是否就绪,否则流量进来会失败。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
典型反例是两个 endpoint 都只返回 200,结果 DB 挂了但 readiness 仍为 true,Service 流量持续打进来,请求全 500。正确做法:
-
/healthz:不做任何外部调用,仅w.WriteHeader(200) -
/readyz:同步检查 DBPingContext、RedisInfo、关键 gRPC 服务Connect等,任一失败就返回503 - 检查逻辑要加 context timeout,比如
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second),防止某个依赖卡住拖垮整个 probe
不要在 handler 里用 time.Sleep 模拟耗时检查
有人为测试 probe 行为,在 health handler 里加 time.Sleep(10 * time.Second),这会导致 kubelet 等待超时后判定失败,进而反复重启 Pod。更严重的是,如果多个 probe 并发进来(K8s 默认每 10 秒发一次,但可能因队列积压并发),sleep 会阻塞 goroutine,快速耗尽 server 的 MaxConns 或导致 goroutine 泄漏。
真正需要模拟延迟或限流的场景,应该:
- 用
context.WithTimeout控制单次检查上限 - 对慢依赖(如第三方 API)做熔断或降级(例如缓存上次成功结果 + TTL)
- 把耗时检查移到后台 goroutine 定期运行,handler 只读共享状态变量(注意加
sync.RWMutex)
probe 接口本身必须轻量、确定、可重复,这是它被 kubelet 高频调用的前提。任何不确定性(随机 sleep、无 timeout 的网络调用、未加锁的全局状态读写)都会让 Kubernetes 的健康决策失真。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










