健康检查端点须用独立http.Server显式监听,避免框架中间件干扰;/health仅检查进程存活,/ready需验证关键依赖;容器内须捕获SIGTERM并优雅关闭健康服务。

健康检查端点必须用 http.Server 显式监听,不能依赖框架默认路由
Go 程序跑在容器里时,Docker 的 HEALTHCHECK 指令(如 curl -f http://localhost:8080/health)会从宿主机网络 namespace 发起请求,它不经过你的 Web 框架中间件、也不走任何代理或重写逻辑。如果你用 gin 或 echo 但没显式启动一个独立的 http.Server,或者把健康检查挂在 /health 却没注册到实际监听的 mux 上,Docker 就会收不到响应,反复标记为 unhealthy。
实操建议:
- 单独起一个轻量
http.Server,绑定到固定地址(如:8081),只处理/health和/ready - 避免复用主服务的
http.ServeMux—— 主服务可能有鉴权、CORS、日志中间件,干扰健康检查的低延迟和确定性 - 不要用
net/http.HandleFunc后直接http.ListenAndServe—— 它会阻塞主线程,且无法优雅关闭 - 示例片段:
healthMux := http.NewServeMux() healthMux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) }) go func() { log.Println("Health server starting on :8081") http.ListenAndServe(":8081", healthMux) }()
livenessProbe 和 readinessProbe 的 Go 实现逻辑必须分离
Docker 的 HEALTHCHECK 只能定义一个命令,但 Kubernetes 中的 livenessProbe 和 readinessProbe 是两个独立配置,对应不同语义:前者决定是否重启容器,后者决定是否接入流量。Go 程序里如果混用同一端点,会导致就绪态异常时被误杀,或存活态卡死却仍接收请求。
实操建议:
-
/health应只检查进程是否存活(如能否响应 HTTP、goroutine 是否 panic)、不依赖外部依赖 -
/ready必须检查关键依赖:数据库连接池是否可用、Redis 是否可 ping、下游 gRPC 服务是否响应 —— 任一失败就返回 503 - 避免在
/ready中做耗时操作(如查全表、生成报告),超时设为timeoutSeconds: 2,Docker/K8s 默认 30 秒才判定失败,太长会拖慢滚动更新 - 别用
database/sql的db.Ping()就完事 —— 它只测连接池是否空闲,不保证能执行真实查询;应执行SELECT 1并设置上下文超时
容器内 Go 程序必须捕获 SIGTERM 并等待健康检查服务器关闭
Docker stop 或 K8s 驱逐 Pod 时,会先发 SIGTERM,等 grace period(默认 30 秒)后才发 SIGKILL。如果你的健康检查服务是 go func(){...}() 启动的,主 goroutine 退出时它还在运行,Docker 可能误判为“进程已死但端口仍占用”,下一次健康检查失败。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
实操建议:
- 用
sync.WaitGroup或errgroup.Group管理多个服务 goroutine 的生命周期 - 监听
SIGTERM后,调用healthServer.Shutdown(context.WithTimeout(...)),而非直接os.Exit(0) - 健康检查服务器的
Shutdown调用必须在主服务Shutdown之后 —— 否则 K8s 可能在你还没响应 readiness probe 时就把流量切走了 - 示例关键行:
sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT)
Dockerfile 中 HEALTHCHECK 的 CMD 必须用绝对路径且不含 shell 特性
很多人写 HEALTHCHECK --interval=30s CMD curl -f http://localhost:8081/health || exit 1,但 Docker 默认用 sh -c 执行,而 Alpine 镜像里常没 curl,或 /bin/sh 不支持 || 语法,导致健康检查直接报 command not found 或静默失败。
实操建议:
- 优先用
cmd形式(exec form):HEALTHCHECK --interval=30s CMD ["http", "GET", "http://localhost:8081/health"]—— 前提是你在镜像里装了http工具(如apk add --no-cache httpie) - 更稳妥的是用 Go 自带的
net/http写个极简二进制(healthcheck),编译进镜像,然后CMD ["./healthcheck", "-url=http://localhost:8081/health"] - 绝对不要依赖
$PATH或当前工作目录 ——HEALTHCHECK在容器 rootfs 中执行,环境变量极少 - 测试方法:运行容器后
docker exec -it,手动运行 HEALTHCHECK 中的 CMD,看是否真能返回 0/bin/sh
健康检查不是加个接口就行,关键在信号传递链路是否完整:从 Docker daemon 发起探测 → 容器网络栈接收 → Go 的 http.Server 响应 → 主进程收到 SIGTERM 后按顺序关闭各服务 → 关闭前确保最后一次 probe 能成功返回。漏掉任意一环,都可能让容器在 K8s 里反复重启却查不出原因。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










