不能只用单一 /health 接口:Kubernetes 的 livenessProbe 和 readinessProbe 语义不同,需分离为 /health/live(仅进程存活,无依赖)和 /health/ready(含依赖检查且设超时),并严格对齐探针配置参数,避免误杀或流量导入失败。

直接暴露 /health 接口并返回 200 是最轻量的集成方式,但 Kubernetes 的 livenessProbe 和 readinessProbe 对响应内容、超时、状态码有隐性要求——不加区分地复用同一接口,容易导致滚动更新失败或服务被误杀。
为什么不能只用 gin.HandlerFunc 写一个简单 /health 路由
看似可行,但实际部署到 Kubernetes 后常出现 CrashLoopBackOff 或流量被过早导入。根本原因在于:Kubernetes 探针默认只看 HTTP 状态码,不解析响应体;而多数手写健康接口未做超时控制、未隔离依赖、未适配探针语义。
-
livenessProbe触发重启前,会连续失败多次(由failureThreshold控制),若健康检查本身耗时超过timeoutSeconds(默认1秒),探针直接判定失败 - 手写 handler 若调用了数据库
ping或 RedisDo,一次网络抖动就足以让整个 Pod 被反复重启 - 所有探针共用同一个
/health路径时,readinessProbe无法表达“依赖未就绪但进程正常”的中间态
liveness 和 readiness 必须分离路由
这是最容易被跳过的一步。Kubernetes 官方明确建议:liveness 检查应仅确认进程存活,readiness 才检查依赖可用性。Gin 中只需注册两个独立 endpoint:
-
GET /health/live:仅返回200 OK,无任何外部调用,响应时间稳定在毫秒级 -
GET /health/ready:可包含 DB、Redis、下游 gRPC 服务连通性检测,但必须设置显式超时(如ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)) - 两者都应设置
w.Header().Set("Content-Type", "text/plain; charset=utf-8"),避免 Kubernetes 探针因 Content-Type 不识别而解析失败
示例精简实现:
func setupHealthRoutes(r *gin.Engine) {
r.GET("/health/live", func(c *gin.Context) {
c.String(http.StatusOK, "ok")
})
r.GET("/health/ready", func(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 2*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
c.Status(http.StatusServiceUnavailable)
return
}
if err := redisClient.Ping(ctx).Err(); err != nil {
c.Status(http.StatusServiceUnavailable)
return
}
c.String(http.StatusOK, "ready")
})
}
探针配置必须与 Gin handler 行为对齐
Kubernetes YAML 中的 probe 参数不是摆设,它们和 handler 的实现是强耦合的。常见错配包括:
-
initialDelaySeconds设太小:Gin 应用启动耗时 > 该值,探针在服务真正 ready 前就开始失败 -
periodSeconds过短 + handler 有 DB 检查:高频探测加重数据库压力,尤其在集群扩缩容时易触发雪崩 -
timeoutSeconds小于 handler 内部 context 超时:探针先超时,handler 还在等 DB 响应,造成状态不一致 - 未设置
successThreshold: 1:默认为 1,但某些旧版 kubelet 会要求连续成功 2 次才认为就绪,导致服务延迟上线
推荐生产配置(配合上文 handler):
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 2
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
successThreshold: 1
别在健康检查里做日志、指标或中间件链
很多团队习惯给 /health 加上 logger、prometheus、auth 中间件——这会显著拖慢响应,并引入非必要失败点。Kubernetes 探针不需要认证,也不需要记录每次探测的详细日志(它每 10 秒打一次,日志量会爆炸)。
- 移除所有中间件:用
r.NoRoute()或单独 router group 隔离健康端点 - 禁用 Gin 的
gin.Logger()和gin.Recovery()对健康路径生效 - 不要在 handler 里调用
metrics.Inc()或写入本地文件,这些操作可能阻塞或失败
最安全的做法是:在 setupHealthRoutes 中直接使用 gin.New() 创建一个无中间件的子引擎,或确保健康路由注册在 gin.Default() 之前且不经过公共中间件链。


















