就绪探针路径必须与go handler注册路径完全一致,如deployment中设path: "/readyz",则代码中必须显式注册http.handlefunc("/readyz", ...),且返回200状态码;路径不匹配或返回非200(如204)将导致pod卡在containercreating或crashloopbackoff。

就绪探针路径必须和 Go handler 注册路径一字不差
常见错误是 Deployment YAML 里写 path: "/readyz",但 Go 代码里只注册了 /ready 或 /healthz,结果 kubelet 每次 GET 都返回 404,Pod 卡在 ContainerCreating 或反复进 CrashLoopBackOff。
Go 标准库 http.ServeMux 不做路径归一化,/readyz 和 /readyz/ 是两个不同路由。别指望中间件自动 trim 尾斜杠——kubelet 发的请求就是裸 HTTP,没浏览器那套重定向逻辑。
- 统一约定:所有探针路径不带尾部斜杠,比如固定用
/readyz、/healthz - 注册时必须显式匹配:
http.HandleFunc("/readyz", func(w http.ResponseWriter, r *http.Request) { ... }) - 若用 Gin,写
r.GET("/readyz", handler),别用r.Any()或通配路由 - 检查方式:本地跑起服务后,直接
curl -v http://localhost:8080/readyz,确认返回 200 且无重定向
就绪探针 handler 必须返回 200 状态码,不能是 204 或 201
Kubernetes 的 readinessProbe 只认 http.StatusOK(即 200),哪怕你返回的是合法的 204 No Content,kubelet 也会判定为失败,把 Pod 从 Service 的 Endpoint 列表里摘掉。
Go 的 http.ServeHTTP 默认状态码就是 200,但一旦你显式调用 w.WriteHeader(),就必须传 http.StatusOK。很多人图省事写成 w.WriteHeader(204) 或 w.WriteHeader(http.StatusNoContent),结果探针永远过不了。
- 安全写法:
w.WriteHeader(http.StatusOK),别依赖默认值 - 避免重复写状态码:不要先
w.WriteHeader(200),再w.Write(...)后又调一次w.WriteHeader() - 如果返回 JSON,记得加头:
w.Header().Set("Content-Type", "application/json"),旧版 kubelet 对空 content-type 解析不稳定 - 响应体建议只写
"ok"或空字节,别塞大 JSON——探针不是 API 接口
就绪探针超时时间必须小于 timeoutSeconds,且不能查重依赖
Deployment 中 readinessProbe.timeoutSeconds 默认是 1 秒。如果你的 handler 里连了数据库、调了外部 gRPC、或做了文件 IO,很容易超时,导致 probe 失败、Pod 被反复重启。
/readyz 的职责是回答“这个实例现在能不能收流量”,不是“它背后整个系统健不健康”。DB 连通性可以查,但得设短超时、加缓存、用 atomic 做状态快照,别每次 probe 都走一遍真实链路。
- DB 检查务必加 context 超时:
ctx, cancel := context.WithTimeout(r.Context(), 500*time.Millisecond) - 关键依赖状态建议用
sync/atomic缓存,probe handler 只读原子变量,不触发实际调用 - 避免日志、锁、网络 I/O —— 它应该是个 O(1) 的内存判断
- 本地验证:用
ab -n 100 -c 10 http://localhost:8080/readyz测 P99 响应时间,确保
Dockerfile 和 Deployment 必须协同对齐端口与镜像构建
Go 服务监听 :8080,但 Deployment 里 containerPort 写成 80,或者 Dockerfile 里没设 EXPOSE 8080,都会让探针连不上,报 connection refused。
更隐蔽的问题是镜像构建:如果忘了关 CGO 或没指定 GOOS,二进制在 distroless 镜像里会直接报 no such file or directory——不是找不到文件,是找不到动态链接器。
-
containerPort必须和 Gohttp.ListenAndServe(":8080", ...)的端口一致 - Dockerfile 多阶段构建必须同时设:
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app . - 基础镜像推荐
gcr.io/distroless/static-debian12或scratch,但用scratch时CMD必须是绝对路径:CMD ["/app"],且WORKDIR / - 启动日志加一句:
log.Printf("server listening on :%s", port),方便排查CrashLoopBackOff时到底卡在哪步
initialDelaySeconds: 5 不是摆设,它和 handler 的轻量性必须一起设计。











