
说明本文讨论的是模型服务的健康判定与就绪探针分层设计属于 AI 运维话题不涉及具体模型版本与价格。AI 领域版本迭代极快凡涉及版本号、价格、可用性请以你阅读时的官方页面为准。文中代码为结构示意请按自己的技术栈调整后再上生产。一、进程活着不等于服务可用模型服务的健康检查第一件要纠正的认知是编排系统默认的那套探测回答的问题和你真正想问的问题不是同一个。1.1 三个看起来没问题的信号最容易让人放心的信号有三个进程在容器没退出主进程状态正常。端口通TCP 能连上握手成功。HTTP 200健康端点返回了成功码。这三个信号在普通 Web 服务上大体够用因为那类服务的请求链路中间没有需要长时间准备的阶段。模型服务不一样它有一个很重的准备步骤把权重从存储读进显存、把推理引擎初始化好。这一步没走完之前进程完全健康端口完全通健康端点也会老老实实返回 200。于是就有了那种典型形态发布完成后监控上一个红点都没有负载均衡把流量打了进来用户侧大面积超时翻日志发现推理请求全卡在初始化上。探针没说谎它只是没被问对问题。信号它实际回答的问题它漏掉的部分进程存活主进程还在不在引擎是否可计算端口可连网络栈是否就绪加载是否完成健康端点 200该端点自身是否正常真实推理是否可用1.2 模型加载完成与权重就位是两件事把加载拆开看至少有三段进程起来、框架初始化能监听端口能返回健康码。权重文件读取与反序列化从存储读到内存或显存可能需要格式转换。推理引擎就位图构建、算力上下文建立、显存池预分配之后才能接受第一次前向计算。第 1 段结束就能算进程存活但只有第 3 段结束才能接活。多数健康端点只反映第 1 段少数会覆盖到第 2 段第 3 段几乎完全不在默认覆盖范围里。一个可操作的判据是问探针你能算一次吗而不是问你还活着吗。前者的答案才和用户拿到的结果直接挂钩。二、三层探针各自探什么、失败动什么2.1 存活层只探自己存活探针回答的问题最窄进程还在不在、端口还能不能连。它的判据应该几乎不可能因为外部因素而失败典型形态就是一次本地进程状态检查。它的失败动作只有一个重启理由是进程已经进入无法自行恢复的状态死锁、崩溃后僵住。反过来说如果存活探针的判据里掺进了会随外部依赖波动的检查那么依赖抖一下就会导致实例被重启重启期间它上面的所有请求全部丢失等于把一次小幅抖动放大成一次服务中断。所以存活探针要刻意做窄只看自己失败容忍次数给到 3 次以上示意值避免偶发超时就被判死。2.2 就绪层能接住第一个请求就绪探针回答的是这个实例现在能不能接住第一个请求。它要覆盖三件事——模型加载与引擎就位、必要的依赖可达、本地资源还能不能接纳新请求。它的失败动作是从流量后端摘掉而不是重启。这个区别是第五章的主题这里先记住结论。时机上就绪探针要在实例真正能服务之前就开始探测、并先失败一段时间这样负载均衡才不会在实例没准备好时把流量切过来。编排系统里的初始延迟与失败阈值要设得比加载耗时更长。2.3 端到端层走一次真实推理并校验返回形态端到端探针是唯一直接回答能不能出正确结果的一层。它发一个受控的短请求拿到返回后校验返回形态——字段是否齐全、结构是否可解析、内容是否落在预期范围内。它不该被当成存活或就绪探针来用原因有两个它太重第三章专门讲代价以及它的失败原因太多可能是上游依赖的问题不一定是本实例的问题。把它单列一层、用较低的频率跑产出的是这个实例的可用性有没有退化的判定而不是能不能接流量的判定。三层对照如下层级探测对象主要判据失败动作探测频率示意存活进程与端口本地进程状态、端口监听重启实例较疏每 30 秒就绪加载与依赖引擎就位、依赖可达、可接纳请求摘流量中等每 10 秒端到端真实推理结果返回形态与内容是否符合预期标记降级触发观察较疏每 1 到 5 分钟表内频率为示意值实际按加载耗时与请求代价来定。⚠️ 代码待验证# 三层探针的配置示意阈值为示例值需按自己服务的基线调整probes:liveness:type:process# 只看本地进程与端口interval:30sfailure_threshold:3# 容忍偶发抖动action:restartreadiness:type:local_and_depsinterval:10sfailure_threshold:2initial_delay:90s# 需覆盖引擎就位的耗时action:remove_from_lb# 只摘流量不重启e2e:type:real_inferenceinterval:120stimeout:20svalidate:response_shapeaction:mark_degraded# 不直接改流量交给打分分档三、探针自己的代价与副作用3.1 真实推理探针占掉的东西一次真实推理至少占三样东西显存会创建序列、占用一部分临时缓冲运行期间实打实占着。排队位置探针请求和用户请求一起排队。队列本来就紧的时候探针相当于给自己插了一个用户的队。计算时间一次前向计算的时间从别的请求那里拿走。探针类型占用显存占用排队位影响成本账主要副作用本地进程检查否否否几乎无依赖可达性检查否少量否依赖侧有额外查询真实推理探针是是是抬高瞬时水位提前触发懒初始化单个探针的代价很小问题在于它和频率、副本数相乘。假设每个副本每分钟发一次一百个副本就是每分钟一百次与业务无关的推理。这个量通常不影响服务本身但会影响显存水位的读数——探针在跑的那一刻水位会比平时高一点。做法是给探针用量单独打标否则它会被混进业务用量让成本核对长期对不上。3.2 探针把冷实例探热这回事还有一层副作用容易被忽略探针会改变它探测的对象。新拉起的实例在没有真实流量之前如果端到端探针跑得早它会提前触发一次推理把引擎的懒初始化算子编译缓存、显存池扩到实际尺寸等提前完成。结果是探针跑完之后这个实例的第一次真实请求变快了。这件事有两面。好的一面是它缩短了用户感知的首次延迟问题是它让探针通过和实例已充分预热画了等号而你看到的首次延迟数据其实是探针贡献的。要判断影响就在灰度环境里对比一次同一版本一组开端到端探针、一组不开看真实首请求的耗时差异。3.3 给探针留一条轻量通道既然探针会和真实请求抢资源常规做法是给它单独留一条通道分三档本地检查不走推理存活与就绪层能本地判定的部分绝不发推理请求。轻量请求走独立配额端到端探针用的短请求单独打标调度上不占业务请求的优先位也不计入业务配额。探针结果不进业务指标探针产生的延迟、成功率、用量单独落到一组指标里避免污染业务的延迟分位。四、两类误判假阳性与假阴性4.1 假阳性探针过、用户挂假阳性的典型形态是所有探针都是绿的用户侧的错误率却在涨。成因通常是探针只探了自己没探链路。最常见的三类探针是一个本地健康端点只检查自身状态而真正的失败发生在上游依赖检索库连不上、上游服务拒绝。探针走了另一条路径比如直连实例绕过了负载均衡、网关或鉴权层链路上的问题它看不见。探针用一个特殊的高权限身份而真实请求用的是普通身份配额或权限出问题时探针照过。识别假阳性靠的不是探针本身而是把探针的成功率和用户侧的成功率放在一张图上对比。两条线长期贴合说明探针有效一旦分开说明探针的判定和真实可用性脱钩了这个差值本身就是一个关键指标。4.2 假阴性探针挂、服务其实好假阴性的形态相反就绪探针失败、流量被摘但手动发一个请求过去服务返回正常。成因多是阈值太紧或判据太脆超时给得太短上游依赖偶发抖动一次就超过探针超时而真实请求因为重试和更宽松的超时并没有受影响。失败容忍次数太低一次网络抖动就累计到阈值。判据里含了本来就该波动的量比如把缓存命中率写进就绪判据它一波动实例就被摘。假阴性的代价比假阳性隐蔽服务其实好着容量却被白白摘掉一块剩下的实例承担更多流量可能就此进入越来越紧张的循环。它还会污染运维判断——值班同学会以为服务有问题去查一个并不存在的故障。4.3 分开度量两个方向必须分开度量因为要看的信号不同假阳性率探针成功率减去用户侧成功率。用同一个时间窗口、同一种成功定义比如返回 200 且结构可解析否则两个数不可比。假阴性率被摘掉之后经旁路复核发现服务正常的比例。样本量不大抽检即可但要留记录。形态典型成因识别方式修法假阳性探针只探自己、走旁路、用高权限身份探针成功率与用户成功率出现长期差值把关键依赖纳入探针用与真实请求一致的路径与身份假阴性超时太短、容忍次数太低、判据含波动量摘流量后旁路复核发现服务正常放宽超时与容忍次数把波动量移出就绪判据这个张力没有统一答案只能按业务对错误结果和容量损失哪个更不可接受来决定并把决定写进配置注释里方便下一次调整时知道当初为什么这么定。五、就绪摘流量存活才重启5.1 两个动作的代价不对称摘流量这个实例不再接新请求。代价是集群少了一份处理能力已经在跑的请求可以做完。影响面可控而且可以撤销——探针恢复流量就切回来。重启这个实例上的进程被终止。代价是该实例上正在处理的请求全部丢失而且重启后要重新走一遍加载与预热期间它一样接不了活。影响面比摘流量大一个量级且不可撤销。两件事的代价差这么多判定条件就必须严格分开只在实例已经不可能自行恢复时才重启。就绪不通过只说明现在不能接活可能下一秒就恢复了重启是过度反应。5.2 滚动升级期的自伤形态一个很常见的组合是部署配置里就绪探针失败被当成了重启条件或者存活探针的判据里带了依赖检查。于是升级期间会出现这样的循环新实例拉起模型正在加载就绪探针失败这本来就该失败。因为就绪失败被当成重启条件实例被杀掉重来。重新加载又失败又重启。永远没有实例能走完加载升级卡住同时旧实例被陆续替换掉整体容量持续下降。根因不是探针坏了而是它的失败动作配错了。修法很直接把重启从就绪的失败动作里去掉只保留摘流量同时给就绪探针足够的初始延迟与失败容忍让加载中这段时间不触发任何动作。5.3 动作分流的判定顺序固定一个顺序值班时就不用现场讨论先看存活。存活失败就重启没有别的选择。存活正常、就绪失败只摘流量同时记录摘流量的持续时长。就绪恢复自动切回流量切回后观察一小段时间再确认。端到端失败、就绪正常不动流量先标记降级并触发观察如果持续存在再按第六章的依赖排查顺序定位。⚠️ 代码待验证# 动作分流伪码存活失败才重启就绪失败只摘流量defdecide(signals,state):ifnotsignals[liveness_ok]:# 唯一触发重启的条件returnrestartifnotsignals[readiness_ok]:# 只摘流量记录持续时长供巡检使用state[detached_since]state.get(detached_since)ornow()returnremove_from_lbifstate.get(detached_since):state[detached_since]Nonereturnattach_to_lbifnotsignals[e2e_ok]:# 端到端失败不直接改流量交给打分分档returnmark_degradedreturnnoop完整版资料清单本文用到的探针配置模板与健康分档对照表都整理在里面了扫码即可获取六、依赖项要不要纳入就绪判定6.1 四类依赖的纳入方式检索库服务必须靠检索才能出结果时它连不上就该判为不就绪。判据用一次轻量的存在性查询按主键取一条已知记录不要用真实业务查询。上游服务代理层或级联调用的场景里上游不可达时本实例确实出不了结果。但它不该直接判不就绪上游故障往往是全局性的把所有实例摘掉不改善任何事只会让整条链路全黑。更稳的做法是纳入端到端层与降级判断。密钥与配额密钥过期、配额耗尽会让请求必然失败。这类问题适合放在启动自检里做一次而不是每轮探针都查因为它们不会在两次探针之间自行恢复重复查只是浪费调用。缓存预热缓存未预热不等于不可用只是首批请求慢一些。它不该进就绪判据但值得作为一个观测项因为它直接影响首批请求的延迟。依赖是否纳入就绪纳入方式理由检索库是轻量存在性查询缺它就无法产出结果上游服务否走端到端层由端到端探针与降级判断覆盖全局性故障摘本实例不带来改善密钥与配额启动自检一次启动阶段校验并告警不会在两次探针之间自行恢复缓存预热否作为观测项影响首批延迟不影响可用性6.2 为什么不该全部纳入把依赖全塞进就绪判据会得到一个敏感但没用的探针。原因是它把本实例的问题和外部的问题混在了一起而这两类问题的正确动作不一样本实例的问题摘掉它把流量给别人是对的外部的问题所有实例都会同时不行摘掉所有实例只会让服务彻底不可用。一个可操作的判据是问这个依赖挂了换一个实例会不会更好。答案是会的纳入就绪答案是不会大家都一样的放进端到端与降级判断或者只做告警。6.3 依赖探针的位置与超时依赖检查放在就绪层时要注意两件事超时必须显著短于探针间隔。依赖查询卡住时探针本身不能跟着卡住否则探针的线程被占满反而让探针自己变成故障点。要有独立的失败计数。依赖与本地检查的失败分开计数摘流量的原因才能区分是实例坏了还是外部坏了。⚠️ 代码待验证# 依赖就绪检查清单示意按你的依赖替换命令set-u# 1) 检索库一次轻量存在性查询超时必须短于探针间隔timeout2s check_retrieval --probe-key known-record||exit10# 2) 密钥有效期本地读一次到期时间不发外部请求check_credential_expiry --min-remaining 1h||exit11# 3) 权重文件确认加载记录存在且校验通过check_weights_loaded --verify-checksum||exit12# 4) 引擎就位读本地标记位不触发真实推理check_engine_ready--marker/run/engine.ready||exit13# 退出码分档10 到 13 表示就绪失败摘流量其余非 0 视为异常exit0七、健康度不是二值连续打分与分档动作7.1 从红绿到分数做法是把每个观测项映射成 0 到 1 的分数再按权重合成一个总分。几条原则分数要单调指标变差分数只能变低不能回勾否则运维没法根据分数判断趋势。权重按对用户的影响给不按实现的难易给。端到端成功率对用户影响最大权重就最高。失败要有惩罚项只要出现硬失败比如端到端连续失败就算其他项都很好总分也要被拉到低档不能被平均掉。分档与权重示意如下阈值均为示意值⚠️ 代码待验证{score_bands:[{band:normal,range:[0.9,1.0],action:keep},{band:observe,range:[0.75,0.9],action:keep_and_watch},{band:degrade,range:[0.5,0.75],action:reduce_function},{band:detach,range:[0.0,0.5],action:remove_from_lb}],weights:{e2e_success:0.5,readiness_ok:0.3,local_checks:0.2},hard_fail_penalty:0.6,note:阈值为示意值hard_fail_penalty 表示出现硬失败时总分的扣减比例}分档的价值在于它让动作有了梯度分数略降时先观察明显下降时减少非核心功能只有跌到低档才摘流量。相比一次失败就摘它对抖动天然更耐受。这里的减少功能只作为动作的名字具体减什么属于服务自己的业务决定。7.2 分档触发与抖动抑制分档之后仍然会有边界抖动分数在某一档的边界来回穿。三种抑制手段滞回区间上升和下降用不同阈值。比如跌到 0.88 才降到观察档但要回到 0.93 才升回正常档均为示意值。驻留时间分数进入某一档后必须持续一段时间才允许动作避免一次尖峰就触发摘流量。冷却时间两次同类动作之间设最短间隔防止动作本身来回震荡。还有一个陷阱分数不能跨实例直接比较。不同实例的分布会因硬件与所在节点而不同横向差异未必反映真实健康差异正确的用法是同一个实例自己和自己比趋势。7.3 观测与巡检节奏健康判定要长期站得住靠的是下面这几个量探针成功率分层看存活、就绪、端到端各自的成功率与失败原因分布。就绪抖动次数单位时间内进入与退出就绪状态的次数。明显上升通常意味着阈值太紧或依赖不稳。摘流量的持续时长单次摘流量持续多久。短时摘流量正常持续变长说明存在没有恢复的问题。假阳性与假阴性的抽检比例每周抽一部分被判定的样本做旁路复核作为探针有效性的体检。⚠️ 代码待验证# 健康判定的观测项示意指标名按你的监控系统改写 probe_success_ratio{layerliveness|readiness|e2e} # 分层成功率 probe_failure_reason{layer, reason} # 失败原因用于区分本地与依赖 readiness_flap_count_1h # 就绪抖动次数 detached_duration_seconds # 单次摘流量持续时长 health_score{band} # 分数与所处档位 false_positive_delta # 探针成功率与用户成功率之差 false_negative_reviewed_total # 假阴性复核样本数 probe_cost_ratio # 探针用量占总量比例用于成本核对 # 一个提醒摘流量时长要按实例和集群两个粒度都看。 # 单实例长时间摘流量多半是实例问题多实例同时摘流量通常指向依赖或全局配置。完整版资料清单本文用到的探针配置模板与健康分档对照表都整理在里面了扫码即可获取附表 A关键取舍一览取舍本文结论判断依据位置健康端点返回 200 能不能代表可用不能加载未完成时端点一样返回成功第一章存活探针的判据范围只看本地进程与端口掺入依赖会让抖动放大成中断第二章就绪探针的失败动作只摘流量不重启就绪失败可自行恢复重启不可撤销第二章端到端探针的失败动作标记降级不直接改流量失败原因过多不等于本实例不可用第二章真实推理探针要不要限速打标要代价随频率与副本数相乘第三章探针要不要避让排队只做打标与独立配额完全避让会测不到排队场景的可用性第三章假阳性怎么发现对比探针与用户成功率差值本身就是探针有效性的判据第四章假阴性怎么发现摘流量后旁路复核复核才发现服务正常说明判据过紧第四章摘流量与重启能否合并不能两者代价相差一个量级第五章上游服务是否纳入就绪不纳入走端到端层全局性故障摘本实例无改善第六章密钥与配额怎么查启动自检一次不会在两次探针之间自行恢复第六章健康度用红绿还是分数用连续分数加分档边界抖动与动作粒度都需要梯度第七章分数能否跨实例横向比较不能分布差异不代表健康差异第七章附表 B术语速查表术语含义存活探针只判定进程与端口是否正常的探针失败动作是重启就绪探针判定实例能否接住请求的探针失败动作是摘流量端到端探针走一次真实推理并校验返回形态的探针用于判断可用性是否退化引擎就位推理引擎完成图构建、上下文建立与显存池预分配可接受首次前向计算假阳性探针判定通过但真实请求大面积失败假阴性探针判定失败但服务实际可用摘流量把实例从负载均衡后端移除不再接收新请求可恢复初始延迟实例启动后开始探测前等待的时间需覆盖加载耗时失败容忍次数连续失败多少次才判定探针不通过滞回区间上升与下降使用不同阈值避免边界反复触发驻留时间分数处于某一档并持续一段时间才允许动作心搏标记引擎就位后写入的本地标记用于就绪检查而不触发推理硬失败直接判定服务不可用的失败类型不参与加权平均探针用量打标给探针产生的调用单独打标避免污染业务用量与延迟分位写在最后这篇用到的资料写这篇文章时我把几个模型的官方文档、参数表和实测记录都对了一遍顺手整理成几份配套的东西大模型学习路线图从 LLM 基础到 Agent 开发各阶段该学什么、用什么资料大模型全套教程按主题分好的视频与文档清单大模型实战好书24 本附每本适合的阶段资料是我自己整理的放在下面这个码上扫码即可获取添加时备注「大模型」优先通过。拿到之后建议先看学习路线图那一份先定位自己在哪个阶段再决定学什么比一上来就啃框架效率高得多。