Kubernetes生产运维11:探针配错比不配还危险,三类探针到底该怎么设计

发布时间:2026/8/30 19:45:59
Kubernetes生产运维11:探针配错比不配还危险,三类探针到底该怎么设计 Kubernetes生产运维11探针配错比不配还危险三类探针到底该怎么设计写在前面探针是Kubernetes里一个很微妙的东西配好了能自愈、能优雅摘流量配错了能把一次小小的下游抖动放大成整个服务被反复重启的雪崩。很多人把三种探针当成都是健康检查随手复制一份配置到处用甚至让liveness探针去检查数据库连通性。这类配置平时看不出问题一旦下游抖动就可能引发集体重启。三种探针的职责其实完全不同Startup探针判断应用是否启动完成保护慢启动应用不被过早杀死Readiness探针判断应用是否能接收流量失败只摘流量、不重启Liveness探针判断应用是否需要重启失败会重启容器这篇不是排障,而是设计。核心是讲清三者的职责边界、失败后果和配合方式,避免配出比不配还危险的探针。本文按照Kubernetes官方机制和命令参考整理。由于当前没有连接可验证的实验集群配置和终端输出均为C级生产化重建不是生产原始记录。不同Kubernetes版本、应用框架和探针类型可能影响细节落地前应结合应用实际启动和依赖情况调整。一、先理解三类探针的职责1.1 三者各管各的探针回答的问题失败后果运行时机Startup应用启动完成了吗未完成时kubelet按重启策略处理超过阈值杀容器只在启动阶段执行Readiness应用能接收流量吗从Service的Endpoint里摘掉不重启整个生命周期周期性执行Liveness应用需要重启吗重启容器整个生命周期周期性执行关键区别Readiness失败只摘流量Liveness失败会重启。把两者混用是最常见的错误。1.2 Startup探针解决慢启动矛盾慢启动应用有个矛盾启动要几分钟但liveness探针如果启动就开始检查会在应用还没起来时就判定它不健康而重启导致永远起不来。Startup探针解决这个它成功之前liveness和readiness探针都不执行。相当于给慢启动应用一段受保护的启动时间。等startup成功才把健康检查交接给liveness和readiness。比起用liveness的initialDelaySeconds硬等startup探针更精准应用早启动完就早交接不用固定等一个保守的长时间。1.3 探针的检查方式三类探针都支持几种检查方式httpGet请求一个HTTP端点2xx/3xx算成功tcpSocket能建立TCP连接算成功exec在容器里执行命令退出码0算成功grpcgRPC健康检查选哪种取决于应用但检查的内容比方式更重要下面会讲。二、探针设计决策树给一个应用配探针 │ ├─ 应用启动慢吗 │ ├─ 是 → 配Startup探针保护启动阈值覆盖最坏启动时间 │ └─ 否 → 可不配Startup用readiness的initialDelay │ ├─ 配Readiness判断能否接收流量 │ ├─ 检查应用自身能否服务 │ └─ 可以包含关键依赖但要理解摘流量的后果 │ ├─ 配Liveness判断是否需要重启 │ ├─ 只检查进程自身是否卡死不查下游 │ └─ 阈值宽松避免抖动误杀 │ └─ 检查三者是否会相互放大故障设计探针前先问三个问题这个应用启动慢不慢它自身卡死时重启能不能救它的健康是否依赖下游答案决定探针怎么配。三、逐个探针的设计原则3.1 Startup探针给足最坏启动时间Startup探针的阈值要覆盖应用最坏情况下的启动时间包括冷启动、加载大模型或大缓存、首次连接建立。startupProbe:httpGet:path:/healthzport:8080periodSeconds:10failureThreshold:30上面的配置允许最多periodSeconds × failureThreshold即约300秒启动。原则阈值宁可宽松覆盖最慢的正常启动启动完成后startup不再执行宽松不影响运行期用了startup探针后liveness的initialDelay可以设小因为有startup保护3.2 Readiness探针判断能否服务失败只摘流量Readiness决定Pod是否出现在Service的Endpoint里。失败时Pod被摘流量但不重启恢复后自动加回。readinessProbe:httpGet:path:/readyport:8080periodSeconds:10failureThreshold:3设计要点readiness可以检查应用是否准备好服务包括必要的初始化完成如果应用暂时不能服务如正在重载配置、依赖不可用readiness失败摘流量是合理的自我保护readiness失败不重启所以即使检查了依赖最坏结果是摘流量不会引发重启雪崩优雅关闭时readiness先失败摘流量再终止避免请求打到正在关闭的Pod3.3 Liveness探针只查自己卡没卡死绝不查下游Liveness失败会重启容器,所以它只应该回答一个问题:这个进程是不是卡死了、只有重启才能救。livenessProbe:httpGet:path:/livezport:8080periodSeconds:10failureThreshold:3timeoutSeconds:3设计要点(这是全文最重要的部分):liveness端点应该只检查进程自身,比如能否响应、主事件循环有没有卡死绝不要在liveness里检查数据库、缓存、下游服务等外部依赖阈值要宽松,避免短暂GC停顿、瞬时负载导致误杀很多应用其实不需要liveness探针,如果进程崩溃会自己退出,靠重启策略就够了,liveness只对进程还在但卡死有意义3.4 为什么liveness不能查下游这是探针设计里最容易埋雷的地方。假设你在liveness里检查数据库连通性:数据库抖动几秒 → 所有连这个库的应用liveness探针同时失败 → kubelet同时重启所有这些容器 → 大批容器重启,连接风暴,数据库压力更大 → 数据库更不稳定,探针继续失败 → 雪崩数据库只是抖了几秒,本来应用重连就能恢复,却因为liveness误判被集体重启,反而放大成大面积故障。依赖检查属于readiness(摘流量)的范畴,绝不属于liveness(重启)。四、三类探针的配合一个慢启动、需要自愈、有下游依赖的应用合理的组合是startupProbe:httpGet:path:/healthzport:8080periodSeconds:10failureThreshold:30# 覆盖最坏启动时间readinessProbe:httpGet:path:/readyport:8080periodSeconds:10failureThreshold:3# 依赖不可用时摘流量livenessProbe:httpGet:path:/livezport:8080periodSeconds:10failureThreshold:3timeoutSeconds:3# 只查自身阈值宽松配合逻辑启动阶段:startup保护,liveness和readiness不执行startup成功后:readiness根据能否服务控制流量,liveness只在进程卡死时重启三个端点应该分开:/healthz(启动)、/ready(就绪含依赖)、/livez(仅自身),不要共用一个大而全的健康检查端点这里体现了第01篇讲过的原则:Pod为Running不代表健康。探针就是把运行和就绪存活区分开的机制。五、C级生产化重建案例一次数据库抖动引发集体重启5.1 先说明哪些是真的哪些是重建的下面不是作者声称亲历的生产事故而是根据Kubernetes探针机制构造的生产化重建用于展示探针误配如何放大故障。内容证据属性liveness失败重启、readiness摘流量的机制Kubernetes官方机制liveness检查数据库导致下游抖动时集体重启机制一致的重建场景Namespace、资源名、相对时间和终端输出为讲解构造的说明性信息liveness探针端点检查了数据库连通性模拟根因不是作者生产记录把依赖检查移到readiness后验证不再集体重启受控验证设计不声称已在当前集群执行所有输出按真实对象关系编排但没有连接实际集群采集读者不能把下面的时间或输出引用为真实事故数据。5.2 现场卡片数据库有一次几秒的抖动之后大批应用Pod几乎同时重启服务出现一段明显不可用。团队最初怀疑是应用集体崩溃。重建的相对时间线相对时间观察或动作当时能够得出的结论T00数据库短暂抖动下游有波动T01大批应用Pod同时重启RESTARTS跳增存在集体重启原因未知T03怀疑应用崩溃待验证假设T05describe看到Liveness probe failed事件转向探针T07liveness端点被发现检查了数据库得到可验证的主假设T验证把依赖检查从liveness移到readiness后观察单变量验证相对时间只表示排查顺序不代表真实数值。5.3 确认是探针触发的重启NSexample-prodPODorder-api-xxxx kubectl describe pod$POD-n$NS|sed-n/Events:/,$p机制一致的说明性Events节选Warning Unhealthy Liveness probe failed: HTTP probe failed with statuscode: 503 Normal Killing Container order-api failed liveness probe, will be restarted大量Pod同时出现Liveness probe failed加Killing说明是liveness探针触发的重启不是应用自己崩溃。503说明应用端点主动返回了不健康。5.4 查liveness端点检查了什么kubectl get pod$POD-n$NS\-ojsonpath{range .spec.containers[*]}{.name}{ liveness}{.livenessProbe.httpGet.path}{ readiness}{.readinessProbe.httpGet.path}{\n}{end}机制一致的说明性输出order-api liveness/health readiness/health发现liveness和readiness用了同一个/health端点。再看应用这个端点的实现从代码或配置/health 端点逻辑检查进程 检查数据库连通性 检查缓存关键发现这个端点把数据库连通性也算进健康,而它同时被liveness使用。证据链大批Pod同时Liveness probe failed并重启 时间与数据库抖动吻合 liveness和readiness共用/health端点 /health检查了数据库连通性 数据库抖动时该端点对所有Pod同时返回503 强烈支持liveness检查下游导致下游抖动被放大成集体重启这仍是主假设验证前不写成根因已闭环。5.5 止损与单变量验证单变量修正把依赖检查从liveness剥离liveness只查进程自身依赖检查交给readiness。不同时改副本、资源和其他配置livenessProbe:httpGet:path:/livez# 只检查进程自身不查数据库port:8080periodSeconds:10failureThreshold:3timeoutSeconds:3readinessProbe:httpGet:path:/ready# 检查含数据库等依赖失败只摘流量port:8080periodSeconds:10failureThreshold:3应用侧相应拆分端点/livez只反映进程存活/ready才检查依赖。验证方式在下游可控地制造一次短暂抖动在非生产或受控环境观察kubectl get pods-n$NS-lapporder-api\-ocustom-columnsNAME:.metadata.name,READY:.status.containerStatuses[*].ready,RESTARTS:.status.containerStatuses[*].restartCount机制一致的说明性预期输出NAME READY RESTARTS order-api-aaaa false 2 抖动期间被摘流量未新增重启 order-api-bbbb false 2 order-api-cccc true 2 抖动恢复后自动加回预期下游抖动时Pod因readiness失败被摘流量但RESTARTS不再增长抖动恢复后readiness成功流量自动加回。不再有集体重启。这些输出分别证明不同范围的事实输出能够支持不能单独证明RESTARTS不再增长liveness不再因下游误杀应用本身永不崩溃抖动期READY为falsereadiness正确摘流量用户完全无感抖动后自动恢复READY依赖恢复后自愈所有依赖都这样处理5.6 根因闭环条件修正只有依赖检查从liveness移到readiness这一项主要变量。修正后下游抖动只导致摘流量不再集体重启。时间线上重启与探针失败、下游抖动的关联被打断。应用自身卡死时liveness仍能重启自愈能力保留。观察窗口内再次抖动不再引发雪崩。如果拆分后仍集体重启就要停止把探针当作唯一原因检查是否还有其他共享的依赖检查或资源问题。六、探针设计要分层考虑层次设计动作关键边界职责划分startup保护启动、readiness控流量、liveness触重启三者端点分开不共用大而全端点依赖处理依赖检查只放readinessliveness绝不查下游liveness查下游会放大故障阈值设定startup覆盖最坏启动liveness阈值宽松防误杀阈值太严会误杀太松会延迟发现慢启动保护用startup而非liveness的长initialDelaystartup更精准优雅关闭关闭时readiness先失败摘流量再终止配合preStop和终止宽限期监控预防监控探针失败率、集体重启和摘流量比例探针失败突增要告警一个反直觉的原则很多应用其实不需要liveness探针。如果进程崩溃会自己退出重启策略就够了。liveness只对进程还活着但卡死有意义而这种情况要谨慎判断宁可不配也不要配一个会查下游的liveness。七、可直接使用的探针配置检查清单发布前对每个工作负载的探针逐项检查职责划分 [ ] startup、readiness、liveness端点是否分开 [ ] 有没有把一个大而全的健康端点同时用于liveness和readiness 依赖处理 [ ] liveness端点是否只检查进程自身 [ ] liveness是否检查了数据库、缓存、下游服务必须否 [ ] 依赖检查是否只放在readiness 慢启动 [ ] 慢启动应用是否配了startup探针 [ ] startup的failureThreshold乘periodSeconds是否覆盖最坏启动时间 阈值 [ ] liveness的failureThreshold和timeoutSeconds是否够宽松防止抖动误杀 [ ] readiness阈值是否合理能及时摘流量又不频繁抖动 优雅关闭 [ ] 关闭时readiness是否先失败摘流量 [ ] 是否配合preStop和terminationGracePeriodSeconds 必要性 [ ] 这个应用是否真的需要liveness探针 [ ] 进程崩溃能自己退出的是否可以只靠重启策略这个清单可以直接放进发布评审或CI校验。八、监控与治理8.1 监控什么各工作负载的liveness、readiness探针失败率短时间内的集体重启重启数突增readiness摘流量的Pod比例探针失败与下游抖动的时间关联startup探针超时导致的启动失败告警要能识别集体重启这种模式短时间内大量Pod同时重启往往是探针误配或共享依赖问题比单个重启更危险。8.2 发布前校验探针端点职责分开liveness不查下游慢启动应用配startup阈值经过评审不过严不过松用探针配置检查清单过一遍8.3 治理把探针配置检查纳入模板和CI提供标准的探针端点约定/healthz、/ready、/livez关键服务的探针变更走评审定期复盘集体重启事件回补探针设计九、常见误区误区1三种探针用同一个端点职责不同共用端点会让依赖检查混进liveness。误区2liveness检查数据库或下游下游抖动会被放大成集体重启雪崩这是最危险的探针错误。误区3用liveness的长initialDelay等慢启动应该用startup探针更精准且不影响运行期。误区4readiness和liveness混用readiness失败只摘流量liveness失败会重启后果完全不同。误区5liveness阈值太严短暂GC或负载抖动会误杀阈值要宽松。误区6给所有应用都配liveness进程崩溃自己会退出的未必需要liveness配错反而有害。误区7优雅关闭不配合readiness关闭时不先摘流量请求会打到正在关闭的Pod。误区8探针配完不监控集体重启集体重启是探针误配的典型信号要专门告警。十、面试怎么说60秒版本三种探针职责完全不同startup保护慢启动成功前另两个不执行readiness判断能否接收流量失败只摘流量不重启liveness判断是否需要重启失败会重启容器。最重要的原则是liveness绝不能检查数据库、下游这些外部依赖否则一次下游抖动会让所有相关容器同时被误杀重启放大成雪崩。依赖检查只能放readiness最坏是摘流量。三个端点要分开慢启动用startup而不是liveness的长延迟。很多应用其实不需要liveness进程崩溃能自己退出的靠重启策略就够。3分钟场景版本假设数据库抖了几秒之后大批Pod几乎同时重启服务一段时间不可用。我先describe看到大量Liveness probe failed和Killing事件说明是liveness触发的重启不是应用崩溃。再看探针配置liveness和readiness共用了一个/health端点而这个端点检查了数据库连通性。根因就清楚了数据库抖动时这个端点对所有Pod同时返回不健康liveness把它当成需要重启于是集体重启连接风暴又让数据库更不稳定形成雪崩。修复是把依赖检查从liveness剥离liveness只查进程自身用/livez依赖检查放到readiness用/ready这样下游抖动最多让Pod被摘流量、恢复后自动加回不再重启。我只改这一个变量验证再制造一次受控抖动确认不再集体重启。这体现了探针设计的核心能摘流量解决的绝不用重启解决。十一、延伸问答1. 三种探针最本质的区别startup管启动阶段的保护readiness失败摘流量liveness失败重启后果一个比一个重。2. 为什么liveness不能查下游下游抖动会让所有相关容器的liveness同时失败被集体重启放大成雪崩而下游本来重连就能恢复。3. startup和liveness的initialDelay有什么区别startup是专门的启动保护成功后才交接给liveness比固定的长initialDelay更精准。4. readiness检查依赖合理吗合理。依赖不可用时摘流量是自我保护最坏只是没流量不会重启。5. 什么应用不需要liveness进程崩溃会自己退出的应用靠重启策略就够liveness只对进程活着但卡死有意义。6. 探针阈值怎么定startup覆盖最坏启动时间liveness宽松防误杀readiness平衡及时摘流量和不频繁抖动。7. 优雅关闭和探针怎么配合关闭时readiness先失败摘流量再配合preStop和终止宽限期避免请求打到关闭中的Pod。8. 怎么发现探针配错了监控集体重启和探针失败率短时间大量Pod同时重启往往是探针误配或共享依赖问题。小结startup、readiness、liveness职责完全不同不能混用或共用端点。readiness失败只摘流量liveness失败会重启后果差别很大。liveness绝不检查数据库、下游等外部依赖否则会放大成集体重启雪崩。依赖检查只放readiness最坏是摘流量。慢启动用startup探针保护比liveness的长initialDelay精准。liveness阈值要宽松避免抖动误杀。很多应用其实不需要liveness进程崩溃靠重启策略即可。监控要能识别集体重启这种探针误配的典型信号。下一篇预告下一篇进入Requests、Limits与QoS。我们会讲清资源请求和限制如何影响调度、超卖、驱逐和CPU限流区分三种QoS等级并整理一份资源配置方法。参考资料Kubernetes官方文档Liveness, Readiness and Startup ProbesKubernetes官方文档Configure Liveness, Readiness and Startup ProbesKubernetes官方文档Pod LifecycleKubernetes官方文档Pod ConditionsKubernetes官方文档EndpointSlicesKubernetes官方文档Attach Handlers to Container Lifecycle EventsKubernetes官方文档Pods and Endpoints Termination FlowKubernetes官方文档Container Lifecycle HooksKubernetes官方文档Configure Pod InitializationKubernetes官方文档Sidecar Containers