我不会起名字322· 后端 / 算法 / 数据库
📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL
文章目录
- K8s 探针配置的 3 个反直觉行为:为什么 Pod 一直没就绪
- 一、先分清三种探针(很多人混用)
- 二、反直觉行为 1:startupProbe 与 initialDelaySeconds 的优先级
- 三、反直觉行为 2:什么时候被杀 = failureThreshold × periodSeconds
- 四、反直觉行为 3:liveness 打到未就绪的 Pod → 重启风暴
- 五、探针参数对照表(可带走物)
- 六、排查没就绪的三步
K8s 探针配置的 3 个反直觉行为:为什么 Pod 一直没就绪
“应用日志明明写着server started on :8080,为什么 Pod 一直是0/1?”
这是个几乎每个上 K8s 的人都会遇到的困惑。你盯着 Pod 状态,READY是0/1,kubectl describe里一堆Readiness probe failed。而应用自己觉得一切正常。
问题几乎总是出在探针配置上。这篇先分清三种探针,再拆 3 个反直觉行为。
一、先分清三种探针(很多人混用)
| 探针 | 作用 | 失败后果 | 失败的"严重度" |
|---|---|---|---|
| livenessProbe | 判断容器活着吗 | 重启容器 | 高(会重启) |
| readinessProbe | 判断容器能收流量吗 | 从 Service 摘除,不重启 | 中(不接流量) |
| startupProbe | 判断容器启动完了吗 | 重启容器 | 高(会重启) |
最常见的错误:把 liveness 探针配得太激进去探业务接口,或者用 liveness 代替 readiness,导致"服务没就绪就被杀 → 重启 → 又没就绪"的重启风暴。
二、反直觉行为 1:startupProbe 与 initialDelaySeconds 的优先级
很多人以为配了startupProbe之后,livenessProbe.initialDelaySeconds就无所谓了。基本正确,但有个坑:
- 配了
startupProbe:在 startup 成功之前,liveness 和 readiness 都不会执行。这期间只有 startup 在起作用。 - 没配
startupProbe:liveness 会立即按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(这是强制的)。
四、反直觉行为 3:liveness 打到未就绪的 Pod → 重启风暴
这是最惨烈的一种。典型配置错误:
# ❌ liveness 探了一个"依赖就绪"的接口livenessProbe:httpGet:{path:/api/health,port:8080}# 这个接口依赖下游 DB/Redis问题链:
- 服务启动,但下游 DB 还没连上(或者自己还没热身完);
/api/health因为依赖未就绪返回 500;- liveness 判定失败 → 重启容器;
- 重启后又没连上下游 → 又失败 → 又重启;
- 重启风暴——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 | 探测间隔 | 10(readiness 可 5) | 太小→频繁探测耗资源 |
timeoutSeconds | 单次超时 | 2~5 | 大于 period→节奏乱 |
failureThreshold | 连续失败几次算失败 | liveness: 3;readiness: 3 | 设 1→一次抖动就摘 |
successThreshold | 连续成功几次算成功 | readiness: 1~2;liveness: 必须 1 | readiness 设太高→恢复慢 |
六、排查没就绪的三步
| 顺序 | 动作 | 命令 |
|---|---|---|
| 1 | 看具体失败原因 | kubectl describe pod <pod>找Probe failed那行 |
| 2 | 手动模拟探测 | kubectl exec -it <pod> -- curl -v localhost:8080/readyz |
| 3 | 核对端口和路径 | 探针的port是容器端口,不是 Service 端口 |
第 3 条特别常错:探针的port必须写容器内监听的端口。如果写了 Service 的端口或 hostPort,探测必然失败。
留个问题给你:你们的 liveness 探针是探/healthz这种纯进程健康端点,还是探业务接口?initialDelaySeconds现在配的是多少?