想象一下,你是一家三甲医院ICU团队的负责人,面前躺着一位特殊的“病人”——它不是血肉之躯,而是一个昼夜不息为千万用户提供智能服务的AI模型。你没法问它“今天感觉怎么样”,它也不会主动喊疼。当它状态下滑、推理变慢、GPU过热、甚至悄悄发生“模型漂移”时,你必须第一时间发现,否则线上的用户就会集体“投诉”。 这就是AI工程化里的监控命题。而Prometheus,就像是ICU里那台24小时不眨眼的监护仪,每隔15秒自动量一次“脉搏”、测一次“血压”;Grafana是把所有体征汇聚到床旁的大屏幕;Alertmanager是护士台的呼叫铃,关键时刻会立刻通知值班医生(也就是你);当常规传感器不够用时,自定义Exporter就像为病人量身定制的化验项目,能测出“模型置信度”“漂移指数”这些隐藏指标。 这篇文章会带你把整套“ICU监护系统”搭起来:从理解Prometheus如何拉取数据,到盯住推理延迟、QPS、GPU等关键体征;从配置告警规则,到动手写一个Python Exporter;从看懂模型漂移的PSI化验指标,到用Grafana画出一眼明白的大屏。读完你就能为自己的AI模型服务建立一套完善的监控体系,做那个心里有底的“值班医生”。
掌握Prometheus监控是AI模型从“能跑”到“稳跑”的必由之路。对MLOps工程师和DevOps团队而言,这是一把发现性能问题、保障服务稳定性的关键钥匙,也是AI工程化岗位上最有性价比的技术投资。
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
监控体系搭建
想象一下ICU监护系统是怎么运转的:病人胸口的电极贴片、指尖的血氧夹,并不会主动朝护士站“喊话”。每隔一段时间,护士站会挨个床位轮询,把各传感器读数统一收回来,在中央屏幕上汇集成一条条跳动的曲线。这套“定时查房、集中汇总”的思路,正是Prometheus监控架构的灵魂。
Prometheus采用的是“拉取式”(Pull)采集模型。被监控的AI服务只需要开放一个HTTP端点,通常路径是 /metrics,把指标以纯文本列出来。Prometheus服务器则像尽职的巡逻护士,每隔固定的采集周期(默认15秒)去访问这些端点,把最新数据拉回自己的时序数据库。相比“推送式”架构,拉取让监控方完全掌握节奏,服务规模变大也不会把监控中心淹没。
每次拉取得到的数据,在Prometheus中被称为一个“样本”,它的完整身份由四要素刻画:
指标名说明“测的是什么”,标签集说明“测的是谁”。比如 gpu_utilization_pct{gpu_id="0", instance="node-1"},一眼就能看出是node-1节点第0块GPU的利用率。Prometheus把指标归纳为四种类型:Counter(计数器)只增不减,适合记录总请求数;Gauge(仪表盘)可升可降,适合表示当前GPU温度;Histogram(直方图)统计延迟分布;Summary(摘要)预计算分位数。此外Prometheus还支持服务发现——就像护士站自动登记新入院病人,Kubernetes里新弹出的Pod会被自动加入采集列表,不需要人工逐个添加IP地址。
理解了这套数据模型,再回头看ICU就豁然开朗:每根传感器线缆都标注着“几号床、测什么”,护士站按固定节奏轮询,给每个读数盖上时间戳。Prometheus把这些经验搬进了服务器集群,让AI服务的每一条“体征”都有了标准化档案——这正是构建一切高级监控能力的基石。
- 指标身份指标名加标签集唯一定位被测对象,例如
requests_total{model="bert"}表示bert模型的请求总数 - 观测数值数值是采样时刻的实测结果,可以是利用率、计数或延迟等任意量化值
- 采样时间戳时间戳记录采样发生的精确时刻,让每条数据都有统一的时间坐标
关键性能指标
ICU护士查房时,最关注“四大生命体征”:体温、脉搏、呼吸、血压。对AI模型服务而言,也恰好有四条必盯的“体征线”:推理延迟、QPS、GPU利用率和显存占用。它们共同回答三个问题:用户等得久不久、系统扛不扛得住、硬件还剩多少余量。
推理延迟是“脉搏”,指从请求发出到拿到结果的总耗时。它不能只看平均值——几个慢请求就能把均值拉高,掩盖大多数请求其实很快的事实,因此业界习惯看分位数:P50表示一半请求的耗时,P99表示99%的请求都能在此时间内完成。直方图配合 histogram_quantile 函数,就能在捅与桶之间插值算出P99:
QPS是“心率”,表示每秒处理多少请求。由于请求计数器是单调递增的累计值,不能直接画图,必须用rate()求增量速率:
GPU利用率和显存占用可以看作“血氧”,都是Gauge指标,当前值多少就上报多少。利用率长期接近100%,说明模型随时可能在流量尖峰下“窒息”;显存逼近容量上限,则可能直接OOM崩溃。实践中把这两条线和QPS放在同一面板对比,能判断资源使用是否与流量匹配。
值班医生扫一眼监护屏,就能判断病人状态:脉搏过速、血氧偏低、血压异常,每一项都指向不同的处置方案。对AI运维者来说,盯住这四条曲线也能快速回答“模型健康吗”——延迟飙升指向代码或推理路径问题,QPS骤降指向流量问题,GPU爆满指向容量问题,显存爬升则往往暗示内存泄漏。
- 取窗口增量requests_total是单调递增计数器,先取出5分钟窗口内前后的计数差值
- 确定时间跨度
[5m]表示窗口时长300秒,窗口越长曲线越平滑、对突变的反应越迟钝 - 增量除以时间rate()把新增请求数平均到每秒,便得到QPS速率
模型漂移监测
ICU里最危险的往往不是惊天动地的指标突变,而是病人“悄悄地变差”——今天精神差一点,明天化验值偏一点,常规监护仪看不出端倪,等真正出事已经晚了。AI模型也有这种“隐性病情”,叫模型漂移(Model Drift)。
漂移分两类。一类是数据漂移(Data Drift):线上输入的特征分布和训练时不一样了。比如信贷模型训练时客户年龄集中在25到40岁,促销活动后涌入大批20岁出头的用户,特征分布悄悄变了形。另一类是概念漂移(Concept Drift):数据本身没变,但“正确答案”变了——疫情后消费习惯永久改变,同样的特征对应着不同的结果。两者都会让模型预测质量退化,而线上请求往往没有真实标签,无法直接计算准确率。
业界最常用的漂移检测工具是PSI(群体稳定性指数)。它把期望分布和实际分布各自分箱,逐箱对比占比差异:
PSI的判读已有行业共识:小于0.1表示分布稳定;0.1到0.25之间需要警惕;超过0.25说明漂移严重,必须告警并考虑重新训练。把它做成一个Gauge指标暴露给Prometheus,等于给病人增加了一项“血液化验”——当漂移指数亮起红灯,值班人员就能赶在模型彻底“病倒”之前介入治疗。
- 分布分箱A₁到Aₙ是实际分布各区间占比,B₁到Bₙ是期望分布各区间占比,n由分箱数决定
- 占比差值(Aᵢ − Bᵢ)表示第i个区间实际与期望的占比差距,方向同样重要
- 对数加权求和ln(Aᵢ/Bᵢ)作为权重,偏离越大的区间贡献越大,累加得到最终PSI
告警规则配置
监护仪如果每秒都“滴滴”乱叫,护士们很快就会把报警器关掉——这是真实的临床教训,也是监控圈常说的“告警疲劳”。好的呼叫铃要回答三个问题:什么情况算异常?异常持续多久才喊人?喊谁、怎么喊?Prometheus的告警体系正是围绕这三点设计的。
Prometheus本体负责“评估”,真正的“喊人”交给Alertmanager。告警规则写在配置文件中,核心是一条PromQL表达式加一个持续时长:
规则还支持分级:延迟超阈值的warning级别只发邮件,GPU OOM的critical级别则同时触发短信和电话。Alertmanager会做三件重要的事:分组(Grouping)——把同一时刻同一服务的多条告警合并成一条通知;抑制(Inhibition)——已经处于critical时屏蔽低级别的warning;静默(Silence)——计划维护期间主动屏蔽已知告警。它们共同保证“该响的铃一定响,不该响的铃绝不乱响”。
回到护士铃的设计哲学:它的精髓不是“灵敏度”,而是“可信度”。经过分级、分组、抑制训练的告警系统,会让值班者形成条件反射——收到critical告警立即行动,收到warning告警从容评估。Prometheus + Alertmanager把这种“可信的呼叫铃”植入了AI服务集群,让每个异常都被及时而不过度地听见。
- 查询条件expr是一条PromQL表达式,例如
avg(instance_latency_seconds[5m]) > 0.07,表示平均延迟超过70ms即为异常 - 持续时长for规定条件必须连续满足多久才告警(如5分钟),有效过滤瞬时毛刺
- 输出状态两者同时满足则进入告警状态交给Alertmanager,否则保持正常
自定义Exporter
ICU里,标准监护仪能测心率、血压、血氧,但有些指标——比如血液中的乳酸浓度——必须靠专门的化验设备。AI模型也一样:node_exporter之流只能报“机器级”健康,而“模型置信度”“排队队列长度”“漂移分数”这些业务指标,原生采集器一概无能为力。这时就需要为模型服务量身定做传感器:自定义Exporter。
Exporter的本质是“翻译器”:把业务数据翻译成Prometheus能理解的指标文本,再通过HTTP端点提供给监控中心:
用Python实现一个Exporter极其简单,prometheus_client库提供了现成的Counter、Gauge、Histogram类。典型做法是:在业务代码里调用 requests_total.inc() 给计数器加一,调用 latency.observe(t) 把延迟样本丢进直方图;然后启动 start_http_server(8000),Prometheus就能从 http://localhost:8000/metrics 拉取全部指标。在Prometheus配置文件的 scrape_configs 中注册这个端点,相当于在护士站登记一台新设备。
有了自研的“化验项目”,ICU监护才算真正覆盖业务生命体征:标准传感器管机器,自定义Exporter管模型。你可以把漂移PSI、队列深度、batch大小、缓存命中率统统暴露出来,让监控体系从“能看”升级为“看得懂模型在想什么”。
- 输入业务数据Exporter从模型内部状态读取自定义指标,如置信度、队列深度、PSI分数
- 标准格式转换把数据写成Prometheus文本格式,每条含指标名、标签、数值
- HTTP端点暴露通过 GET /metrics 文本响应供Prometheus拉取并入库
Grafana可视化
采集了那么多指标,如果都躺在时序数据库里只供告警使用,就像ICU把化验单收进抽屉、不给床旁配屏幕——医生总不能每次都翻档案看病。Grafana就是那张“床旁监护屏”,把Prometheus里的数据变成实时、直观、可交互的面板。
接入Grafana只需两步:添加Prometheus数据源,创建仪表盘。数据源告诉Grafana去哪儿查数据,之后在每个面板里写PromQL查询即可。仪表盘上能放各种面板:时序图看延迟和QPS走势,柱状图对比各实例负载,统计卡直接展示SLO达成率:
一个优秀的AI服务仪表盘往往分“层级”设计:第一层是全景总览,一行文字显示服务整体是否健康;第二层是核心指标,延迟、QPS、GPU、显存四个面板并排;第三层是下钻面板,按模型版本、按实例、按入口路由分别展开。Grafana还支持模板变量,做一个下拉框切换“模型A/模型B”,同一套面板就变成每个模型的专属监护屏。仪表盘还能导出JSON存进代码仓库,团队成员拉下来即可复用,实现“监控即代码”。
床旁监护屏的价值不只是好看,它让医生形成全局直觉:扫一眼就判断病情,点一下就能下钻到病因。Grafana把这种直觉赋予了AI运维团队——大屏常驻作战室,告警群弹出消息时,所有人抬头看同一块屏幕,三分钟内就能对齐故障全貌。
- 统计达标请求分子是延迟满足SLO阈值(如≤100ms)的请求数,可直接取直方图对应桶
- 统计总请求数分母是全部请求数,用直方图的 +Inf 桶计数表示
- 计算达成率两者相除再乘100%,得到0到100%的SLO达成率并展示在统计卡上
AI 应用场景
模型上线前的“入院体检”
阶段:模型部署与上线
解决的问题:传统上线只验证功能“能不能跑”,不验证运行状态“稳不稳”。接入Prometheus监控后,新模型一上线就有完整的延迟、QPS、GPU基线,任何偏离都能被立即发现——就像病人入住ICU先做全套生命体征建档,后续任何异常都有对照。
具体案例:某推荐团队上线新排序模型,Grafana面板显示P99延迟从85ms飙到260ms。通过按模型版本下钻对比,定位到新模型batch size过大导致GPU排队,团队立即回滚并优化后重新上线,整个过程不到半小时。
大模型推理集群的资源水位预警
阶段:推理资源运营
解决的问题:GPU是AI服务最贵的“氧气”,利用率过低浪费预算,显存爆满导致OOM崩溃。通过nvidia-dcgm-exporter采集GPU指标,Grafana大屏实时展示集群水位,接近阈值自动告警扩容,相当于给ICU加装了一台“氧气瓶余量监测仪”。
具体案例:某LLM服务的历史对话不断累积,显存占用从11GB慢慢爬升到15GB。监控在达到13GB时触发critical告警,值班人员排查发现上下文未做截断,及时修复后避免了一次服务OOM事故。
模型漂移的“血液化验”
阶段:模型持续监控
解决的问题:线上无标签环境无法监控准确率,模型悄悄退化无人知晓。用自定义Exporter定期计算PSI并暴露给Prometheus,漂移超过0.25立即告警,就像定期给病人抽血化验,把隐性病变扼杀在早期。
具体案例:某信贷风控模型在大型促销活动后,用户特征分布骤变,PSI由0.06升至0.31触发告警。算法团队重新采样训练,两天内完成模型更新,避免了大批错误授信。
Python 演示
import randomimport timefrom prometheus_client import Counter, Gauge, Histogram, start_http_server REQUESTS = Counter('model_requests_total', 'Total number of inference requests')LATENCY = Histogram('model_latency_seconds', 'Inference latency in seconds', buckets=(0.01, 0.05, 0.1, 0.25, 0.5, 1.0))GPU_UTIL = Gauge('gpu_utilization_percent', 'Current GPU utilization in percent')DRIFT_PSI = Gauge('model_drift_psi', 'Current model drift PSI score') class MockModelServer: def __init__(self): self.req_count = 0 self.gpu_util = 60.0 self.drift = 0.05 def predict(self): self.req_count += 1 REQUESTS.inc() latency = max(0.01, random.gauss(0.12, 0.04)) LATENCY.observe(latency) self.gpu_util = max(5.0, min(99.0, self.gpu_util + random.uniform(-4, 4))) GPU_UTIL.set(self.gpu_util) self.drift = max(0.0, self.drift + random.uniform(-0.01, 0.015)) DRIFT_PSI.set(self.drift) return self.req_count if __name__ == '__main__': start_http_server(8000) server = MockModelServer() while True: server.predict() time.sleep(0.5)
REQUESTS = Counter('model_requests_total', ...)定义计数器并命名,Prometheus按这个名字识别总请求数指标LATENCY = Histogram('model_latency_seconds', ..., buckets=(...))定义直方图并自定义桶边界,让P99计算更贴近真实分布REQUESTS.inc()每次推理请求让计数器加一,模拟请求量累计LATENCY.observe(latency)把单次延迟样本丢进直方图,自动累加到对应桶GPU_UTIL.set(self.gpu_util)用Gauge刷新当前GPU利用率,反映实时水位DRIFT_PSI.set(self.drift)定期更新漂移分数,让监控端能画出一条漂移走势线start_http_server(8000)在8000端口启动HTTP服务,Prometheus从这里拉取 /metricsserver.predict()模拟一次真实推理,驱动所有指标持续变化
常见误区
误区我以为只要部署了Prometheus,GPU利用率、显存这些指标就会自动出现在监控里,结果面板上一片空白。
正解Prometheus本身是“数据收集框架”,它不会自动理解GPU。需要额外部署采集器,如node_exporter看机器级指标、nvidia-dcgm-exporter看GPU级指标,这些Exporter把硬件数据翻译成 /metrics 指标,Prometheus再去拉取。就像ICU监护仪必须接上对应的传感器端子,不是插上电源就什么都能测。
为何常见:在服务器上直接敲 nvidia-smi 就能看到GPU数据,容易误以为监控系统也能开箱即用。
误区我以为Prometheus也像日志系统那样,在每台机器上装agent,把数据推送到中心服务器。
正解Prometheus是拉取式(Pull)架构,中心服务器主动按周期访问各目标的 /metrics 端点。被监控方只是“摆好数据等被取”,不需要知道监控中心在哪。这种设计让采集节奏可控、权限管理简单,也带来了服务发现机制——新服务上线自动被发现,就像护士站自动登记新入院的病人。
为何常见:很多日志采集系统是agent推送模式,先入为主地认为监控也是推数据。
误区我把 requests_total 这个计数器直接放到Grafana面板上,以为曲线就是请求速率,结果看到一条一直向上的台阶线。
正解Counter是单调递增的累计值,直接画出来是累计总量,看不出当前速率。必须用 rate() 或 irate() 对计数器求增量除以时间,得到每秒速率才是QPS。就好比里程表的累计公里数不能当车速看,要看速度表。
为何常见:计数器数值直观,新手容易忽略“它是累计值”这个关键属性。
误区我怕漏掉异常,把延迟告警阈值设成10ms,让它一有风吹草动就报警,结果手机半夜被通知刷屏。
正解过低的阈值产生海量告警,值班人员被“狼来了”刷屏后,真正的问题反而被淹没——这叫告警疲劳。正确的做法是用 for 持续时长过滤瞬时抖动、分级告警(warning/critical)、用分组与抑制合并同类告警,让告警“少而准”。ICU的呼叫铃从不是为了覆盖每一个微小的波动设计的。
为何常见:人工盯指标太久,总希望机器尽早介入,却低估了噪音带来的认知成本。
误区我打算在监控面板上画一条线上准确率曲线,认为高于95%就说明模型没漂移。
正解线上推理请求往往没有真实标签,根本算不出准确率。漂移监控的对象是“特征分布”和“预测分布”:用PSI对比训练分布与线上分布、近期分布与历史分布,通过分布偏离程度推断模型是否失效。准确率是结果指标、分布是原因指标,监控分布才能提前预警。
为何常见:训练阶段习惯了准确率这个核心指标,把离线思维直接照搬到了线上监控。
面试题
Q1. Prometheus 采用什么方式采集监控指标?
- A.拉取式(Pull)周期轮询采集← 正确答案
- B.推送式(Push)Agent 主动上报
- C.读取服务器系统日志文件采集
- D.直接连接业务数据库进行查询
答案:A。Prometheus是拉取式架构,监控中心按固定周期主动访问各服务的 /metrics 端点,就像护士定时巡房量体温。B描述的是推模式,C是日志采集系统的做法,D是数据库直查,都不是Prometheus的采集方式。
Q2. 哪种指标类型适合表示 GPU 当前利用率?
- A.计数器(Counter)类型指标
- B.仪表盘(Gauge)类型指标← 正确答案
- C.直方图(Histogram)类型指标
- D.摘要(Summary)类型指标
答案:B。GPU利用率可升可降、反映当前状态,对应Gauge(仪表盘),就像温度计显示此刻体温。Counter只增不减适合总请求数;Histogram和Summary面向延迟等分布统计,都不适合承载“当前利用率”。
Q3. 下列哪个 PromQL 表达式能正确求每秒请求速率?
- A.avg(requests_total[5m])
- B.rate(requests_total[5m])← 正确答案
- C.max(requests_total[5m])
- D.sum(requests_total[5m])
答案:B。Counter是累计值,必须用rate()对窗口内的增量求导才能得到每秒速率,相当于用里程表的间隔差值算车速。avg/max/sum都只做聚合,没有“增量除以时间”的步骤,得不到速率语义。
Q4. 要监控推理延迟的 P99 分位数,应使用哪个函数?
- A.changes(0.99, 延迟桶)
- B.stddev(0.99, 延迟桶)
- C.histogram_quantile(0.99, 延迟桶)← 正确答案
- D.topk(0.99, 延迟桶)
答案:C。P99的含义是“99%的请求耗时小于该值”,直方图累计桶配合histogram_quantile分位数插值就能算出来,相当于考试排名中第99百分位的成绩。topk取前几名,stddev算标准差,changes统计变化次数,都得不到分位数。
Q5. PSI 漂移指标数值越大,通常说明什么?
- A.模型训练效果变得越来越好
- B.服务集群的规模越来越大
- C.分布差异越大,越需要关注← 正确答案
- D.数据采集的频率越来越高
答案:C。PSI衡量两个分布各区间占比差异的加权和,数值越大说明实际分布与期望分布偏离越严重。业界约定小于0.1稳定、0.1到0.25需关注、大于0.25告警,就像化验单上炎症指标超标需要干预。A/B/D都与分布偏离无关。
Q6. 为避免告警风暴,Prometheus 运维中可采取哪些措施?
- A.使用分组与抑制合并同类通知← 正确答案
- B.设置 for 持续时长过滤瞬时抖动← 正确答案
- C.直接删除所有历史告警规则
- D.调大重复告警的发送时间间隔← 正确答案
答案:ABD。for时长让瞬时毛刺不触发告警,分组把同类告警合并成一条,抑制让高级别告警屏蔽低级别,重复间隔控制同一告警的骚扰频率,这些都能有效降噪。C删除规则等于让监护仪彻底失聪,属于因噎废食。
Q7. 自定义 Exporter 默认在哪个端点暴露指标?
- A./config 端点
- B./metrics 端点← 正确答案
- C./health 端点
- D./status 端点
答案:B。Prometheus生态约定所有Exporter在 /metrics 端点以文本格式暴露指标。/status 和 /health 是常见的服务自身状态接口,/config 是配置接口,Prometheus只会去约定的 /metrics 拉取数据。
Q8. 关于模型漂移监控,下列说法正确的有哪些?
- A.预测置信度变化也是漂移的信号← 正确答案
- B.PSI 用于衡量两个分布的差异大小← 正确答案
- C.漂移无法用 Prometheus 进行监控
- D.数据漂移指线上特征分布发生变化← 正确答案
答案:ABD。数据漂移是输入分布变化,概念漂移是答案分布变化,两者都会让模型失效;PSI是量化这种偏离的标准指标;置信度分布偏移同样是重要信号。把这些指标通过自定义Exporter暴露成Gauge,Prometheus完全能监控漂移,C说法不成立。
Q9. 计算最近 5 分钟内平均推理延迟的表达式是?
- A.rate(inference_latency[5m])
- B.sum_over_time(inference_latency[5m])
- C.avg_over_time(inference_latency[5m])← 正确答案
- D.count_over_time(inference_latency[5m])
答案:C。inference_latency如果是Gauge值,avg_over_time会对窗口内每个样本求算术平均,得到5分钟平均延迟。B是求和,D是计数,A的rate()只能用于Counter求速率而非求均值。
Q10. Grafana 仪表盘中可以添加下列哪些面板?
- A.柱状图(Bar chart)面板← 正确答案
- B.物理拓扑(Topology)面板
- C.时序图(Time series)面板← 正确答案
- D.统计卡(Stat card)面板← 正确答案
答案:ACD。柱状图、时序图、统计卡都是Grafana内置常用面板,分别适合对比、趋势、摘要展示。物理拓扑图不在Grafana默认面板集中,通常需要额外插件或由其他网管系统提供。