就绪探针(readinessProbe)应检查容器是否真正具备接收流量的能力,即所有上游依赖(如DB、Redis、gRPC服务)是否可连通可读写,以及关键内部状态是否就绪;不可仅检查端口存活或本地文件,避免耗时操作和未设超时的HTTP客户端调用。

就绪探针(readinessProbe)该检查什么?
就绪检查不是“服务能不能启动”,而是“它现在能不能收请求”。Kubernetes 会把未就绪的 Pod 从 Service 的 Endpoint 列表里摘掉,所以检查逻辑必须反映真实流量承载能力。
常见错误是只 ping localhost:8080/healthz 或检查进程存活——这完全没意义,HTTP 服务端口开着不代表 DB 连得上、依赖缓存可用、或内部状态机已初始化完毕。
- 必须检查所有上游依赖是否可写/可读:比如 PostgreSQL 连接池是否 ready、Redis 是否能执行
SET、gRPC 依赖服务是否响应/health - 避免检查“冷启动中”的资源:比如检查本地文件是否存在,但该文件本该由 initContainer 写入,此时失败会导致循环重启
- 不要在就绪检查里做耗时操作(如拉取大配置、扫描磁盘),超时默认 1 秒,超过会反复失败
用 net/http 实现 HTTP 就绪端点的典型结构
Go 标准库 net/http 足够轻量,无需引入框架。关键在于把依赖检查收敛到一个同步函数,并控制超时和错误分类。
示例端点逻辑:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
func readinessHandler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 500*time.Millisecond)
defer cancel()
<pre class="brush:php;toolbar:false;">err := checkAllDependencies(ctx)
if err != nil {
http.Error(w, "dependencies unavailable", http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusOK)}
-
context.WithTimeout必须设,防止某个依赖卡住整个 HTTP handler - 返回
http.StatusServiceUnavailable(503)是 Kubernetes 识别“不就绪”的标准信号,不要用 500 或重定向 - 检查函数
checkAllDependencies应并行发起各依赖探测(用errgroup.Group),但需限制并发数,避免压垮下游
livenessProbe 和 readinessProbe 能共用同一个端点吗?
技术上可以,但不推荐。二者语义不同:就绪检查失败应摘流,而存活检查失败会触发容器重启——如果共用端点,一次 DB 临时抖动可能直接杀掉整个 Pod,反而延长恢复时间。
- 就绪端点(如
/readyz)应检查依赖连通性 + 关键内部状态(如 gRPC server 已注册 handler) - 存活端点(如
/healthz)只需确认进程活着、goroutine 没死锁、内存没 OOM —— 甚至可以只返回 200,不做任何外部调用 - Kubernetes 允许为两者配置不同路径、超时、失败阈值,分开定义更可控
Pod 启动后就绪检查一直失败?重点查 initContainer 和 startupProbe
很多 Go 服务在 main 函数里初始化 DB 连接、加载配置、预热缓存,耗时可能达数秒。Kubernetes 默认立即开始就绪检查,必然失败。
- 先加
startupProbe:给启动窗口期,例如failureThreshold: 30+periodSeconds: 10,相当于允许最多 5 分钟启动 - 确保
initContainer真的完成了它该做的事:比如等待 ConfigMap 挂载完成,或调用外部 API 获取 token,别漏掉exit 0 - 检查 Go 服务日志里是否有 panic 或阻塞在
database/sql.Open上——这种问题不会报错,但会卡住就绪检查
最常被忽略的是:就绪检查的 HTTP client 默认没有设置 Timeout,一旦依赖服务 DNS 解析慢或网络延迟高,http.Get 可能卡住几十秒,导致 probe 超时判定失败。务必显式配置 http.Client{Timeout: 2 * time.Second}。

















