1. 滚动更新为什么会在“一切正常”时翻车
做Kubernetes平台运维这几年,让我感触最深的一个主题就是Pod健康探测。我刚开始负责这块时,遇到过一次印象很深的故障:一个核心服务发版本,滚动更新流程跑得很顺,新Pod一个个起来,旧的Pod一个个退出,kubectl get pods看起来一切正常,没有CrashLoopBackOff,没有ImagePullBackOff。但发布完不到两分钟,监控告警就炸了——接口错误率直线上升,大量5xx。
1.1 一次没有探针的发布事故
排查之后发现,新Pod虽然已经显示Running,容器内的Java进程也确实起来了,但Spring Boot应用还在做数据库连接池初始化、Redis缓存预热、服务注册发现,至少需要60秒才能真正处理请求。而Service的Endpoints在Pod进入Running的那一刻,就已经把Pod IP加进去了,流量随即打进来,全部撞在“还没准备好”的应用上。
这个故障的核心,就是Pod健康探测没配置到位。准确地说,是readinessProbe缺失,导致Endpoints过早纳入了新Pod。
很多人理解Pod健康探测,觉得就是“探活”,容器活着就万事大吉。但实际上,健康探测在Kubernetes的滚动更新链路里扮演的角色,远不止“存活检查”这么简单。它决定了流量什么时候进来、旧Pod什么时候退出、发布过程中有没有请求失败或者超时。可以说,探针配置的质量,直接决定了滚动更新是“零故障丝滑升级”还是“发布即事故”。
1.2 Pod Running不等于应用可用:Endpoints的纳入机制
这里先普及一个底层机制:Pod从创建到进入Running,kubelet会启动容器,容器内进程起来以后,Pod状态就是Running。但Pod状态变成Running,不代表应用可用。Endpoints控制器会监听Pod状态,一旦Pod进入Running且所属Service的selector匹配,就会把Pod IP写进Endpoints。从这一刻起,流量就会打到这个Pod上。
说白了,Kubernetes只看“容器进程活着没”,并不知道“你的应用能不能处理请求”。这个信息差,就是各种升级事故的温床。而Pod健康探测,就是用来弥补这个信息差的机制。你通过探针告诉kubelet:“容器进程活着只是第一步,还要满足哪些条件才算真正可用”。有了这套机制,Kubernetes才能在升级过程中做出正确的流量调度决策。
1.3 健康探测在整个升级链路中的角色
把视角拉到整个滚动升级的链路来看,探针其实卡在三个关键节点上:
- 新Pod启动后,靠startupProbe确认“启动完成”,避免在慢启动阶段就被误杀;
- 新Pod就绪后,靠readinessProbe决定“是否把流量切进来”;
- 运行过程中,靠livenessProbe判断“进程是否还值得存活”,失败则重启。
这三个节点任何一个配置失误,都可能让滚动更新从“零故障”变成“事故现场”。接下来我按三个层面展开:三种探针的职责边界、探针参数如何影响升级、滚动更新策略与探针的配合方式,最后用一个真实排查案例收尾。
2. 三种探针的分工:liveness管生死,readiness管流量,startup管启动
2.1 livenessProbe:容器活着的证据,但不是可用的证据
livenessProbe解决的是“容器假死”的问题。什么情况会用得上?最常见的是死锁、内存溢出导致进程无响应,或者某个线程把CPU占满了,进程还挂着但已经不干活了。这时候livenessProbe发现探活失败,kubelet会主动杀掉容器,重建一个新的。
关键点:liveness失败的动作是重启容器,而不是从负载均衡摘除。如果在流量高峰时,一个Pod因为瞬时压力变大导致接口响应慢、探活失败,kubelet直接重启容器,Pod的IP从Endpoints里消失,正在处理的连接全部中断,这是很危险的。所以liveness的失败阈值要适当放宽,不要用太敏感的检查。
我见过不少团队把liveness配成了一个“全能检查”,里面既查数据库连通性又查下游依赖,结果数据库一抖动,所有Pod一起重启,整个服务雪崩。liveness应该只检查“进程还活着、基本健康”,最好和外部依赖解耦。外部依赖出问题应该由readiness来摘流量,而不是靠liveness重启——重启也解决不了数据库抖动的问题,只会让情况更糟。
2.2 readinessProbe:决定流量是否进入的关键闸门
readinessProbe是滚动更新中影响最大的探针。它的失败动作是把Pod从Service的Endpoints中摘除。也就是说,readiness没通过,流量不会进来;通过了,流量才进来。
为什么它这么关键?回到滚动更新的场景。Deployment滚动更新时,新Pod创建后,只有readinessProbe通过,Endpoints切片才会把新Pod加入负载池。readiness没通过的新Pod,不会接收流量。而在maxUnavailable=0的策略下,控制器会等到新Pod ready之后,才会终止旧Pod。所以“新Pod什么时候算好”这件事,全由readinessProbe说了算。
很多人以为滚动更新时流量切换是靠Deployment自身的逻辑,其实不是。真正决定“接下来流量往哪里打”的,就是readinessProbe的通过与否。没有readinessProbe,Pod一Running就进Endpoints,流量就进来,前面说的那个502事故就是这样发生的。
readiness还承担着运行期优雅摘流量的职责。比如应用连接池满了、依赖的Redis挂了、缓存还没准备好,readiness应该能感知到这些状态并主动“脱岗”,让流量绕开自己。等恢复了再通过,重新接入流量。这就相当于给每个Pod装了一个可编程的“流量阀门”。
2.3 startupProbe:慢启动应用的保护伞
startupProbe是Kubernetes 1.16引入的,解决的正是慢启动应用的误杀问题。很多Java系应用、大数据组件,启动要几十秒甚至几分钟。如果只配livenessProbe,容器启动中探活失败,kubelet会把容器杀了重启,陷入CrashLoopBackOff死循环。
startupProbe的作用是:在它成功之前,liveness和readiness都不生效。相当于给了应用一个“宽限期”,在这个期间只检查startupProbe,成功了才开始跑liveness/readiness。
配置逻辑很简单,startupProbe的periodSeconds * failureThreshold应该大于应用最坏的启动时间。实际做法上,periodSeconds设成2~5秒,failureThreshold设成30甚至60。比如periodSeconds=2、failureThreshold=60,就能容忍120秒的启动时间,非常充裕。如果你的应用启动时间不稳定,宁可把阈值调大一点,也不要让它在启动阶段被打断。
2.4 三种探测方式的选型:exec、httpGet、tcpSocket怎么选
每种探针都可以用三种方式实现,选择逻辑其实很直接:
| 探测方式 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| httpGet | 请求HTTP端点,2xx/3xx视为成功 | 最能反映应用真实状态 | 需要应用提供health端点 | HTTP/gRPC服务 |
| tcpSocket | 尝试建立TCP连接 | 通用、不依赖应用实现 | 端口通不代表应用就绪 | 非HTTP协议、TCP服务 |
| exec | 容器内执行命令,退出码0为成功 | 灵活、可自定义检查逻辑 | 每次执行有额外资源开销 | 无网络端口的worker、脚本进程 |
httpGet用得最多,但要注意:health端点应该是轻量的,不要在里面查数据库、调第三方接口,否则会产生连带效应。tcpSocket太粗糙,只能做一个“兜底”级别的探测。exec每次探测会fork一个进程,如果periodSeconds太短或者命令很重,会浪费不少资源。
我曾经维护过一组由shell脚本实现的worker,用的是exec探针,脚本里写了个复杂的健康检查逻辑,包含对文件锁的检查。结果periodSeconds设了5秒太频繁,脚本本身的资源消耗和锁竞争把worker拖慢了。后来把检查脚本简化,periodSeconds调到10,问题解决。exec探针本身的开销不容忽视。
3. 探针参数不是拍脑袋填的:初始延迟、周期、阈值的配置逻辑
3.1 五个核心参数的完整含义
探针配置里有五个参数,很多人只知道大概意思,但配的时候还是拍脑袋。先说清楚:
| 参数 | 默认值 | 含义 | 判断逻辑 |
|---|---|---|---|
| initialDelaySeconds | 0 | 容器启动后等多久才开始探测 | 给应用预留启动时间 |
| periodSeconds | 10 | 每隔多少秒探测一次 | 频率太短浪费资源,太长反应慢 |
| timeoutSeconds | 1 | 一次探测最多等多久 | 超时即本次失败 |
| successThreshold | 1 | 连续成功几次才判为成功 | readiness恢复接流量时起作用;liveness固定为1 |
| failureThreshold | 3 | 连续失败几次才判为失败 | 越短越敏感,越长越耐错 |
这里面容易忽略的是successThreshold。readinessProbe从失败状态恢复时,需要连续多次成功才会把Pod重新加回Endpoints。如果你把successThreshold设成1,那么一次成功探测后Pod就恢复接流量;设成3,需要连续3次成功,相当于多了一个缓冲,避免“刚恢复又失败”的抖动。不过也不要设太大,否则恢复时间会被拉长,流量长时间绕开Pod,其他Pod压力会变大。
3.2 结合启动时间和依赖初始化速度做参数推演
参数设计的核心是:先搞清楚应用的启动曲线,再倒推参数。
以一个典型的Java服务为例:
- 进程启动到端口监听:大约10秒
- 数据库连接池初始化:大约20秒
- Redis缓存预热:大约30秒
- 完全可服务:大约35秒
如果我们直接在readinessProbe里配initialDelaySeconds=0,periodSeconds=10,failureThreshold=3,那么第一次探测在启动后0秒就执行,应用端口还没监听,探测失败。第二次10秒,可能端口起来了但还没ready,失败。第三次20秒,仍可能失败。连续3次失败之后,Pod就被摘除了——但这其实只是个误会,应用35秒后就好了。而且一旦readiness失败,后续的探测还在继续,它会在应用ready后的下一次probe通过时恢复。问题在于中间这段“判定为失败”的时间窗口如果和其他操作叠加,就会出问题。
所以比较稳的做法是:
readinessProbe: httpGet: path: /health/ready port: 8080 initialDelaySeconds: 25 # 等应用基本启动完再开始探测 periodSeconds: 5 # 每5秒探测一次 timeoutSeconds: 3 successThreshold: 1 failureThreshold: 3 # 连续3次失败才摘流量initialDelaySeconds设25秒,意味着前25秒不探测,Pod即使没就绪也不会被摘除,因为压根还没开始检查。注意:在没有readinessProbe时,Pod进Endpoints的时机是Pod Running;配了readiness之后,Pod只有在readiness成功后才进Endpoints。所以initialDelay期间虽然不探测,但Pod也不会接流量,是安全的。
对于启动特别慢的应用,再加一个startupProbe:
startupProbe: httpGet: path: /health/start port: 8080 periodSeconds: 2 failureThreshold: 60 # 最长容忍120秒启动startupProbe通过之前,liveness和readiness都不会执行。所以即使liveness的initialDelaySeconds很短,也不会误杀启动中的应用。
3.3 判定时间窗口的近似计算
这里给一个可以套用的计算公式,方便你在评审别人配置时快速判断有没有问题。
判断Pod从不健康到被摘除的最短时间(即最坏情况下需要多久才开始摘流量):
挂起时间 ≈ initialDelaySeconds + (failureThreshold - 1) * periodSeconds + timeoutSeconds举个例子,initialDelaySeconds=30,failureThreshold=3,periodSeconds=10,timeoutSeconds=3,那么在超时最坏情况下:
- 第30秒开始第一次探测,第33秒超时,失败1次
- 第40秒开始第二次探测,第43秒超时,失败2次
- 第50秒开始第三次探测,第53秒超时,失败3次
也就是说Pod从启动到被判定为失败并摘除,大约需要53秒。反过来,如果应用实际上在第45秒就ready了,那它会在第50秒的探测中通过,重新加入Endpoints,损失的时间只有几秒。
如果你追求快速摘除故障Pod,可以把periodSeconds调小、failureThreshold调小。但要注意权衡:太激进会把瞬时抖动误判成故障。这个平衡没有标准答案,取决于你的应用对延迟的容忍度。我的经验是:对于关键链路上的服务,readiness的failureThreshold至少3,periodSeconds控制在5~10秒,既能较快摘除故障Pod,又不会因为一次GC停顿或网络抖动就误摘。
4. 滚动更新策略与探针联动:maxSurge、maxUnavailable怎么配才能零故障
4.1 滚动更新的完整决策链路
Deployment滚动更新,Kubernetes做决策的顺序是这样的:
- Deployment控制器创建一个新的ReplicaSet,按照maxSurge的比例额外创建新Pod;
- 新Pod启动后,先经过startupProbe确认启动完成;
- readinessProbe通过后,Pod IP被加入Service的Endpoints;
- 控制器时刻检查可用Pod数量,保证可用Pod数不低于
replicas - maxUnavailable。在这个前提下,滚动替换旧Pod; - 终止旧Pod时,会先从Endpoints中摘除,再等待优雅终止期(terminationGracePeriodSeconds),最后发送SIGTERM。
这里面有两个容易被忽略的细节。
第一,当maxUnavailable=0时,控制器必须先等到至少一个新Pod ready,才会终止一个旧Pod。这就是“先扩容、后缩容”的逐个替换方式,能保证任何一个时刻可用Pod数量不下降。而默认的maxUnavailable=25%策略下,控制器允许提前终止少量旧Pod,只要总不可用数不超标。所以如果你没配readiness探针,新Pod秒进Endpoints,控制器也秒杀旧Pod,发布过程完全失控。
第二,如果新Pod一直不ready,比如镜像启动失败、探针配置错误,滚动更新就会卡住,不继续终止旧Pod。这其实是一个安全机制,避免“旧的还没好、新的又不行”。很多人在发布时发现Deployment卡住不动,kubectl rollout status一直等,就是因为新Pod的readiness没过。这种情况不要慌,先看新Pod的探针状态,而不是盲目取消发布。
4.2 maxSurge、maxUnavailable的选取逻辑
maxSurge和maxUnavailable共同决定了滚动的速度与风险。
- maxSurge:滚动更新过程中,允许比期望副本数多出来的Pod数量。比如
replicas=10,maxSurge=25%,那么更新过程中最多可以有13个Pod同时存在。 - maxUnavailable:滚动更新过程中,允许最多有多少比例的Pod不可用。默认25%。
零故障升级的前提是:在任何时刻,至少保留足够的可用Pod来支撑流量。也就是说,maxUnavailable不能太大,否则旧Pod已下线、新Pod还没就绪,可用Pod不足,流量就会受损。
举个例子,replicas=10,maxUnavailable=50%。滚动更新开始后,控制器可能会一次性把5个旧Pod杀掉,然后创建5个新Pod。如果新Pod的readiness比较慢,在它们ready之前,集群只有5个可用Pod,如果这5个Pod扛不住原有流量,就出事了。
更稳的配置是针对关键服务把maxUnavailable设为0:
strategy: type: RollingUpdate rollingUpdate: maxSurge: 25% maxUnavailable: 0maxUnavailable=0的意思是:在滚动更新期间,任何时刻都不允许减少可用Pod的数量。控制器必须等一个新Pod ready,才能终止一个旧Pod。对于不能接受容量下降的服务来说,这是最稳妥的策略。
代价也很明显:滚动速度慢。尤其是Pod数量大、readiness检查时间长的服务,整个发布可能要走很久。所以实际中要结合业务容忍度来选。如果服务有多副本且单副本挂了不影响可用性,maxUnavailable=25%也够用;但如果你只有2个副本的线上关键服务,我建议maxUnavailable直接设0,别赌。
4.3 maxSurge与实际流量压力的关系
maxSurge并不是越大越好。它决定了瞬时多出多少Pod来承接流量。如果maxSurge=100%,相当于先起一批完整的新Pod,全部ready后再开始缩旧的。这样对流量最安全,但资源消耗翻倍。
我见过不少团队为了“零故障”把maxSurge设成100%,但没考虑到集群资源是否足够。如果节点资源不足,新Pod一直Pending,滚动更新就卡在那儿。资源充足时,maxSurge大一点确实更稳,因为新Pod有充分的时间完成预热,再逐步替代旧Pod。
我的建议是:
- 资源宽松、追求速度:maxSurge=50%,maxUnavailable=25%
- 资源较紧、追求稳定:maxSurge=25%,maxUnavailable=0
- 只有2~3个副本的关键服务:maxSurge=100%(全部先建好),maxUnavailable=0
4.4 readinessGates:把自定义条件塞进滚动更新决策
readinessProbe是内置的检查维度,但有些场景需要额外的“外部就绪条件”。比如:我这个Pod启动后,必须等注册中心确认注册成功、必须等配置中心拉取到最新配置、必须等某个Agent上报心跳,才算真正ready。这些外部条件无法用httpGet/tcpSocket/exec直接表达。
Kubernetes提供了readinessGates机制:可以在Pod上声明一个或多个Custom Condition,只有当所有condition都为True时,Pod才算ready。这个机制在Service Mesh场景里很常见,比如Istio就利用readinessGates来确保Pod在Envoy代理注入并Ready之前,不会被标记为就绪。
spec: readinessGates: - conditionType: "example.com/registered"外部控制器(比如自定义Operator)负责在Pod满足条件后,更新Pod的status.conditions里该condition为True。否则,即使readinessProbe通过了,Pod也不会进入ready状态。
这个机制对零故障升级的意义在于:它把“应用层就绪”和“平台层就绪”分离了。如果你的应用依赖外部系统同步,建议用readinessGates补充,避免出现“应用本身探测通过了,但外部依赖还没就绪”的窗口期。
5. 一次真实的探针误判排查:从502告警到根因定位的全过程
5.1 故障现象与初步怀疑
某一个SaaS服务,版本升级后发现线上偶发502。监控面板显示的Pod状态全部正常,readinessProbe配置也正确,滚动更新也没卡住,看起来一切都好。但错误率确实在发布后的半小时内间歇性升高。
我的第一反应是查最近变更。除了发版,有没有改过网络策略、有没有动过Service?因为502是网关侧报出来的,通常意味着后端Pod不可用或连接被重置。查了一圈,没发现其他变更,于是把目光聚焦到探针上。
5.2 排查链路:从Endpoint到探针日志
排查链路的第一步是看Service的Endpoints。用kubectl get endpoints检查,发现Pod的IP确实都在Endpoints里。这就排除了“Pod被摘除”的原因。如果是readiness失败导致Pod被摘除,Endpoints里会少IP,请求就会失败。既然IP都在,问题不在摘除环节。
第二步,看Pod事件。kubectl describe pod看最近的事件,发现有个别Pod在短时间内被重启过,重启原因显示是Liveness probe failed。这就奇怪了,因为监控面板显示Pod状态正常。原因在于:Pod重启之后状态恢复Running,监控就显示正常了,但期间有一小段不可用窗口,正好赶上了流量高峰,造成了502。
第三步,看探针日志和之前的监控曲线。发现探针失败其实是尾部的偶发现象,并不是持续的。再深入看,应用在做Full GC时有明显的停顿,探针请求的响应时间飚到几秒,超过了timeoutSeconds。
5.3 根因:liveness与readiness共用了同一个检查点
根因在于:我们当时把livenessProbe和readinessProbe配置成了完全相同的检查,都指向/health/live这个端点。而这个端点虽然是专门为探针服务的,但实现里带了一个轻量的数据库状态查询(判断是否需要降级),结果数据库在主库切换时短暂不可用,/health/live返回5xx。liveness连续失败3次后,kubelet直接重启了容器。
这里有两个叠加的问题。
第一,liveness检查不应该依赖外部组件。数据库状态查询本质上是检查依赖,不是检查“进程活着”。依赖抖动时,正确的动作是让readiness把Pod摘出去,让流量绕开它,等依赖恢复后再接回来。而liveness的职责是判断“进程是否值得继续存活”,它失败就该重启,但这不适合应对依赖抖动——重启也解决不了数据库主从切换的问题。
第二,liveness和readiness完全共用同一个端点,导致“临时不可用”和“进程死亡”被混为一谈。这是一个非常常见的错误设计。你想想,这两个探针的失败动作完全不同:readiness失败只是摘流量,liveness失败可是会重启容器的。把同一个检查点同时交给两个动作完全不同的机制,等于让一个开关同时控制电灯和消防喷淋,必然出问题。
5.4 修复与验证
修复方案分两步。
把readinessProbe和livenessProbe拆开。readinessProbe检查/health/ready,这个端点包含依赖状态判断(比如数据库连通性、连接池使用率);livenessProbe只检查/health/live,这个端点只负责确认进程活着,不碰任何外部依赖。
同时把liveness的failureThreshold从3提高到5,timeoutSeconds从1提高到3,避免因为GC停顿或瞬时负载导致误杀。
livenessProbe: httpGet: path: /health/live port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 5 readinessProbe: httpGet: path: /health/ready port: 8080 initialDelaySeconds: 25 periodSeconds: 5 timeoutSeconds: 3 successThreshold: 1 failureThreshold: 3线上验证:重新发布后观察了三天,502彻底消失。即使数据库再发生主从切换,Pod也只会在readiness层面被摘除,请求会自动绕开,切换完成后再重新加入Endpoints,全程无感。
5.5 这个坑的另一个变体:探针端点本身太重
顺带说一个相关案例。有团队把readinessProbe的HTTP端点写得很重,里面扫数据库、统计全量缓存、调用下游健康检查接口,导致每次探针请求都要几百毫秒甚至几秒。由于探针是周期性高频发起的,这些请求本身就成了应用的一个额外负载源。更糟的是,如果探针端点在处理中发生死锁,探针超时失败,Pod被摘除,请求全打到其他Pod上,其他Pod也可能因为过载而探针失败,形成雪崩。
探针端点就应该“轻、快、无副作用”。readiness端点只做三件事:确认进程活着、确认关键依赖可用、确认自身状态(比如连接池水位)在可接受范围。超过一个阈值或依赖不可用就返回非2xx,其他情况一律快速返回200。不要在探针端点里做任何需要长时间等待的操作,也不要在里面写日志、洗数据。
6. 零故障升级的探针设计检查清单
结合前面所有踩过的坑,我整理了一份可以直接拿去评审用的检查清单。每次排障或发版前,拿它过一遍,能挡掉大部分升级事故。
6.1 探针配置自查清单
- [ ] 是否配置了readinessProbe?如果没有,Pod Running即自动进Endpoints,等同于裸奔。
- [ ] livenessProbe和readinessProbe是否使用了同一个端点?如果共用,建议拆开,区分“临时不可用”和“进程死亡”。
- [ ] livenessProbe是否依赖了外部组件(数据库、下游服务、注册中心)?如果依赖,建议去掉或单独检查,避免依赖抖动引发全量重启。
- [ ] initialDelaySeconds是否大于应用监听端口所需的最短时间?如果应用启动需要30秒,别把initialDelay设成0。
- [ ] 启动特别慢的应用(超过30秒)是否配置了startupProbe?如果没有,liveness可能在启动阶段就误杀容器。
- [ ] timeoutSeconds是否足够?探针端点在GC停顿或高负载下的响应时间,必须明显小于timeoutSeconds。
- [ ] failureThreshold是否合理?关键服务建议不低于3,避免瞬时抖动导致误判。
- [ ] 探针端点是否轻量?不要在探针端点内查数据库、扫描索引、调用远程接口。
- [ ] 探针端点在依赖不可用时,返回码是否正确?应该返回非2xx而不是一直卡住超时。
6.2 滚动更新策略自查清单
- [ ] maxUnavailable是否可能让可用Pod数低于安全水位?只有2~3个副本的关键服务,建议设为0。
- [ ] maxSurge是否超出了集群可用资源?超出会导致新Pod Pending,滚动更新卡死。
- [ ] 是否配置了terminationGracePeriodSeconds?优雅终止期要覆盖应用接到SIGTERM后的“摘流量+清理资源+退出”时间,常见默认30秒不一定够。
- [ ] 是否验证过滚动更新在“新Pod永远不ready”时的表现?预期应该卡住而不是继续发布。
- [ ] 如果平台支持,是否对发布过程做了灰度分组?建议先将新版本发布到低流量节点,再逐步扩大。
6.3 几个通用经验
第一,探针是“防御性”设计,不显眼但决定了事故概率。我见过太多团队把精力花在CI/CD流程上,却忽略了把readiness配好,结果发布管道再顺,最后一公里在Kubernetes这边翻了车。
第二,health endpoint应该和业务接口分离。业务接口里如果有降级逻辑、复杂处理,不适合作为探针端点。探针端点最好是一个独立的轻量接口,只传健康状态。
第三,探针的“噪音”不能忽略。如果集群规模大,Pod数量多,探针请求本身就是不小的流量。比如1000个Pod,每5秒一次readiness探测,每秒就有200次请求打到业务进程里。建议periodSeconds不要设太短,常见的5~10秒对于绝大多数应用足够。
第四,无论配置看起来多合理,都要通过实战验证。最有效的验证方式就是主动制造故障:手动把某个Pod的readiness端点改为返回500,观察Endpoints是否及时摘除;或者把某个Pod停掉,观察滚动更新是否卡住。混沌工程里最简单的两个实验,Kubernetes场景下就是这两个。
最后分享一个我一直在用的技巧:把探针的日志和事件接入告警。kubectl describe pod里的事件,平时没人看,但一旦探针开始失败,事件里会有Liveness probe failed或者Readiness probe failed的连续记录。把这些事件接入监控,设置聚合告警,很多问题能在用户感知之前就被你发现。这也是我做“零故障升级”专项以来,觉得投入产出比最高的一项改进。