Golang应用在Kubernetes中的健康检查配置 Go语言实现Liveness与Readiness

7次阅读

go http handler需用context.withtimeout控制超时、避免panic和goroutine泄漏,liveness与readiness应分离且逻辑精简,shutdown前需主动摘除endpoint并协调k8s信号流。

Golang应用在Kubernetes中的健康检查配置 Go语言实现Liveness与Readiness

Go HTTP handler 怎么写才符合 kubernetes 的 /healthz 要求

Kubernetes 的 livenessProbereadinessProbe 默认用 HTTP GET 检查,但 Go 默认的 http.HandleFunc 不会自动处理超时、panic 或阻塞,容易让探针误判为失败。

必须确保 handler 在几秒内返回且不 panic。常见错误是直接在 handler 里调用数据库连接检查或长耗时逻辑,导致 probe 超时(默认 1 秒),K8s 连续失败后重启 Pod。

  • context.WithTimeout 包裹所有外部依赖调用,超时时间设为 probe timeoutSeconds 的 60% 左右(比如 probe 设 3s,代码里用 1.8s)
  • handler 内不要做 log.Fatal 或未捕获的 panic,否则整个进程挂掉;改用 recover() + 返回 500
  • 避免在 handler 中新建 goroutine 后不等结果就返回 —— 探针只看 HTTP 状态码和响应体,不等后台完成

示例:

func healthz(w http.ResponseWriter, r *http.Request) {     ctx, cancel := context.WithTimeout(r.Context(), 1800*time.Millisecond)     defer cancel()      if err := db.PingContext(ctx); err != nil {         http.Error(w, "db unreachable", http.StatusInternalServerError)         return     }     w.WriteHeader(http.StatusOK)     w.Write([]byte("ok")) }

为什么 readinessProbe 返回 200 但 Pod 还是不进 Service

不是返回 200 就算就绪 —— Kubernetes 要求 endpoint 必须稳定通过 probe 若干次(由 initialDelaySecondsperiodSeconds 共同决定),且 endpoint 列表需同步更新。

典型问题:Go 应用启动快,但 gRPC server 或消息队列消费者还没 ready,此时 readiness handler 却已返回 200,Service 流量进来后请求失败。

立即学习go语言免费学习笔记(深入)”;

  • 把 readiness 拆成多层检查:基础 HTTP 可达 → 本地依赖(如 config 加载)→ 关键远程依赖(如 redis、下游 API)
  • 关键依赖用「缓存上次检查结果 + 异步刷新」策略,避免每次 probe 都重连;例如用 sync.Once 初始化,probe 时只读状态变量
  • 确认 kube-proxy 或 CNI 插件没卡住 endpoint 同步 —— 查 kubectl get endpoints <svc-name></svc-name>,看 IPs 是否及时出现

livenessProbe 失败重启后反复 CrashLoopBackOff 怎么定位

根本原因往往是 liveness handler 自身不稳定,或者 probe 配置和代码逻辑冲突。比如 handler 里检查了磁盘空间,但磁盘满导致 handler panic,Pod 重启后又立刻满,形成死循环

更隐蔽的是:liveness 和 readiness 共用同一个 handler,而 readiness 应该容忍短暂抖动,liveness 却要求绝对稳定 —— 二者逻辑不该混用。

  • 给 liveness 单独写 handler,只检查最核心项(如进程是否还在运行、HTTP server 是否 accept 连接),别碰外部依赖
  • 加日志:在 liveness handler 开头打 timestamp,在结尾打「liveness ok」,方便查是不是卡在中间某步
  • 临时把 livenessProbe.failureThreshold 调大(比如从 3 改成 10),再看日志 —— 如果连续 10 次都失败,基本能排除偶发网络抖动,聚焦代码逻辑

Go net/http Server Shutdown 与 probe 的竞态问题

应用收到 SIGTERM 后调用 srv.Shutdown(),但 Kubernetes 在发送 SIGTERM 前已停止发 readiness probe;如果 shutdown 期间还接受新连接,旧连接又没及时 close,probe 可能打到正在关闭的 server 上,返回 404 或 connection refused,触发误杀。

这不是配置问题,是 Go HTTP server 生命周期和 K8s 信号协作没对齐。

  • srv.Shutdown() 前,先关掉 readiness handler(比如用原子开关控制 handler 返回 503),让 endpoint 从 Service 中快速摘除
  • 设置 readinessProbe.initialDelaySeconds 大于应用冷启动时间,避免 probe 在 server listen 前就发起请求
  • http.Server.IdleTimeout 防止长连接拖慢 shutdown —— 否则 Shutdown() 可能等几十秒才返回

真正麻烦的从来不是怎么写 handler,而是 handler 的生命周期如何跟 K8s 的信号流、endpoint 同步节奏咬合上。少一个 context.WithTimeout,或多一次没受控的 goroutine,都可能让健康检查从保护机制变成定时炸弹。

text=ZqhQzanResources