http健康检查端点须返回200且响应体为空或仅"ok";/healthz做轻量存活检查,/readyz做依赖就绪检查;需用server.shutdown配合readiness下线,并对齐probe超时与http server超时设置。

HTTP 健康检查端点必须返回 200 且响应体为空或轻量
Go 标准库 http.ServeMux 或 http.HandleFunc 可快速暴露 /healthz,但注意:Kubernetes、Docker 等只校验 HTTP 状态码,不解析 JSON;响应体过大(如返回完整配置)会拖慢探针频率,甚至触发超时。
推荐写法是直接 w.WriteHeader(http.StatusOK) 后立即返回,不调用 w.Write 或仅写入 "ok"。
常见错误:
• 返回 200 OK 但 body 是 HTML 或含 stack trace 的 error 页面
• 在 handler 中执行耗时 DB 连接检查,导致 liveness 探针失败重启容器
• 忘记设置 http.TimeoutHandler,使单次健康检查阻塞整个 HTTP server
liveness 与 readiness 要拆开实现,不能共用一个 handler
二者语义不同:liveness 判断进程是否“还活着”(比如 goroutine 是否卡死),readiness 判断是否“能收请求”(比如依赖的 Redis 是否连通)。混用会导致误杀——例如 Redis 临时抖动,readiness 应返回 503,但 liveness 仍应是 200。
实操建议:
• /healthz 仅做最轻量检查(如 atomic.LoadInt64(&upTime))
• /readyz 可包含 DB ping、Redis ping、gRPC health check 等依赖探测
• 用 sync.Once 缓存依赖连接状态,避免每次探针都新建连接
立即学习“go语言免费学习笔记(深入)”;
使用 http.Server 的 Shutdown 配合 readiness 下线
滚动更新时,Kubernetes 会在发 SIGTERM 前先将 Pod 从 Service Endpoint 中移除,前提是 readiness probe 失败。但 Go 程序若没主动关闭 listener,旧连接可能持续处理请求,造成 5xx。
关键步骤:
• 启动时用 http.Server{Addr: ":8080", Handler: mux} 显式声明 server 实例
• 收到 os.Interrupt 或 syscall.SIGTERM 后,先调用 server.Shutdown()
• 在 /readyz handler 中加判断:if shuttingDown { w.WriteHeader(http.StatusServiceUnavailable) }
• 不要依赖 defer server.Close() —— 它不等待活跃连接结束
Probe 超时和重试参数必须匹配 Go HTTP server 的实际响应能力
Docker 的 HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 是常见配置,但若你的 /readyz 平均耗时 2.8s(比如查三次下游),就极易被判定为失败。Go 侧需对齐:
• http.Server.ReadTimeout 和 WriteTimeout 应设为略小于 probe timeout(如 2.5s)
• 使用 context.WithTimeout 包裹所有依赖调用,防止单个慢依赖拖垮整个探针
• 在 init() 或 main 开头预热依赖连接(如 redis.Ping()),避免 start-period 内首次探针因连接建立失败
容易忽略的一点:Go 1.19+ 默认启用 http.Transport 的 idle connection 复用,但容器内 DNS 变更后,长连接可能指向已下线的实例——健康检查里若复用旧连接,会掩盖真实就绪状态。










