你有没有想过,深夜值班的护士是怎么确认病房里每位病人都安好的?她不会守在床前盯着每一个人,而是每隔一段时间轻轻推开门,看一眼呼吸、听一下心跳,必要时再测个血压。如果病人安然入睡,她就悄悄关上门;如果发现异常,她立刻按铃呼叫医生。这套“定时巡房 + 异常上报”的机制,就是医院里保障病人安全的基本功。 微服务架构里的服务,就像是病房里的病人。一个大型系统动辄几十上百个服务实例,某个服务挂了、卡住了、或者“装死”(进程还在但已无法正常处理请求),如果没有一套巡检机制,用户就会在毫无征兆的情况下遇到报错页面。健康检查与探活 (Health Check & Probe) ,就是微服务世界的“护士巡房”——定期戳一下每个服务,确认它“活着”且“能干活”,一旦发现异常,立即通知编排系统处理,把故障的影响范围降到最小。 这篇文章会带你从零开始,建立这套探活体系的完整认知:为什么要做健康检查、主动探活和被动健康检查有什么区别、Kubernetes 里 liveness / readiness / startup 三种探针怎么配、以及如何让服务故障后优雅地恢复。学完你会明白,高可用不是说出来的,是“查”出来的。

岗位关联

掌握健康检查与探活,是云原生时代后端工程师的必备基本功。它让你从“被动背锅”变成“主动排雷”,让系统故障从“用户发现”变为“系统自愈”——这是走向高级工程师与 SRE 的关键一步。

目录
  1. 可视化演示
  2. 章节一:探活必要性——病人那么多,护士不能靠运气
  3. 章节二:主动探活机制——护士定时巡房,敲敲门问声好
  4. 章节三:被动健康检查——病人按铃,监护仪异常报警
  5. 章节四:K8s三种探针——liveness、readiness 与 startup
  6. 章节五:优雅恢复策略——从救回来到重新接客
  7. 章节六:高可用实践——探活的调教与演练
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示存活探针自动重启故障容器、就绪探针流量路由摘除、HTTP/TCP/Exec三种探针类型对比,步进切换探索探活核心机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

存活探针:自动重启 探针定期GET /healthz检查 健康返回200无需干预 失败触发kubelet重启容器 liveness → restart on fail 存活探针检测假死自动重启恢复服务

章节一:探活必要性——病人那么多,护士不能靠运气

想象一家大型医院有上百间病房,每间病房里住着一位病情各异的病人。如果护士只凭“感觉”来判断病人是否安好,或者等病人自己按铃才去查看,那么深夜突发状况的病人很可能错过最佳救治时间。更糟的是,有些病人看起来在睡觉(进程还在),实际上已经呼吸衰竭(服务不可用)——这就是“装死”的故障。

微服务架构与之惊人相似。一个中等规模的系统可能有几十上百个服务实例,散布在多台机器上。某个服务进程虽然还活着,但可能因为死锁、内存溢出、依赖的数据库超时而无法正常处理请求。用户点一下按钮,请求就像接力棒一样在服务间传递,任何一环出了问题,前端就会出现超时或 500 错误。

健康检查与探活的核心价值,就是把“被动等故障暴露”变成“主动发现隐患”。它是高可用的第一道防线:监控报警能让你知道系统出问题了,而健康检查能让你在用户感知之前就发现并修复问题。Kubernetes 这类编排系统更是把健康检查作为内置能力,配合自动重启、流量摘除等机制,实现真正的“系统自愈”。

那怎么“查”呢?两条路线:一条是护士主动去病房敲门问“你还好吗”(主动探活),另一条是病人自己身上带着监护仪,异常时自动报警(被动健康检查)。下一节先从主动探活说起。

章节二:主动探活机制——护士定时巡房,敲敲门问声好

主动探活(Active Probing)是最直观的健康检查方式,就像护士每隔固定时间挨个病房敲门:屋里有人应一声,说明病人还清醒;没人应答,立刻推门查看。在微服务世界里,这个“敲门”的动作就是由一个调度器(如 Kubernetes 的 kubelet)定期向服务的健康端点发送 HTTP 请求。

具体实现上,服务需要暴露一个轻量级的健康检查端点,比如 /healthz 或 /readyz。这个端点不应该有复杂的业务逻辑,它的职责就是快速回答一个问题:“我还能不能正常工作?”它通常检查进程是否存活、关键依赖(数据库、缓存)是否可达、内部队列是否积压等。如果一切正常,返回 HTTP 200;如果异常,返回 500 或 503。

主动探活的关键参数有三个:探测间隔(每 10 秒敲一次门)、失败阈值(连续敲 3 次都没回应才判定 “病危”)、超时时间(敲门后 1 秒内没应答就算失败)。这几个参数直接决定了故障发现的延迟和误报的概率。间隔太短,频繁的探测请求会消耗服务资源;间隔太长,故障发现慢,影响范围扩大。失败的判定也可以用一个公式来表达:

主动探活像护士巡房,责任明确、发现及时,但它也有盲区:它只验证“服务自己说自己还行”,至于服务实际处理的请求质量如何,需要结合被动健康检查来补充——也就是下一节的“病人按铃”。

判定失败=(连续失败次数  ≥  failureThreshold)\text{判定失败} = (\text{连续失败次数} \; \ge \; \text{failureThreshold})
只有当连续失败的次数达到预设的门槛,才真正“宣判”服务不健康。这是为了过滤偶发的网络抖动,避免一次超时就误杀一个健康的服务。
  1. 连续失败次数(n)每次探测请求超时或返回非 200 状态码,就记一次失败。
    n=连续失败次数n = \text{连续失败次数}
  2. 失败阈值(T)我们允许的“容忍次数”,比如 3 次。这是配置里的 failureThreshold。
    T=failureThreshold=3T = \text{failureThreshold} = 3
  3. 比较判定当连续失败次数达到或超过阈值,系统才认定服务不健康并触发后续动作(重启/摘流量)。
    n≥T  ⟹  判定为不健康n \ge T \implies \text{判定为不健康}

章节三:被动健康检查——病人按铃,监护仪异常报警

主动探活是护士挨个敲门,被动健康检查则是病人床头装了监护仪,一旦生命体征异常,机器自动报警。在微服务体系里,被动健康检查不主动发请求,而是通过收集服务运行过程中的“副产品”来判断健康状态。

这些副产品主要有三类来源。第一类是日志和指标:服务本身会持续输出访问日志、错误日志、CPU 使用率、内存占用、请求延迟等数据。监控系统(如 Prometheus)周期性地拉取或接收这些指标,当某个指标超过设定的阈值(比如 5 分钟内的 5xx 错误率超过 10%),就触发告警。这个过程是“被动”的,因为服务并没有被专门地问“你还好吗”,它只是在正常工作的同时被观察着。

第二类是熔断器状态。在微服务调用链中,调用方通常会配置熔断器(如 Resilience4j 或 Hystrix)。当对某个下游服务的调用失败率超过阈值,熔断器会自动打开,直接拒绝后续请求进入该服务。熔断器本身就承担了“被动健康检查”的职责:它根据实时的调用结果,动态判断下游服务是否可用。这比主动探活更能反映真实的“服务体验”——如果连接池已满、线程阻塞,即便健康端点返回 200,实际调用也可能失败。

第三类是事件驱动上报。服务在发生特定异常事件(如内存溢出、死锁、重要依赖断连)时,主动向注册中心或监控系统上报“我将要下线”或“我不健康”的消息。注册中心收到消息后,会将其从可用服务列表中摘除。这类检查的优点是实时性最强,缺点是需要服务自身具备“自我认知”能力。

被动健康检查像病人身上的监护仪,能捕捉到很多主动探活发现不了的问题,但它是“事后”的。把主动和被动结合起来——既定时敲门,也随时听报警——才是完整的探活体检方案。

章节四:K8s三种探针——liveness、readiness 与 startup

现在我们把视角切到 Kubernetes——微服务时代的“护士长”。K8s 用 Pod 来运行服务实例,每个 Pod 相当于一个病房。K8s 内置了三种探针(Probe),分别对应三种不同的探问方式:liveness 问“还活着吗”,readiness 问“能接客吗”,startup 问“准备好接客了吗”。三者分工明确,互相配合。

liveness 探针决定是否重启容器。如果 liveness 探测失败,kubelet 会杀死容器,然后按重启策略重新创建。这相当于护士发现病人昏迷不醒,立即实施急救和心肺复苏。liveness 探针适合检测死锁、内存泄漏、进程假死这类不可恢复的故障。但要注意,liveness 失败会触发容器重启,代价较大,所以失败阈值一般要设得宽松一些。

readiness 探针决定是否将流量路由到 Pod。如果 readiness 探测失败,K8s 会把该 Pod 从 Service 的 Endpoints 中剔除,不再向它转发流量,但容器不会被杀死。这相当于护士发现病人虽然活着但虚弱无力,于是给他挂上“免打扰”的牌子,不再安排访客(流量)进去,让他静养恢复。readiness 探针适合检测服务暂时过载、依赖组件暂时不可用等可恢复的故障。

startup 探针用于保护启动缓慢的应用。有些服务启动时需要加载大量数据或编译代码,耗时可能长达数分钟。如果直接用 liveness 探针,很可能会在启动完成之前就被判定为“不健康”而反复重启。startup 探针提供一个“宽限期”:在 startup 探针成功之前,liveness 和 readiness 探针都不会执行。这相当于护士长知道有些新病人需要更长时间做入院检查,于是特别标记“该患者检验中,请勿打扰”。

1. 三种探针的判定结构完全一致:都由探测动作 + 参数构成,只是探测目标不同。

Probe=Action+Parameters\text{Probe} = \text{Action} + \text{Parameters}

2. liveness 判定:连续失败次数 ≥ failureThreshold,kubelet 杀死容器。

livenessfail⇒kill container\text{liveness}_{\text{fail}} \Rightarrow \text{kill container}

3. readiness 判定:连续失败次数 ≥ failureThreshold,Pod 从 Endpoints 摘除。

readinessfail⇒remove from Endpoints\text{readiness}_{\text{fail}} \Rightarrow \text{remove from Endpoints}

4. startup 判定:在启动阶段持续探测,成功后 liveness/readiness 才接管。

startupsuccess⇒activate liveness & readiness\text{startup}_{\text{success}} \Rightarrow \text{activate liveness \& readiness}

三种探针就像三道不同的防线,各管一段。但要真正实现高可用,光有探针还不够,服务故障后怎么优雅地恢复,是下一个关键问题。

text3 行
liveness 失败 → 重启容器readiness 失败 → 摘除流量(不重启)startup 失败 → 容器持续重启(启动保护期)

章节五:优雅恢复策略——从救回来到重新接客

探针发现问题只是第一步,“如何恢复”才是完整的闭环。想象一下:护士发现病人病情突变,呼叫医生急救,病人脱离了危险。但接下来呢?直接把他推回普通病房?显然不行,他需要一个从“重症监护”到“普通病房”再到“正常生活”的渐进过程。

微服务的优雅恢复遵循同样的逻辑,分为三个阶段。第一阶段:隔离。readiness 探针失败后,服务从负载均衡池中摘除,新的请求不再进入。此时服务不会收到新的流量,相当于住进了隔离病房。第二阶段:修复/重启。liveness 探针失败会触发容器重启。重启后,新的容器会重新执行启动流程——加载配置、连接数据库、预加载缓存。这个过程可能需要几十秒甚至几分钟。此时如果沿用 liveness 探针检测,就可能误杀“启动慢的好人”,所以需要 startup 探针的保护。第三阶段:重新接入。当恢复健康的容器通过 readiness 探针验证后,K8s 会将其重新加入 Endpoints,流量逐渐恢复。这里有个设计细节:对终端用户影响最小化的关键,不是“多快恢复”,而是“恢复时别中断已有连接”。Kubernetes 的滚动更新(Rolling Update)就是优雅恢复的典型案例:它逐个替换旧版本 Pod,新 Pod 通过 readiness 检查后才继续下一个,确保服务全程可用。

真正的优雅恢复还需要“温柔地告别”。Pod 被终止时,K8s 会先发送 SIGTERM 信号,给应用留出优雅停机时间(terminationGracePeriodSeconds)。应用应该监听这个信号,主动停止接收新请求、处理完正在进行的请求、然后才退出。如果超时未退出,kubelet 才发送 SIGKILL 强制杀死。这个流程避免了“服务正在处理请求,就被突然掐断”的恶劣体验。

从救回来到重新接客,优雅恢复保证了用户几乎无感知的故障切换。但这些机制要真正在生产环境发挥作用,还需要一些工程上的“调教”。

优雅停机时间=min⁡(terminationGracePeriodSeconds,  处理完存量请求所需时间)\text{优雅停机时间} = \min(\text{terminationGracePeriodSeconds}, \; \text{处理完存量请求所需时间})
优雅停机时间的本质是给服务一个“交接”的缓冲期:处理完手头的活再下班,而不是听到铃声就夺门而出。
  1. SIGTERM 信号K8s 通知容器“准备下线”,应用应该监听并启动退出流程。
    Pod 下线→SIGTERM\text{Pod 下线} \rightarrow \text{SIGTERM}
  2. 停止接新请求应用从注册中心摘除自己,不再接收新流量(readiness 标记为不健康)。
    drain 流量⇒readinessfail\text{drain 流量} \Rightarrow \text{readiness}_{\text{fail}}
  3. 处理存量请求已有请求在超时时间内继续处理完毕。
    处理存量≤terminationGracePeriodSeconds\text{处理存量} \le \text{terminationGracePeriodSeconds}
  4. SIGKILL 兜底若超时未完成,系统强制杀死进程。
    超时⇒SIGKILL\text{超时} \Rightarrow \text{SIGKILL}

章节六:高可用实践——探活的调教与演练

探活的机制讲完了,但把这套机制用好的关键,在于参数调优和实践内化。这就像护士巡房的频率和路线需要根据病房的危重程度来设计一样,不同服务也需要个性化配置。

参数调优是第一步。一个高流量的用户服务和一个深夜批处理的离线任务,探针的参数应该截然不同。服务的健康端点应该响应极快(毫秒级),超时时间不宜设置过长。失败阈值太小会导致误杀,太大则发现延迟。以下是一个生产环境的常见配置参考:

多副本容灾是第二道保险。即使有探活机制,单副本服务的重启窗口内仍然会出现服务不可用。生产环境至少要保证每个服务有 2-3 个副本,配合反亲和性调度让它们分布在不同节点上。这样当其中一个 Pod 被 liveness 杀掉重启时,另外的副本仍然能承接流量。

发布门禁与故障演练是将探活能力“内化”为团队习惯的关键。发布门禁指的是:新版本服务上线时,必须通过 readiness 或 liveness 检查才能接入生产流量。这要求 CI/CD 流水线中集成健康检查步骤,未通过不发布。故障演练则是主动制造故障(比如人为杀掉一个 Pod、模拟数据库断连),验证探活机制能否及时响应。混沌工程中的“随机杀 Pod”实验之所以有效,就是因为它能让团队在真实故障来临前,反复练习“发现—隔离—恢复”这套肌肉记忆。

健康的探活体系最终呈现的状态是:每一次故障都被探测到,每一次恢复都不需要人工干预。护士的巡房记录越来越精准,系统的自愈能力也就越来越强。

---

yaml21 行
livenessProbe:  httpGet:    path: /healthz    port: 8080  initialDelaySeconds: 10  # 启动后等待 10 秒再探(配合 startup)  periodSeconds: 10        # 每 10 秒探测一次  timeoutSeconds: 1        # 1 秒超时  failureThreshold: 3      # 连续失败 3 次判死readinessProbe:  httpGet:    path: /readyz    port: 8080  periodSeconds: 5         # 就绪探测更频繁  timeoutSeconds: 1  failureThreshold: 1      # 一次失败立即摘流量startupProbe:  httpGet:    path: /healthz    port: 8080  periodSeconds: 2  failureThreshold: 30     # 容忍 60 秒启动时间

AI 应用场景

场景一:电商大促期间的自动扩缩容与故障自愈

阶段:运维自动化 / 智能化故障响应

解决的问题:双十一期间流量瞬时暴涨,人工盯监控根本来不及。某个订单服务的 Pod 因内存溢出导致假死,如果没有健康检查,用户会持续遇到下单失败;有了探活机制,系统能自动杀掉故障 Pod、拉起新副本,并在流量洪峰时自动扩容。

具体案例:某电商平台在大促前夕,通过配置 liveness 探针(periodSeconds=5, failureThreshold=3)和 readiness 探针,使订单服务的自愈时间从 15 分钟降到 40 秒。同时结合 AI 流量预测,在预测到洪峰到达前 30 分钟通过 HorizontalPodAutoscaler 自动扩容副本数,保证了 99.99% 的订单成功率。

场景二:微服务依赖故障的智能隔离

阶段:故障根因分析与隔离决策

解决的问题:微服务调用链中,下游数据库连接池耗尽会导致上游大量请求阻塞。通过被动健康检查(熔断器)收集调用失败数据,AI 可以自动识别依赖故障的模式,并触发熔断隔离,避免故障在调用链中传播。

具体案例:某金融科技公司的用户服务依赖积分服务,当积分服务响应变慢时,readiness 探针检测到“健康端点响应时间超过阈值”便自动摘除该实例流量。同时,调用方的熔断器根据失败率动态调整隔离策略,将超时从 2 秒降到 200 毫秒,防止线程池被占满。

Python 演示

python83 行
import timeimport randomfrom dataclasses import dataclass  class FakeService:    """模拟一个健康的微服务容器"""    def __init__(self, name):        self.name = name        self.alive = True        self.request_count = 0     def ping(self):        """模拟一次健康探测"""        self.request_count += 1        if random.random() < 0.18:  # 18% 概率模拟故障            return False, 500        if self.request_count % 50 == 0:  # 每 50 次请求模拟一次假死            self.alive = False            return False, 500        return True, 200  @dataclassclass ProbeConfig:    """探针参数配置"""    period_seconds: int = 1    timeout_seconds: float = 0.5    failure_threshold: int = 3    success_threshold: int = 1  class HealthChecker:    """健康检查器:主动探活 + 失败判定"""    def __init__(self, service, config):        self.service = service        self.config = config        self.consecutive_failures = 0        self.consecutive_successes = 0        self.is_healthy = True     def probe_once(self):        """执行一次探测,模拟网络超时和 HTTP 响应"""        start = time.time()        ok, status = self.service.ping()        elapsed = time.time() - start        if elapsed > self.config.timeout_seconds:            ok = False        if ok:            self.consecutive_failures = 0            self.consecutive_successes += 1            if self.consecutive_successes >= self.config.success_threshold:                self.is_healthy = True        else:            self.consecutive_successes = 0            self.consecutive_failures += 1            if self.consecutive_failures >= self.config.failure_threshold:                self.is_healthy = False        return ok, status, self.is_healthy  def main():    config = ProbeConfig(period_seconds=1, timeout_seconds=0.3,                         failure_threshold=3, success_threshold=2)    service = FakeService("order-service")    checker = HealthChecker(service, config)    print("=== 健康检查模拟开始 ===\n")    for t in range(1, 21):        ok, status, healthy = checker.probe_once()        state = "健康" if healthy else "不健康(已摘流量)"        mark = "OK" if ok else "FAIL"        print(f"[t={t:>2}s] {service.name:<15} "              f"探测: {mark:<4} 状态码: {status:<3} "              f"连续失败: {checker.consecutive_failures} "              f"实例状态: {state}")        time.sleep(config.period_seconds)        if not healthy:            print(f"   -> k8s 已将该实例从 Endpoints 中摘除,等待下一次恢复探测...")    print("\n=== 模拟结束 ===")  if __name__ == "__main__":    main()
目的:演示主动探活的核心机制——定时探测、连续失败计数、阈值判定和流量摘除。
  1. class FakeService模拟一个微服务实例,包含 18% 概率的随机故障和周期性的“假死”,帮你理解服务为什么会“进程还在但不可用”。
  2. @dataclass class ProbeConfig用数据类承载探针参数,等价于 K8s YAML 里 periodSeconds 和 failureThreshold 的配置。
  3. elapsed > config.timeout_seconds模拟网络超时:探测请求超过超时阈值即判定失败,与实际 HTTP 探测逻辑一致。
  4. self.consecutive_failures = 0成功后清零失败计数,这是“连续失败”语义的关键实现,避免累积历史失败导致长期不健康。
  5. self.is_healthy = False当连续失败数达到阈值,标记实例不健康,对应 readness 探针摘流量的动作。
  6. time.sleep(config.period_seconds)按探测间隔循环,模拟 kubelet 周期性的巡检行为。

常见误区

误区“为了让健康检查更准确,我把数据库连接、缓存状态、业务依赖全都检查了一遍,这样最能反映服务的真实健康情况。”

正解健康检查端点的设计原则是“轻量快速”。试想护士巡房如果每次都做全套体检,反而会耽误发现真正急危的病人。健康端点如果依赖数据库,一旦数据库抖动就会导致健康检查失败,进而触发不必要的重启,形成“检查本身导致的故障”。正确的做法是:/healthz 只检查进程级存活;数据库等依赖的状态交给专门的检查或被动监控去盯。

为何常见:初学者容易把“健康检查”和“全面体检”混为一谈,认为检查项越全越安全,忽略了健康检查本身也有成本和副作用。

误区“既然都是探针,那我只配一个 liveness 探针不就等于同时做了存活和就绪检查吗?反正失败都会重启。”

正解混用会导致灾难性的后果。liveness 失败会重启容器,而如果服务只是暂时的负载过高(readiness 可能需要摘流量),liveness 重启会加剧问题——所有正在处理的请求被强行中断,服务雪崩。正确的分工是:可恢复的故障用 readiness(摘流量,等它自己恢复),不可恢复的故障才用 liveness(重启)。读操作频繁的服务建议只配 readiness,避免 liveness 造成不必要的重启。

为何常见:K8s 文档中两者配置格式几乎一样,初学者容易认为它们可以互换,忽略了各自触发动作的底层差异。

误区“故障发现要快,所以我把探测间隔设为 1 秒,失败阈值设为 1,这样一有问题立刻就能发现。”

正解探测过于频繁会变成一种“自我攻击”。每个健康检查请求也会消耗 CPU 和网络连接,对于有几十个实例的服务,1 秒一次的探测会占用大量资源。更关键的是,间隔越短越容易受网络抖动影响导致误判。生产环境的推荐做法是:普通服务 periodSeconds=10,高优先服务 5 秒左右,失败阈值 3 次以上,用“快慢结合”来平衡响应速度和稳定性。

为何常见:追求“最快发现”是直觉反应,但忽略了探测本身对系统的开销和噪声敏感性。

误区“我不用 K8s,用的是一台裸机部署,所以不需要健康检查。”

正解健康检查是服务治理的基本功,不是 K8s 的专利。即使不用 K8s,注册中心(如 Nacos、Consul)也会通过心跳机制感知服务上下线;负载均衡器需要健康检查来摘除异常节点;监控系统需要探活来触发告警。这些都属于健康检查的范畴。K8s 只是把这些能力统一封装成了标准探针。

为何常见:很多开发者是从 K8s 入手的,容易把探针机制等同于 K8s 功能,忽略了它在传统架构中早已存在的形式。

误区“系统有探活了,Pod 挂了会自动重启,那我不用配监控告警也不会出大问题。”

正解探活解决的是“当前实例是否可用”,监控解决的是“系统整体质量趋势”。探活保证了一个 Pod 重启后恢复了;但如果所有 Pod 都在频繁重启,探活机制会让你“看到每次重启都成功”,却掩盖了“系统正在整体劣化”的事实。真实故障往往需要结合错误率、延迟、资源水位等指标才能定位根因。探活和监控是互补关系,不可替代。

为何常见:探活的自动恢复机制容易给人“系统很健壮”的错觉,忽略了频繁重启本身就是一个严重信号。

面试题

Q1. K8s 中 liveness 探针探测失败后,kubelet 会执行什么操作?

单选题 · easy

  • A.将 Pod 从 Endpoints 中摘除
  • B.重启容器(杀掉并重新创建)← 正确答案
  • C.发送告警但不处理
  • D.自动扩容副本数

答案:B。liveness 探针的核心职责是判断“容器是否存活”,一旦失败,kubelet 会杀死容器并按重启策略重建。选项 A 是 readiness 失败的动作,C 和 D 都不是探针的职责。可以把 liveness 理解为护士发现病人心跳停止后立即实施急救,而不是拉个帘子不让人看。

Q2. 一个服务因数据库暂时不可用而无法处理请求,但进程本身活着。此时最合适的探针配置是?

单选题 · easy

  • A.配置 liveness 探针,失败后重启容器
  • B.配置 readiness 探针,失败后摘除流量← 正确答案
  • C.同时配置 liveness 和 readiness,且都设为 1 秒间隔
  • D.不配探针,依赖人工处理

答案:B。数据库暂时不可用是可恢复的故障,readiness 探针会将该 Pod 从 Service 的 Endpoints 摘除,等依赖恢复后再接流量。如果用 liveness 重启容器,反而会打断正在处理的请求并产生无意义的重启循环。这就像病人只是虚弱乏力(不是昏迷),需要休息而不是电击复苏。

Q3. 以下哪种场景最适合用 startup 探针?

单选题 · mid

  • A.请求量已经很高的服务
  • B.启动时需要加载大量数据、耗时较长的应用← 正确答案
  • C.需要频繁重启的服务
  • D.无状态服务

答案:B。startup 探针专为“启动慢”的应用设计,它在应用启动完成前保护 liveness/readiness 不生效,避免容器在初始化过程中被误杀。比如加载 2GB 模型文件的应用,如果直接用 liveness 探测,几分钟内就会被连续失败判定为不健康而反复重启。startup 探针相当于护士长标了“该患者检验中”,给足启动时间。

Q4. 关于主动探活与被动健康检查,下列说法正确的是?

单选题 · mid

  • A.主动探活能发现所有服务故障
  • B.被动健康检查不产生任何额外请求
  • C.主动探活通过定时请求健康端点来判断服务状态← 正确答案
  • D.被动健康检查比主动探活实时性更差

答案:C。主动探活的本质是周期性向服务发起探测请求,根据响应判断健康状态。A 错误,主动探活有盲区(如服务内部依赖问题);B 错误,被动检查虽然不主动发请求,但指标采集同样会消耗资源;D 错误,事件驱动上报的被动检查实时性往往比定时探测更强。

Q5. 以下哪几种方式属于被动健康检查的范畴?

多选题 · mid

  • A.K8s kubelet 定时请求 /healthz 端点
  • B.熔断器根据调用失败率打开熔断开关← 正确答案
  • C.Prometheus 拉取 metrics 指标并触发阈值告警← 正确答案
  • D.负载均衡器每 5 秒发送一次 TCP 探测

答案:BC。被动健康检查的核心特征是“不主动问,而是通过观察服务运行副产物来判断”。熔断器(B)根据实时调用结果动态调整状态,指标监控(C)根据数据判断健康,都属于被动检查。A 和 D 都是主动发起探测请求的主动探活方式,它们不是被动检查。

Q6. 在 K8s 中,readiness 探针失败后,对服务的主要影响是?

单选题 · mid

  • A.容器会被立刻杀死
  • B.Pod 的 IP 会变化
  • C.Pod 从 Service 的后端列表中移除,不再接收新流量← 正确答案
  • D.Pod 会被迁移到另一台节点

答案:C。readiness 探针管理的是“流量接入”——失败时 Pod 被从 Service 的 Endpoints/后端列表中摘除,不再转发新请求,但容器本身继续运行,等待恢复。这就像挂上“免打扰”牌子后访客不再进来,但病人仍在病房里躺着恢复。

Q7. 某服务在发布新版本后,readiness 探针频繁失败导致流量无法接入。合理的排查思路包括?

多选题 · hard

  • A.查看服务日志确认是否有启动异常← 正确答案
  • B.检查 readiness 探针的健康端点是否依赖了新版本中不可用的资源← 正确答案
  • C.将探针间隔调为 1 秒,让 K8s 更快发现恢复
  • D.确认探针的 path/port 是否与新版本实际监听端口匹配← 正确答案

答案:ABD。探针失败通常是服务侧的问题,应该检查应用日志、健康端点的依赖是否完整、以及探针配置是否与新版服务匹配(A/B/D 都是有效的排查方向)。C 是常见误区,调短探针间隔并不会让服务启动更快,反而可能增加资源消耗。正确做法是通过 startup 探针提供启动宽限期。

Q8. 关于优雅停机(Graceful Shutdown),以下描述正确的是?

单选题 · hard

  • A.Pod 被删除时立即发送 SIGKILL 信号
  • B.应用收到 SIGTERM 后应立即停止所有请求处理并退出
  • C.应用应停止接收新请求、处理完存量请求后再退出,超时才被强制杀死← 正确答案
  • D.优雅停机只对无状态服务有意义

答案:C。优雅停机要求应用监听 SIGTERM(不是 SIGKILL),主动停止接新流量并处理完正在进行的请求,K8s 会等待 terminationGracePeriodSeconds。如果超时仍未退出,kubelet 才发送 SIGKILL 强制终止。这就像下班前把手头的病人交接完再走,而不是听到铃声就夺门而出。

Q9. 主动探活的“失败阈值”(failureThreshold)主要作用是?

单选题 · easy

  • A.让系统更快发现故障
  • B.过滤偶发的网络抖动,避免因单次超时误判服务不健康← 正确答案
  • C.限制探测请求的并发数
  • D.控制日志输出量

答案:B。失败阈值规定“连续失败多少次才算不健康”,它的核心价值是容忍偶发抖动(比如网络瞬断 1 次),只有持续失败才触发重启或摘流量。阈值设得太小容易误杀,设得太大则会延迟故障发现,需要根据业务容忍度权衡。

Q10. 以下哪些策略能有效提升微服务系统的高可用性?

多选题 · hard

  • A.为服务配置 liveness 和 readiness 探针,实现自动重启和流量摘除← 正确答案
  • B.每个服务部署多个副本,并调度到不同节点← 正确答案
  • C.将健康检查纳入 CI/CD 发布门禁← 正确答案
  • D.为每个副本设置独立的用户流量入口,不做负载均衡

答案:ABC。探针自愈(A)、多副本容灾(B)和发布门禁(C)都是提升高可用的关键实践。D 完全错误——每个副本独立入口会导致流量无法在故障时自动切换,恰恰破坏了高可用。高可用的核心不是“更多的入口”,而是“统一的调度与自动的故障转移”。