通八洲科技

如何使用Golang实现容器健康检查_Golang Docker与Kubernetes健康管理方法

日期:2026-01-02 00:00 / 作者:P粉602998670
Go服务应暴露/healthz端点返回200 OK,避免副作用;/readyz分离依赖检查,用带超时的PingContext;K8s探针与Docker HEALTHCHECK无关,应统一配置于K8s资源中。

Go 服务如何暴露标准健康检查端点

Kubernetes 的 livenessProbereadinessProbe 默认依赖 HTTP 状态码,Go 服务只需提供一个稳定、低开销的 HTTP handler 即可。关键不是“实现多复杂”,而是“不引入副作用”——比如避免在健康检查中触发数据库连接、缓存刷新或日志刷盘。

推荐用 net/http 直接注册 /healthz(K8s 社区约定路径),返回 200 OK 即可:

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("ok"))
})

如果需要轻量级状态反馈(如是否加载了配置),可返回 JSON,但务必控制字段数量和序列化开销;避免调用 time.Now()runtime.NumGoroutine() 这类看似无害实则可能波动的指标。

何时需要自定义 TCP 或 exec 探针而非 HTTP

HTTP 探针失效的典型场景:服务监听 Unix socket、仅支持 gRPC、或明确禁止外部 HTTP 访问(如某些内部管理服务)。这时需退回到更底层的探测方式。

Go 中实现 readiness 检查的常见陷阱

readiness 不等于 liveness:前者决定“能否接收流量”,后者决定“是否该重启”。很多团队误把数据库连通性检查塞进 /healthz,导致 DB 临时抖动时整个服务被 K8s 从 Service Endpoints 中摘除,反而放大故障。

更合理的做法是分离端点:

在 Go 中,/readyz 应使用带超时的非阻塞检查。例如检查 PostgreSQL 连接:

db, _ := sql.Open("pgx", dsn)
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
err := db.PingContext(ctx)
if err != nil {
    http.Error(w, "db unreachable", http.StatusServiceUnavailable)
    return
}
w.WriteHeader(http.StatusOK)

注意:不要复用全局 *sql.DBPing(),它不带上下文,超时不可控;也不要在这里执行 SELECT 1 —— PingContext 已足够。

Docker HEALTHCHECK 指令与 K8s 探针的关系

Docker 自身的 HEALTHCHECK(如 HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/healthz || exit 1)和 K8s 的探针是两套独立机制。K8s 完全忽略 镜像里定义的 HEALTHCHECK,它只认 Pod spec 中的 livenessProbe/readinessProbe

这意味着:

建议:在 K8s 环境下,彻底移除 Dockerfile 中的 HEALTHCHECK,所有健康策略统一收口到 Helm chart 或 K8s YAML 的 probe 字段中。