我不会起名字322· 后端 / 算法 / 数据库 技术栈 力扣 Hot100 Go 项目 Redis MySQL文章目录K8s 探针配置的 3 个反直觉行为为什么 Pod 一直没就绪一、先分清三种探针很多人混用二、反直觉行为 1startupProbe 与 initialDelaySeconds 的优先级三、反直觉行为 2什么时候被杀 failureThreshold × periodSeconds四、反直觉行为 3liveness 打到未就绪的 Pod → 重启风暴五、探针参数对照表可带走物六、排查没就绪的三步K8s 探针配置的 3 个反直觉行为为什么 Pod 一直没就绪“应用日志明明写着server started on :8080为什么 Pod 一直是0/1”这是个几乎每个上 K8s 的人都会遇到的困惑。你盯着 Pod 状态READY是0/1kubectl describe里一堆Readiness probe failed。而应用自己觉得一切正常。问题几乎总是出在探针配置上。这篇先分清三种探针再拆 3 个反直觉行为。一、先分清三种探针很多人混用探针作用失败后果失败的严重度livenessProbe判断容器活着吗重启容器高会重启readinessProbe判断容器能收流量吗从 Service 摘除不重启中不接流量startupProbe判断容器启动完了吗重启容器高会重启最常见的错误把 liveness 探针配得太激进去探业务接口或者用 liveness 代替 readiness导致服务没就绪就被杀 → 重启 → 又没就绪的重启风暴。二、反直觉行为 1startupProbe 与 initialDelaySeconds 的优先级很多人以为配了startupProbe之后livenessProbe.initialDelaySeconds就无所谓了。基本正确但有个坑配了startupProbe在 startup 成功之前liveness 和 readiness 都不会执行。这期间只有 startup 在起作用。没配startupProbeliveness 会立即按initialDelaySeconds开始计时。反直觉点initialDelaySeconds不是启动后多久开始探而是容器创建后多久开始第一次探测。如果镜像拉取花了 40 秒而initialDelaySeconds 10那么第一次 liveness 探测发生在应用还没启动时——直接失败。# ❌ 危险初始延迟太短livenessProbe:httpGet:{path:/healthz,port:8080}initialDelaySeconds:5# 应用 30 秒才启动完 → 前 5 次全失败periodSeconds:5failureThreshold:3# 15 秒后就被杀 → 重启风暴# ✅ 正确用 startupProbe 单独兜启动期startupProbe:httpGet:{path:/healthz,port:8080}periodSeconds:5failureThreshold:30# 最多等 150 秒启动livenessProbe:httpGet:{path:/healthz,port:8080}periodSeconds:10failureThreshold:3记忆点startupProbe是给启动慢的应用用的配了它liveness 的initialDelaySeconds就可以设成 0。三、反直觉行为 2什么时候被杀 failureThreshold × periodSeconds这是最容易被误算的一个。决定多久被杀的不是initialDelaySeconds而是最长容忍时间 failureThreshold × periodSeconds举例livenessProbe:initialDelaySeconds:10# 第一次探测在 10s 后periodSeconds:10# 每 10s 探一次failureThreshold:3# 连续失败 3 次就杀从容器创建到被杀的最坏时间initialDelaySeconds failureThreshold × periodSeconds 10 30 40 秒。反直觉点timeoutSeconds是每次探测的超时它也算进周期。如果timeoutSeconds 5、periodSeconds 10那实际周期可能变成探测 5 秒超时 等 5 秒节奏被打乱。# 完整的时间账livenessProbe:initialDelaySeconds:10periodSeconds:10timeoutSeconds:5# 单次探测超时failureThreshold:3successThreshold:1# liveness 必须为 1# 最坏10 3×(5超时10间隔) 55 秒才被杀readiness 的 successThreshold 可以 1需要连续成功 N 次才算就绪但liveness 的 successThreshold 只能是 1这是强制的。四、反直觉行为 3liveness 打到未就绪的 Pod → 重启风暴这是最惨烈的一种。典型配置错误# ❌ liveness 探了一个依赖就绪的接口livenessProbe:httpGet:{path:/api/health,port:8080}# 这个接口依赖下游 DB/Redis问题链服务启动但下游 DB 还没连上或者自己还没热身完/api/health因为依赖未就绪返回 500liveness 判定失败 → 重启容器重启后又没连上下游 → 又失败 → 又重启重启风暴——Pod 永远起不来还拖垮了下游。正确原则探针该探什么不该探什么liveness进程是否卡死一个不依赖任何外部组件的轻量端点业务接口、依赖下游的接口readiness是否具备处理请求的能力可以含依赖检查会触发重活的接口# ✅ liveness 只探进程健康livenessProbe:httpGet:{path:/healthz,port:8080}# 只返回 200不查依赖# ✅ readiness 探业务就绪可含依赖readinessProbe:httpGet:{path:/readyz,port:8080}# 检查 DB 连接等Go 实现示例// 健康端点分离mux.HandleFunc(/healthz,func(w http.ResponseWriter,r*http.Request){// liveness只要进程活着就返回 200不查任何依赖w.WriteHeader(http.StatusOK)w.Write([]byte(ok))})mux.HandleFunc(/readyz,func(w http.ResponseWriter,r*http.Request){// readiness检查下游依赖ctx,cancel:context.WithTimeout(r.Context(),1*time.Second)defercancel()iferr:db.PingContext(ctx);err!nil{w.WriteHeader(http.StatusServiceUnavailable)w.Write([]byte(db not ready))return}w.WriteHeader(http.StatusOK)w.Write([]byte(ready))})五、探针参数对照表可带走物参数含义推荐值常见错误initialDelaySeconds创建后多久首次探测liveness: 配了 startup 可设 0设太短→启动期被杀periodSeconds探测间隔10readiness 可 5太小→频繁探测耗资源timeoutSeconds单次超时2~5大于 period→节奏乱failureThreshold连续失败几次算失败liveness: 3readiness: 3设 1→一次抖动就摘successThreshold连续成功几次算成功readiness: 1~2liveness: 必须 1readiness 设太高→恢复慢六、排查没就绪的三步顺序动作命令1看具体失败原因kubectl describe pod pod找Probe failed那行2手动模拟探测kubectl exec -it pod -- curl -v localhost:8080/readyz3核对端口和路径探针的port是容器端口不是 Service 端口第 3 条特别常错探针的port必须写容器内监听的端口。如果写了 Service 的端口或 hostPort探测必然失败。留个问题给你你们的 liveness 探针是探/healthz这种纯进程健康端点还是探业务接口initialDelaySeconds现在配的是多少