健康检查接口必须注册到HTTPServer路由中,否则curl访问/healthz会返回404;gRPC健康服务需单独调用grpc_health_v1.RegisterHealthServer注册,二者互不影响。

健康检查接口必须注册在 HTTPServer 上,不能只配 gRPC
Kratos 的健康检查默认不自动暴露 HTTP 端点,即使你启用了 health.NewHandler(),如果没把它挂到 HTTPServer 的路由里,curl http://localhost:9000/healthz 就会返回 404。gRPC 的健康检查(grpc.health.v1.Health)是独立协议,和 HTTP 健康探针互不影响——K8s liveness/readiness 默认走 HTTP,所以只配 gRPC 没用。
正确做法是显式把健康 handler 注册进 HTTP 路由:
// 在 wire.go 或 server 初始化处
h := health.NewHandler(health.WithStatusFunc(yourStatusFunc))
srv := http.NewServer(
http.Address(":9000"),
http.Middleware(
recovery.Recovery(),
),
)
srv.Handle("/healthz", h) // 注意:不是 HandleFunc,要用 Handle
-
Handle接收的是http.Handler,而health.NewHandler()返回的就是它;HandleFunc只接受函数,直接传会编译失败 - 路径建议用
/healthz或/health,避免和业务路由冲突;K8s 默认探测路径可配置,但别用/或/status这类易撞车的 - 如果用了
gin或echo作为 HTTP 框架(非 Kratos 原生http.Server),得手动适配为对应框架的 handler 类型
health.WithStatusFunc 决定检查逻辑,不是开箱即用的“存活即健康”
默认的 health.NewHandler() 只返回 SERVING,不校验数据库、Redis、下游依赖等真实状态。生产环境必须自定义 WithStatusFunc,否则 K8s 可能误判——服务进程活着但 DB 连不上,照样发流量过去。
典型实现要聚合多个组件状态:
func yourStatusFunc(ctx context.Context) (stat health.Status, err error) {
stat = health.SERVING
// 检查 MySQL
if err = db.PingContext(ctx); err != nil {
stat = health.NOT_SERVING
return
}
// 检查 Redis
if err = redis.Ping(ctx).Err(); err != nil {
stat = health.NOT_SERVING
return
}
return
}
- 函数必须有上下文超时控制,否则探针卡住会拖垮 K8s kubelet;建议在
ctx上加context.WithTimeout(ctx, 2*time.Second) - 返回
health.NOT_SERVING时,HTTP 响应状态码仍是 200(符合 gRPC Health Checking Protocol 规范),K8s 靠响应体里的status字段判断,不是靠 HTTP 状态码 - 不要在该函数里做重试或复杂初始化——它是高频调用的,应轻量、幂等、无副作用
gRPC 健康检查需显式注册 grpc_health_v1.RegisterHealthServer
如果你的服务同时提供 gRPC 接口,且希望 gRPC 客户端或 Istio 等服务网格调用 Check 方法做健康判断,光有 HTTP handler 不够。Kratos 的 grpc.Server 不会自动注册健康服务,必须手动注册。
gs := grpc.NewServer(
grpc.Address(":9001"),
)
// 必须这行
grpc_health_v1.RegisterHealthServer(gs, health.NewServer())
// 再注册你的业务 service
api.RegisterYourServiceServer(gs, &YourService{})
- 顺序很重要:必须在
RegisterYourServiceServer之前注册HealthServer,否则 gRPC Server 启动时可能报重复注册错误 - 注册后,可用
grpcurl -plaintext localhost:9001 grpc.health.v1.Health/Check测试,请求体为{"service": ""}表示检查整体状态 - Kratos v2.5+ 的
health.NewServer()默认使用内存状态,如需对接分布式状态(比如多实例共享健康视图),得自己实现health.Checker接口并注入
K8s Probe 配置要匹配实际延迟和失败阈值
即使代码侧全配对了,K8s 的 livenessProbe 和 readinessProbe 参数设得太激进,会导致频繁重启或流量中断。Kratos 健康检查涉及 DB/Redis 连接,网络抖动时容易偶发超时。
- 初值建议:
initialDelaySeconds: 10(等服务冷启动完成)、timeoutSeconds: 3、failureThreshold: 3、periodSeconds: 10 - 不要把
liveness和readiness设成完全一样的路径和参数——liveness可稍宽松(比如允许 2 次失败才重启),readiness要更敏感(1 次失败就摘流量) - 如果健康检查里包含外部 HTTP 依赖(比如调第三方鉴权服务),务必加 circuit breaker 或 fallback,否则一个外部故障会雪崩拖垮整个健康检查链路
最常被忽略的一点:本地开发用 docker-compose 测试时,K8s Probe 的 hostNetwork: true 或 network_mode: host 配置缺失,导致容器内 curl 能通,但 kubelet 从宿主机 namespace 去连却超时——这时得看 iptables 和 CNI 插件日志,不是代码问题。


















