HTTP探针handler需返回200–399状态码且不超时、不panic、不重定向;应仅做轻量本地检查,避免网络I/O和中间件拦截,必要时用带deadline的context控制超时并默认返回200。

HTTP探针的 handler 怎么写才不被 kubelet 误判为失败
Kubernetes 的 livenessProbe 和 readinessProbe 默认走 HTTP GET,要求 handler 必须在 200–399 状态码范围内返回,且响应体可为空,但不能超时、不能 panic、不能返回 5xx 或重定向(302)。常见错误是 handler 里调了未就绪的下游服务或数据库连接,导致偶发 500,进而触发重启循环。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 健康检查 endpoint(如
/healthz)只做轻量级检查:内存是否 OOM、goroutine 数是否暴增、本地缓存是否加载完成——避免任何网络 I/O 或锁竞争 - 用
http.HandlerFunc直接注册,不要依赖中间件(比如 auth 中间件可能拦截并返回 401) - 加超时控制:handler 内部不做阻塞操作;若必须查状态,用带 context deadline 的子 goroutine +
select等待,超时直接 return 200(“假阳”好过“真阴”) - 示例:
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 100*time.Millisecond) defer cancel() select { case <-ctx.Done(): w.WriteHeader(http.StatusOK) // 不报错,宁可放过 return default: w.WriteHeader(http.StatusOK) } })
如何区分 liveness 和 readiness 的逻辑边界
很多人把两个 probe 写成同一个 handler,这是危险的。liveness 判定的是“进程是否还活着”,readiness 判定的是“能否接收流量”。一旦 readiness 失败,k8s 会从 Service endpoints 中摘除该 pod;而 liveness 失败会直接 kill container 并重启。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
/healthz→ 专用于livenessProbe:只检查进程基础状态(如runtime.NumGoroutine()是否 debug.ReadGCStats 是否卡住) -
/readyz→ 专用于readinessProbe:检查依赖是否 ready(如 Redis 连接池ping成功、gRPC 后端服务ConnectivityState() == CONNECTING || READY) - 不要在
/readyz里做耗时操作(比如 list 所有 configmap),kubelet 默认探测间隔是 10s,超时时间默认 1s,容易拖垮整个 probe 链路 - 如果依赖服务临时不可用,
/readyz应返回 503,而不是等它恢复——让 k8s 主动摘流,比让请求打进来再失败更可控
为什么用 exec 探针反而更容易出问题
exec 探针执行的是容器内 shell 命令,看似灵活,实际在 Go 程序中极少需要。常见误用是写 exec: ["sh", "-c", "kill -0 $(cat /var/run/myapp.pid)"],但 Go 程序通常不写 pid 文件,且容器里没 sh 会导致探针直接失败(exit code 127)。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 除非你明确需要检查某个二进制是否存在、或某个文件锁是否被持有,否则一律用 HTTP 探针
- 如果非要用 exec,命令必须是静态链接的二进制(如
cat、ps),且路径写绝对路径(/bin/cat而非cat),因为 alpine 镜像默认无/usr/bin/env - Go 程序自身可通过
os.Getpid()和syscall.Kill(pid, 0)在 handler 里模拟“进程存活”检查,没必要绕到 exec
Probe 配置参数怎么设才不反模式
k8s 探针的 initialDelaySeconds、periodSeconds、timeoutSeconds 设得太激进,是线上 pod 反复重启的高频原因。Go 程序启动慢(尤其带 gRPC server、Prometheus metrics、etcd client 初始化),还没 ready 就被 kill。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
-
initialDelaySeconds至少设为应用冷启动耗时 + 5s(可用time.Now()打日志测出 main 函数到 server.ListenAndServe 的耗时) -
timeoutSeconds必须 ≤periodSeconds,且建议设为 1–2s;Go HTTP handler 超过 2s 基本说明有阻塞,不该靠延长 timeout 来掩盖 -
failureThreshold别乱调大:设成 10 意味着连续 10 次失败(比如 100s)才重启,期间所有请求都 503——不如设为 3,快速失败快速恢复 - readinessProbe 的
periodSeconds可比 livenessProbe 大(如 10s vs 5s),避免频繁摘流影响负载均衡器的连接复用
log.Printf 打调试信息,而 log 包底层用的是锁,高并发 probe 下会成为瓶颈。

















