K8s探针生效前提是Go程序必须暴露语义分明的/livez(仅检查进程存活,无外部依赖)和/readyz(可查依赖但带≤1s超时)端点,用原子变量控制状态、避免中间件拦截、路径与YAML严格一致,否则将导致Pod反复重启或持续503。

只配 YAML 里的 livenessProbe 和 readinessProbe 不够,Go 程序必须自己暴露端点、区分语义、用原子变量控制状态,否则 K8s 会反复杀 Pod 或持续 503。
怎么写 /livez 和 /readyz 两个端点才不踩坑
别把两个探针都指向同一个 handler——语义混淆是线上重启风暴的常见源头。
-
/livez只检查进程是否卡死:比如主 goroutine 是否还在跑、关键 channel 是否阻塞、atomic.LoadInt32(&health)是否为 1。不访问 DB、Redis、HTTP 外部服务 -
/readyz可查依赖,但必须带超时且≤1s:用ctx, cancel := context.WithTimeout(r.Context(), 800*time.Millisecond)包裹db.PingContext()或redis.Ping(),超时直接返回http.StatusServiceUnavailable - 避免中间件拦截:直接用
http.HandleFunc("/livez", ...)注册,别让 Gin/Zap/Recovery 中间件吞掉或延迟响应 - 返回体加时间戳:
{"status":"ok","ts":1730016000},方便日志里对齐 probe 调用和实际故障点
YAML 里 livenessProbe 和 readinessProbe 怎么配才不打架
配错参数会让滚动更新卡住、Pod 频繁重启却始终没流量,核心是让两者“各司其职、节奏错开”。
-
initialDelaySeconds:Go 启动慢(比如加载配置、初始化 gRPC client),livenessProbe至少设30,readinessProbe可设10,但不能比你代码里真正就绪的时间还短 -
periodSeconds和failureThreshold要匹配:若 DB 抖动周期约 3s,设periodSeconds: 5+failureThreshold: 3,避免单次抖动就触发重启 -
timeoutSeconds必须小于periodSeconds:比如timeoutSeconds: 2,periodSeconds: 5,否则 probe 会堆积、占用连接数 - 路径和端口必须和 Go 服务监听一致:如果 Go 监听
:8080且注册了/readyz,YAML 里就得写port: 8080+path: /readyz,错一个就 404 → 探针失败
Go 里怎么动态控制 readiness 状态(比如收到 SIGTERM)
K8s 不支持运行时改探针配置,但你可以让 /readyz handler 主动返回非 200,Endpoint Controller 就会自动从 Service 的 endpoints 列表里摘掉这个实例。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 用原子变量控制开关:
var isReady int32 = 1,在/readyzhandler 开头加判断:if atomic.LoadInt32(&isReady) == 0 { http.Error(w, "shutting down", http.StatusServiceUnavailable); return } - SIGTERM 处理逻辑:注册
signal.Notify(sigChan, syscall.SIGTERM),收到信号后先atomic.StoreInt32(&isReady, 0),再 sleep 几秒等正在处理的请求完成,最后srv.Shutdown() - 别在
/livez里改这个变量:liveness 失败会重启,而 readiness 失败只是摘流量——这是两者根本区别
用 go-health 这类库要注意什么
它能简化多依赖检查的聚合,但容易掩盖超时和阻塞问题,上线前务必压测验证。
- 每个 checker 必须自带上下文超时:
health.AddChecker("db", health.CheckerFunc(func(ctx context.Context) error { return db.PingContext(ctx) })),别让它默认用无限 timeout - 避免在 checker 里做重操作:比如调用
SELECT * FROM huge_table,应该只做SELECT 1或连接池健康检查 - 聚合结果默认是“全通过才 ok”,但有些依赖临时不可用可容忍(如日志上报服务),得自己 wrap logic 实现降级逻辑
- 注意它注册的 handler 默认路径是
/health,和 K8s 推荐的/livez//readyz不一致,要么改路径,要么在 YAML 里同步调整path
最常被忽略的是:probe 的超时设置必须严格小于 K8s 自身 probe 调用间隔,且 Go 端 handler 内部所有子调用(DB、Redis、HTTP)的 context 超时要再砍掉至少 200ms——网络抖动和调度延迟永远存在。

















