应使用显式构造的 http.ServeMux,避免 http.HandleFunc 零散注册导致冲突;/healthz handler 须轻量、无外部依赖、并发安全,仅返回 200 和 {"status":"ok"}。

健康检查端点该用哪个 HTTP handler?
直接复用 http.DefaultServeMux 或自定义 http.ServeMux 都可以,但别用 http.HandleFunc 在 main 包里零散注册——它底层还是往 DefaultServeMux 上挂,容易和其它库(比如 prometheus/client_golang 的 /metrics)冲突或被覆盖。推荐显式构造 mux:
mux := http.NewServeMux()
mux.HandleFunc("/healthz", healthzHandler)
mux.HandleFunc("/readyz", readyzHandler)
// 后续再挂 /metrics、/debug 等
这样控制权清晰,也方便单元测试时传入 mock mux。
怎么写一个靠谱的 /healthz handler?
/healthz 应只检查当前进程是否存活、监听端口是否就绪、基础依赖(如本地配置加载)是否可用,**不查外部服务**。常见错误是把数据库连通性塞进 /healthz,结果 DB 暂时抖动导致整个 Pod 被 kubelet 杀掉。
实操建议:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 返回 200 即可,body 可空或只含
{"status":"ok"};别加时间戳、版本号等非必要字段 - 避免耗时操作:不要做文件读写、goroutine sleep、HTTP 外调
- 如果用了结构体字段存状态(比如
isInitialized bool),确保并发安全——用sync/atomic或sync.RWMutex保护 - 示例精简版:
func healthzHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
}
LivenessProbe 和 ReadinessProbe 的路径与参数怎么配?
Kubernetes 不关心你 handler 里具体逻辑,只认返回码。但 probe 配置稍有不慎就会让 Pod 反复重启或流量打进来却无法处理:
-
livenessProbe.httpGet.path建议固定为/healthz,超时设短(initialDelaySeconds: 10,timeoutSeconds: 2),失败阈值别太低(failureThreshold: 3) -
readinessProbe.httpGet.path推荐单独用/readyz,用于检查依赖就绪(如 DB 连接池已 warmup、gRPC server 已 start、configmap 已 reload 完毕) - 千万别给 readiness 设置
initialDelaySeconds: 0—— 程序还没 bind port 就开始探活,必然 503,导致 Service endpoint 一直为空 - 若用
exec探针替代 HTTP,注意容器内必须有curl或netstat,Alpine 镜像默认没有
为什么 /readyz 返回 500 但 Pod 状态还是 Running?
因为 kubelet 只看 probe 返回码是否在 200–399 范围内。500 属于“失败”,但 readiness probe 失败只会从 Endpoints 对象中摘除该 Pod,**不会杀容器**;而 liveness probe 失败才会触发 restart。所以看到 500 却没重启,说明你配的是 readiness,不是 liveness。
容易忽略的点:
- 两个 probe 的
periodSeconds别设成一样(比如都是 5s),否则可能在极端情况下出现探测请求堆积,加大 CPU 负担 - handler 里 panic 会导致整个 HTTP server crash,必须用
recover()拦住 —— 尤其是解析 query 参数或 JSON body 时 - Go 1.22+ 默认启用
http.Server.ReadTimeout,若 probe 超时设置比它小,会提前断连并返回 408,而不是你 handler 控制的 5xx

















