想象你是一家大型综合医院的体检中心主任。每天清晨,护士们带着体检设备到各个病房测量病人的体温、心率、血压,把数据记录在病历系统里;主治医生打开电脑,查看某位病人过去24小时的体温曲线;检验科发现某项指标超标,立刻呼叫相关科室准备会诊;大屏幕上滚动着各病区的实时床位状态。这套流程,就是Prometheus在云原生世界里做的事——只不过它监测的不是病人,而是一个个微服务程序的生命体征。本文会用“医院体检中心”这个比喻,带你从零理解Prometheus如何采集指标、编写查询、配置告警,最终搭建出一套完整可靠的可观测性体系。
对AI从业者而言,可观测性是保障在线推理服务稳定性的基石。学懂Prometheus,你就能用数据说话、预判故障,让模型服务从“能用”升级为“可信赖”。
可视化演示
展示Prometheus Pull架构组件协同、四种指标类型特征及告警规则评估与Alertmanager路由机制,交互理解监控告警闭环。
本节配有交互动效(进入视口后自动加载,约 3KB)。
'+modeData.architecture.caption+'
数据模型核心
体检中心的病历系统里,每位病人都有一张唯一的体检表:表头写着“姓名:张三,科室:心内科”,表格里记录着每次测量的血压值和测量时间。Prometheus 的数据模型也是如此——每个被观测的服务都会产生一系列“体检表”,这张表的身份标识由指标名和标签共同决定。
指标名描述“测的是什么”,比如 http_requests_total 表示 HTTP 请求总数,node_memory_usage 表示节点内存使用率。标签则是体检表上的“筛选维度”,比如 method="GET"、status="200"。一个指标名配上完整标签集合,就构成一条独一无二的时间序列——同一病人同一时刻的血压只能有一个读数,同一序列在同一时间戳也只能有一个样本值。
每条时间序列在某个时刻记录的数据叫样本,它由值和时间戳组成。所有同名指标统称为一个指标家族。
理解这个模型,就像记住“体检表 = 病人姓名 + 科室 + 测量时间 + 测量值”。你后续写的所有 PromQL 查询、告警规则,本质上都是在和这样的时间序列打交道。
- 指标名描述观测对象的标准命名,如
http_requests_total,推荐使用基础单位加_total后缀。 - 标签集合一组键值对,如
method="GET",把同一指标拆分成多个业务维度。 - 时间戳与值每个样本是带时间戳的数值,
v(t)表示在时刻t的瞬时值。
四大指标类型
如果体检中心只有一种测量仪器,就分不清“累计心跳次数”和“当前体温”的区别。Prometheus 也为不同场景准备了四种指标类型。
Counter 计数器像健身房门口的打卡机,只增不减,记录服务启动以来累计处理的请求数。Gauge 仪表盘像温度计,可以上下浮动,适合当前 CPU 使用率、在线连接数。Histogram 直方图像体检时的血压测量分布表,把大量样本分桶统计,方便计算“95% 的请求耗时低于多少”。Summary 摘要则在客户端直接算出分位数,像护士在床边统计好后直接填表,服务端拿来就能用。
Counter 的值会越滚越大,直接看它没有意义,所以通常配合 rate() 函数计算每秒变化率。
选对指标类型,就像体检时选对仪器:想看累计量用 Counter,想看瞬时量用 Gauge,想看分布用 Histogram 或 Summary,仪器选错,后面的告警和查询都会变形。
- 计数器末期值
v(t2)当前时刻的累计值。 - 计数器初期值
v(t1)查询窗口起始时刻的累计值。 - 时间差
t2 - t1窗口长度,比如 5 分钟就是 300 秒。 - 速率结果一段时间内的增长量除以时长,得到每秒均值。
采集配置方法
体检中心的护士不能凭空知道病人状态,她们要定期走到病床前测量。Prometheus 也采用“主动拉取”模式:由 Prometheus 服务端按配置的 scrape_interval 访问目标服务的 /metrics 端点,把暴露的指标拉回来存储。
每个被监控的服务就是抓取目标(target),它的 /metrics 端口如同一张体检窗口,应用通过客户端库在这个窗口展示自己的健康数据。scrape_interval 是采集周期,默认 15 秒——这会直接决定数据精度:采集周期越短,数据越精细,但存储和计算压力也越大。
配置方式有两种。静态配置适合固定不变的机器,直接在配置文件中写死 IP 和端口,如同给老病号固定床位。服务发现适合 Kubernetes 这类动态环境,Prometheus 通过 API 自动找到新启动的 Pod,如同护士从住院系统实时获取新入院的病人清单。
这个公式帮你理解为什么窗口查询的曲线是锯齿状——间隔越短,曲线越平滑,但存储开销越大。合理选择采集周期,是监控体系设计的第一个权衡点。
- 时间范围
T_时间范围你想回溯查看的时长,比如 5 分钟。 - 采集周期
T_scrape两次抓取之间的间隔,通常 15 秒。 - 样本点数量窗口内的样本数 = 300 秒 ÷ 15 秒 = 20 个点。
PromQL查询
体检数据存进系统后,主治医生需要一个查询工具:既能查单个病人 24 小时体温曲线,也能统计全科高血压患者比例。PromQL(Prometheus Query Language)就是这套病历查询语言。
先看选择器:http_requests_total{method="GET"} 只选标签 method="GET" 的序列。如果查询“过去 5 分钟的请求速率”,则写 rate(http_requests_total[5m]),把 5 分钟内的所有样本装进“窗口”再算变化率。这种带时间窗口的向量叫范围向量,不带窗口只取最新值的叫即时向量。
PromQL 还提供了丰富的聚合运算:sum by (service) 表示按服务维度汇总,avg 表示求平均。配合 histogram_quantile() 函数,可以从直方图中估算出 P95、P99 分位数,解决“大多数请求很快,但少数请求特别慢”这类长尾问题。
PromQL 就好比医生的查询笔记本:先选病人范围,再加时间窗口,最后做统计运算。写得越准确,诊断结论越可信。
- 范围向量
[5m]把过去 5 分钟内每个样本装进时间窗口。 - rate 函数把 Counter 的区间增长换算成每秒速率,消除累计量影响。
- sum by 聚合按
service标签分组求和,得到全站每秒请求量。
告警规则编写
医生不会时刻盯着每一张体检表,而是依赖“诊断标准”:体温超过 37.3℃ 持续 30 分钟,就需要通知值班医生。Prometheus 的告警规则就是这套诊断标准,写在 YAML 规则文件中。
一条告警规则由三部分组成:告警名称(什么问题)、告警表达式(什么条件)、持续时间 for(条件要持续多久)。for 字段很关键,如果要求 CPU 高负载持续 15 分钟才告警,就能过滤掉偶发的瞬时毛刺,避免“狼来了”式的告警风暴。
表达式就写在 expr 字段里,例如“最近 15 分钟平均负载大于 2”:
规则文件还支持标签模板:通过 {{ $labels.instance }} 把触发告警的实例 IP 动态写入告警信息,这样值班人员收到通知时能看到“哪台机器、哪个服务、当前值是多少”。告警规则本身不负责通知,它更像体检中心的“诊断标准”,一旦触发就把异常病例转交给急诊调度中心——也就是 Alertmanager。
- 指标项
node_load1系统 1 分钟内平均负载的 Gauge 指标。 - 时间窗口
[15m]只取过去 15 分钟的样本参与计算。 - avg_over_time 函数对窗口内的值求平均,抹平瞬间抖动。
- 阈值判断
> 2平均负载超过 2 时满足触发条件。
告警通知路由
急诊调度中心收到大量病例后,不会电话轰炸每个医生,而是按科室、按严重等级分类处理。Prometheus 的 Alertmanager 就是这样的调度中心,负责把告警变成有条理的通知。
它有三大核心能力。分组:把同一时间、同一服务的多个告警合并成一条通知,像把同一病房的病例装进同一个文件夹。抑制:如果已经发出“数据库不可用”,那么由数据库引发的“订单服务异常”这类二级告警就不必再发,避免雪崩式消息。静默:预知到凌晨 2-4 点做版本升级时,可以提前设置静默时间窗口,让这段时间的告警不打扰值班人员。
分组后的告警要发给谁,由路由树决定:路由规则按告警标签匹配不同接收器(邮件、钉钉、Webhook)。告警对象 a 经过分组机构聚合,再被路由到接收器:
Alertmanager 把“告警”和“通知”解耦,就像调度中心不负责诊断,只负责把病人安排到正确的科室。这样一来,即使同一时刻触发 100 条告警,值班人员收到的可能是 3 条经过分组合并的高质量通知。
- 告警对象
aPrometheus 规则触发的某条告警,带有一组标签。 - 分组聚合
group_by按alertname或service标签把相似告警合并。 - 路由分发
route根据路由树选择对应的接收器。 - 接收器
r最终通知渠道,邮件、钉钉、Webhook 等。
可视化与高可用
体检中心再忙,也要在大屏幕上滚动显示各区病患情况,还要保证系统停电了也能继续运转。Prometheus 生态里,Grafana 就是那块大屏幕。把 Prometheus 配置为 Grafana 的数据源后,你就能拖拽绘制折线图、柱状图、热力图,把零散的查询组织成一张张可视化面板(Dashboard),让团队一眼看清系统整体状态。
高可用方面,Prometheus 单实例部署存在单点故障风险。最简单的方案是部署主从副本:两个 Prometheus 实例采集同样的目标,通过 Alertmanager 的 deduplication 机制去重,主挂掉后从节点继续工作。更复杂的是联邦集群:每个数据中心部署一套 Prometheus 采集本地指标,根节点 Prometheus 只负责聚合子节点的结果,像体检中心总部汇总各分院数据,实现分层扩展。
从数据量的角度看,单实例能承载的序列数是有限度的:
当你的服务规模增长到上万条指标序列时,不提前规划副本和联邦,Prometheus 会成为新的故障点。记住:体检中心要服务好病人,也得先保证自己不“生病”。
- 目标数量
M_目标被监控的服务实例数。 - 单目标序列数
S_平均序列数每个实例暴露的指标序列数量。 - 总序列数
C_序列两者乘积决定了存储和查询压力。
AI 应用场景
在线推理服务监控
阶段:模型服务部署与运维
解决的问题:传统方式靠人工 SSH 登录服务器敲 top 命令看 CPU,故障发生时无法回溯历史曲线。用 Prometheus 自动采集推理服务的 QPS、延迟、GPU 利用率后,异常波动一眼可见。
具体案例:某 NLP 服务的模型推理延迟从 50ms 飙升到 800ms。通过 Grafana 查看 P99 延迟曲线,发现高峰时段 GPU 显存接近耗尽,定位到是并发队列堆积,随后通过限流和扩容解决了问题。
告警驱动的自动扩容
阶段:弹性伸缩与成本控制
解决的问题:基于阈值的告警规则让服务在流量突增时自动触发扩容,把“被动救火”变成“主动预防”。
具体案例:推荐系统在大促前流量翻倍,运维配置了规则:container_cpu_usage_ratio > 0.8 持续 5 分钟触发 Webhook,通知 K8s 自动扩容 Pod 副本数,整体响应时间保持稳定。
业务健康度大盘
阶段:业务效果可视化
解决的问题:产品经理也要看模型效果和业务指标,Grafana 大屏把“DAU、推荐点击率、API 错误率”汇聚到同一屏,打破数据孤岛。
具体案例:电商平台搭建“双十一作战大屏”,用 Prometheus 采集网关和推荐模型指标,Grafana 实时渲染每秒订单数、支付成功率、推荐服务 P95 耗时,技术团队和业务团队共享同一份数据结论。
Python 演示
from prometheus_client import start_http_server, Counter, Gauge, Histogramimport randomimport time REQUEST_COUNT = Counter("web_requests_total", "累计处理的请求数")REQUESTS_IN_PROGRESS = Gauge("web_requests_in_progress", "当前正在处理的请求数")REQUEST_DURATION = Histogram("web_request_duration_seconds", "请求耗时分布") def simulate_request(): REQUESTS_IN_PROGRESS.inc() time.sleep(random.uniform(0.02, 0.15)) duration = random.uniform(0.05, 0.8) REQUEST_COUNT.inc() REQUEST_DURATION.observe(duration) REQUESTS_IN_PROGRESS.dec() if __name__ == "__main__": start_http_server(8080) print("metrics endpoint: http://localhost:8080/metrics") while True: simulate_request()
/metrics 端点,供 Prometheus 抓取模拟的业务指标。Counter("web_requests_total", ...)定义只增不减的计数器,记录累计请求数Gauge("web_requests_in_progress", ...)定义上下浮动的仪表盘指标,记录并发数Histogram("web_request_duration_seconds", ...)定义直方图,自动分桶记录请求耗时分布REQUEST_COUNT.inc()每模拟一次请求,计数器加 1REQUEST_DURATION.observe(duration)把耗时样本投入直方图start_http_server(8080)启动指标暴露服务,等 Prometheus 来拉取while True:无限循环模拟业务流量的持续产生
常见误区
误区我以为 `web_requests_total` 的值直接展示在 Grafana 上就能反映请求量,结果曲线像爬山一样一直往上走,根本看不出当前速度。
正解Counter 是累计量,它的绝对大小没有意义,必须用 rate() 或 increase() 转换为速率后再画图。就像看一辆车跑了多远没用,要看它的速度(每秒跑多少米)才有意义。
为何常见:初学者容易把“数值”和“速率”混为一谈,忽视了 Counter 的设计初衷是累计。
误区我配置告警时 `for: 0s`,觉得越灵敏越好,结果一个抖动就让告警疯狂轰炸,值班手机响个不停。
正解for 表示条件要持续多久才触发告警,应该根据业务容忍度设置,比如 CPU 高负载持续 10 分钟才告警。它像医生的诊断标准:一次咳嗽不能说明得了感冒,连续打喷嚏加发热才值得警惕。
为何常见:新手追求“即时告警”,却没考虑瞬时抖动导致的误报率。
误区我觉得 Histogram 和 Summary 都能算分位数,那它们应该可以相互替代吧?
正解Summary 在客户端直接算好分位数,无法在服务端跨实例聚合;Histogram 只存分桶计数,可以在 Prometheus 端用 histogram_quantile() 灵活计算任意分位数。两者类比分桶存储的原材料和在窗口直接卖半成品的区别。
为何常见:两者都能回答“P95 是多少”,导致初学者忽略了聚合能力的本质差异。
误区告警信息只写“服务异常”,没有实例 IP 和当前数值,值班人员收到通知后还要手动查是哪个机器出了问题。
正解用 {{ $labels.instance }}、{{ $value }} 把实例地址和当前值动态带入告警信息,让通知自带上下文。体检报告若只写“指标异常”而不写病人姓名和数值,医生根本没法干活。
为何常见:规则文件写完后很少有人主动测一条真实告警,等到故障时才暴露信息不完整的问题。
误区我写了一个脚本把指标 POST 到 Prometheus 服务器,结果发现它根本不接收,以为 Prometheus 坏掉了。
正解Prometheus 是拉取模式,它主动去访问目标暴露的 /metrics 端点。就像医院规定护士去病房测体温,而不是让病人自己跑来检验科交数据。Push 模式可以用 Pushgateway 做特殊补充,但常规采集必须由服务端发起。
为何常见:很多监控系统(如 StatsD)是推送式的,带着固有思维迁移就踩了坑。
面试题
Q1. Prometheus 采集指标使用哪种模式?
- A.服务端主动拉取← 正确答案
- B.客户端主动推送
- C.双向建立长连接
- D.定期复制日志文件
答案:A。Prometheus 使用服务端主动拉取(Pull)模式,定时访问目标的 /metrics 端点。就像体检中心护士主动到病房测量,而不是病人跑去找检验科。虽然 Pushgateway 支持推送,但那是特殊补充,不是主模式。
Q2. 网站接口“累计被调用总次数”适合用哪种指标类型?
- A.Counter 积分递增类← 正确答案
- B.Gauge 仪表实时类
- C.Histogram 分布统计类
- D.Summary 分位摘要类
答案:A。累计调用次数只会不断增长,符合 Counter「只增不减」的特性。Gauge 适合可上下浮动的当前值(如 CPU 使用率),Histogram 和 Summary 适合耗时分布,都不匹配“累计总量”的语义。
Q3. 下面哪条表达式能计算每秒请求速率?
- A.rate(http_requests_total[5m])← 正确答案
- B.sum(http_requests_total[5m])
- C.avg(http_requests_total[5m])
- D.count(http_requests_total[5m])
答案:A。rate() 专门把 Counter 在时间窗口内的增长量换算为每秒速率。sum、avg、count 都是聚合函数,无法直接作用于范围向量计算速率,就像只统计病人总数无法得知心跳速度。
Q4. 关于 Prometheus 时间序列的说法正确的是?
- A.指标名加标签构成唯一序列← 正确答案
- B.指标名不同也可互相合并
- C.标签值只能使用固定数字
- D.标签数量越多查询越快
答案:A。同一指标名配合不同标签值,形成各自独立的时间序列,如 method="GET" 和 method="POST" 是两条序列。标签是键值对、可为任意字符串;标签越多基数越高,查询反而更慢。
Q5. Histogram 与 Summary 的核心差异是什么?
- A.直方图可在服务端聚合求分位← 正确答案
- B.摘要可以在服务端灵活聚合
- C.直方图与摘要可以完全替代
- D.摘要必须依赖外部存储不可用
答案:A。Histogram 在客户端只做分桶统计,服务端能用 histogram_quantile() 跨实例聚合求 P95/P99;Summary 在客户端就算好分位数,无法再跨实例聚合。这就像医院把原始测量单汇总给总院分析,而不是让每个分院各自下结论。
Q6. 下列哪些指标类型适合度量“请求耗时”这类分布型数据?
- A.耗时分布用 Histogram← 正确答案
- B.耗时分布用 Summary← 正确答案
- C.耗时分布用 Counter
- D.耗时分布用 Gauge
答案:AB。Histogram 和 Summary 都是为分位数、分布类数据设计的。Counter 只增不减无法体现波动分布,Gauge 只能记录当前值,无法还原耗时长尾。项目里通常优先用 Histogram,因为它支持服务端聚合。
Q7. Alertmanager 的“分组”主要解决什么问题?
- A.把同类告警合并一起发送← 正确答案
- B.把所有告警永久沉默处理
- C.替代 Prometheus 存储时序
- D.自动修复故障并恢复服务
答案:A。分组是按 group_by 标签把相似告警聚合成一条通知,避免 100 条告警轰炸值班手机。它不负责存储、不修补故障、更不是永久静默,而是减少通知噪音的手段。
Q8. 以下哪些属于 Prometheus 高可用常用方案?
- A.主从副本双实例部署← 正确答案
- B.联邦集群分层聚合采集← 正确答案
- C.Alertmanager 告警去重兜底← 正确答案
- D.Grafana 双活替代抓取存储
答案:ABC。主从副本做冗余,联邦集群做横向扩展,Alertmanager 的 deduplication 可让多副本只发出一次告警。Grafana 只是可视化工具,不具备指标抓取和存储能力,无法替代 Prometheus 本体。
Q9. 告警规则中 for: 5m 字段表示?
- A.条件需持续 5 分钟才触发← 正确答案
- B.告警最多发送 5 次通知
- C.指标每隔 5 分钟采集一次
- D.查询表达式超时时间设为 5 分钟
答案:A。for 表示告警条件要连续满足多久才真正触发,用于过滤瞬时抖动。它不控制通知次数、采集周期或超时。就像医生要求“发烧超过 1 小时才用药”,而不是一量体温偏高就立刻下诊断。
Q10. Prometheus 可以通过哪些方式发现抓取目标?
- A.Kubernetes API 自动发现← 正确答案
- B.Consul 注册中心动态发现← 正确答案
- C.静态配置直接写 IP 端口← 正确答案
- D.修改 Prometheus 源代码支持
答案:ABC。服务发现机制包括 Kubernetes、Consul、DNS 等,静态配置也是标准方式之一,适合固定机器。修改源代码显然不是合理的运维做法,而且每次版本升级都要重写,完全不可维护。