先给结论:健康检查探针不是"越探越稳",探得太深反而可能成为故障放大器。我踩过一次真实的坑,今天想把这个争点摊开聊。
【背景】
我维护一批 K8s 1.27(12 节点、containerd 1.7)的核心服务。去年一次 Redis 抖动约 40 秒(网络抖动叠加一次慢查询把主从复制拉落后),我们一个订单服务的 readinessProbe 是个 HTTP 端点,内部直接 redis-cli ping + 查一个业务 key。Redis 抖的那 40 秒里,4 个副本 readiness 全红,kube-proxy / Ingress 把流量全切走——但问题在于:上游还有一层应用依赖这个服务,它开始超时、开始重试,重试把 Redis 连接数直接打爆,一次 40 秒的小抖动被放大成 8 分钟的雪崩。事后复盘,"探针探得越全,越容易在资源抖动时把整批实例同时摘掉",这句话我记到现在。
【几种常见做法 / 分歧】
- 浅探针派:liveness 只探 TCP 端口、进程存活;readiness 只探框架层 HTTP 200,绝不碰 DB / Redis / 下游 RPC。理由:探针的语义只是"我还活着 / 能接流量",不该替业务做健康判断;更关键的是,探针一旦依赖了它要保护的资源,等于把故障半径从"一个实例"扩大到"整批实例 + 上游重试放大"。
- 深探针派:readiness 必须探到核心依赖就绪(DB 可连、缓存已热、队列可写)。理由:灰度发布 / 扩容时,流量打到还没 ready 的实例,比"探活把实例踢下线"更糟——那是直接 5xx,是用户可见的故障。
- 折中派:liveness 浅、readiness 深,但只探"硬依赖",且探针自身带超时和降级;再加"摘除保护"——连续 N 次失败才摘,且摘除时保留最小可用副本。
【我的倾向与理由】 我站"分级 + 探针自保护",具体做法(可复现):
- livenessProbe:
tcpSocket只探端口,failureThreshold=3、periodSeconds=10、timeoutSeconds=1。它唯一职责是"进程假死 / 线程池全阻塞时重启",永不依赖外部资源。 - readinessProbe:探 HTTP 端点,但端点内部只做"本地就绪"判断(线程池可调度、配置已加载、gRPC 端口 LISTEN);对 DB / Redis 用"软检查"——单次查询带 200ms 超时,失败不立即返回 503,而是进入"degraded 但可接流"状态,把兜底交给上游的超时 + 熔断。
- 关键一条:探针自身必须有超时上限(100–300ms),kubelet 侧
failureThreshold给足(3–5)、periodSeconds拉长到 10s,避免一次 30 秒的资源抖动就把整批实例同时摘掉——摘除窗口应该比"抖动收敛时间"长一个量级。 - 更狠一条:给摘除加"最小可用副本"保护(PDB 或自实现 minAvailable),摘除前校验
availableReplicas ≥ N,否则拒绝摘除,宁可让个别实例带病扛,也不让服务整体摘空。
一句话:liveness 判"死没死",readiness 判"能不能扛",而"扛不住"和"死"要分开处理,不能让探针替业务做"全有或全无"的裁决。
【想听大家在什么场景下怎么选】
- 你们线上 liveness / readiness 分别探到哪一层?有没有探针直接查 DB / Redis / 下游 RPC 的?踩过"探活本身触发雪崩"吗?
- 灰度发布 / 金丝雀场景里,"readiness 探深依赖"和"流量打到没 ready 的实例"你们怎么权衡?有没有中间态(只读放行、降级放行)?
- 探针自保护的阈值(failureThreshold / periodSeconds / timeoutSeconds / minAvailable)你们怎么定?有没有数据支撑——抖动时长 vs 摘除窗口的关系?
- 有没有人试过在"内核 / 连接层"判断就绪(eBPF 看 socket 状态、sidecar 探下游 RTT),而不是让应用自己报 ready?这条路你们觉得能落地吗?
欢迎贴你们线上真实的探针配置和事故时间线,比结论更有用。
0赞
0收藏
0楼层