2、Prometheus基础:掌握Prometheus的数据模型、指标类型(Counter、Gauge、Histogram、Summary)、Pull模型与Pushgateway的适用场景
在容器化推理服务的可观测性体系中,Prometheus 是事实上的标准监控方案。要设计一套高质量的指标采集系统,首先必须深刻理解 Prometheus 的核心设计哲学:指标(Metric)是带标签(Label)的时间序列数据。本章将围绕数据模型、四种核心指标类型、Pull 与 Push 两种采集模式的适用边界展开,帮助你建立从“理解概念”到“设计采集方案”的完整认知链路。
2.1 数据模型:时间序列与标签体系
Prometheus 将所有监控数据统一建模为带标签的时间序列。一条时间序列由指标名称(Metric Name)和一组键值对标签(Label Set)唯一标识。指标名称用于描述被监控对象的一般特征,例如 http_requests_total 表示 HTTP 请求总数;标签则用于区分同一指标下的不同维度,例如 method="POST"、status="500"。每个时间序列由一系列样本点组成,每个样本点包含一个 float64 数值和一个毫秒精度的时间戳。
inference_latency_seconds{model="resnet50", version="v2"} 与 inference_latency_seconds{model="resnet50", version="v1"} 是两个不同的序列。在设计指标时,标签的基数(Cardinality)必须严格控制——高基数标签(如用户 ID、请求 ID)会导致序列数量爆炸,压垮存储与查询。
判断方法:在设计新指标时,先问三个问题:这个指标是计数型还是测量型?需要哪些标签来区分不同维度?标签的取值集合是否有限且可控?例如,推理服务的模型版本标签取值有限(v1、v2、v3),适合作为标签;而客户端 IP 取值无限,不适合作为标签。
| 数据模型要素 | 说明 | 示例 |
|---|---|---|
| 指标名称 | 描述被监控对象,需符合 [a-zA-Z_:][a-zA-Z0-9_:]* | inference_requests_total |
| 标签 | 键值对,用于维度区分 | model="yolov5", device="cuda:0" |
| 样本 | 时间戳 + 数值 | 1680000000000 → 1024 |
| 序列 | 指标名 + 标签集唯一确定 | inference_requests_total{model="yolov5"} |
案例拆解:假设你为推理服务定义了一个指标 inference_batch_size,标签为 model 和 instance。如果模型数量为 5,实例数量为 10,则最多产生 50 条时间序列。但如果误将 request_id 作为标签,每处理一个请求就产生一条新序列,几分钟内就会产生数百万条序列,导致 Prometheus 内存溢出。因此,标签设计必须遵循“低基数”原则。
model_a_requests、model_b_requests。正确做法是使用同一个指标名 inference_requests_total,通过标签 model="a" 和 model="b" 区分。这样便于使用 PromQL 进行跨模型聚合与对比。
2.2 四种指标类型:Counter、Gauge、Histogram、Summary
Prometheus 客户端库提供四种核心指标类型,每种类型对应不同的数据语义和查询模式。选择正确的类型是采集方案设计的关键一步。
2.2.1 Counter(计数器)
核心原理:Counter 是单调递增的计数器,只增不减(除非系统重启)。典型场景包括:请求总数、错误总数、任务完成数。Counter 的绝对值通常没有直接意义,需要配合 rate() 或 increase() 函数计算速率或增量。
# 定义 Counter 指标
inference_requests_total = Counter('inference_requests_total', 'Total inference requests')
# 使用
inference_requests_total.labels(model='resnet50').inc()
判断方法:如果指标的值“只会增加,不会减少”,且你关心的是“在一段时间内发生了多少次”,则选择 Counter。例如,累计推理请求数、累计超时次数。
2.2.2 Gauge(仪表盘)
核心原理:Gauge 表示可增可减的瞬时值,反映当前状态。典型场景包括:当前 GPU 利用率、当前队列长度、当前内存使用量。Gauge 可以直接查询当前值,也可以使用 delta() 计算一段时间内的变化量。
gpu_utilization = Gauge('gpu_utilization_percent', 'GPU utilization', ['device'])
gpu_utilization.labels(device='cuda:0').set(87.5)
判断方法:如果指标的值“上下波动”,且你关心的是“当前是多少”,则选择 Gauge。例如,当前推理服务的工作线程数、当前温度。
2.2.3 Histogram(直方图)
核心原理:Histogram 用于观测值的分布统计,例如请求延迟、响应大小。它在客户端计算累计计数(_bucket)、总和(_sum)和总次数(_count),服务端通过 histogram_quantile() 函数计算分位数。Histogram 需要预先定义桶(Bucket)边界。
inference_latency = Histogram('inference_latency_seconds', 'Inference latency',
buckets=[0.01, 0.05, 0.1, 0.5, 1.0, 2.5, 5.0])
inference_latency.observe(0.23)
判断方法:如果你需要计算“P95 延迟”或“延迟分布”,且无法在客户端预知分位数,则选择 Histogram。桶边界的设计需要根据业务经验调整,桶太粗则分位数不准确,桶太细则增加开销。
2.2.4 Summary(摘要)
核心原理:Summary 在客户端直接计算分位数,并暴露为 _quantile 系列指标。它不依赖预定义桶,但无法在服务端进行聚合计算。Summary 适用于“已知需要哪些分位数”且“单实例部署”的场景。
inference_latency_summary = Summary('inference_latency_summary_seconds', 'Inference latency summary',
quantiles=[0.5, 0.9, 0.99])
inference_latency_summary.observe(0.23)
判断方法:如果客户端明确知道需要哪些分位数(如 0.5、0.9、0.99),且不需要跨实例聚合分位数,则选择 Summary。但要注意,Summary 无法通过 PromQL 进行分位数聚合,多实例部署时只能使用平均值。
| 类型 | 语义 | 典型场景 | 查询方式 | 聚合能力 |
|---|---|---|---|---|
| Counter | 只增不减 | 请求总数、错误数 | rate(), increase() | 支持 sum() |
| Gauge | 可增可减 | GPU 利用率、队列长度 | 直接查询 | 支持 avg(), max() |
| Histogram | 分布统计 | 延迟、响应大小 | histogram_quantile() | 支持 sum() 后重新计算分位数 |
| Summary | 客户端分位数 | 单实例已知分位数 | 直接查询分位数 | 不支持分位数聚合 |
案例拆解:某推理平台需要监控模型推理延迟。团队最初使用 Summary 并配置了 0.5、0.9、0.99 分位数。后来服务扩展到 10 个实例,发现无法通过 PromQL 计算所有实例的 P99 延迟。切换到 Histogram 后,使用 histogram_quantile(0.99, sum(rate(inference_latency_seconds_bucket[5m])) by (le)) 成功计算出全局 P99。这个案例说明:在多实例场景下,Histogram 是更优选择。
2.3 Pull 模型与 Pushgateway 的适用场景
Prometheus 默认采用 Pull 模型:Prometheus 服务器主动从目标端点(如 /metrics)拉取指标。这种模式要求目标服务必须可被 Prometheus 网络访问,且持续运行。对于容器化推理服务,Pull 模型是首选方案。
核心原理:Pull 模型通过服务发现机制(如 Kubernetes 的 kubernetes_sd_configs)动态获取目标列表,周期性拉取指标。这种模式天然支持健康检查——如果目标无法访问,Prometheus 会标记该实例为 DOWN,从而反映服务可用性。
# prometheus.yml 配置示例
scrape_configs:
- job_name: 'inference-service'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: 'inference'
action: keep
metrics_path: '/metrics'
scrape_interval: 15s
判断方法:当目标服务是长期运行的进程(如推理服务、Web 服务),且网络拓扑允许 Prometheus 主动访问时,应使用 Pull 模型。Pull 模型的优势在于:集中控制采集频率、目标状态可观测、天然支持水平扩展。
Pushgateway 的适用场景:Pushgateway 是 Prometheus 生态中的一个辅助组件,允许目标服务主动推送指标到 Pushgateway,再由 Prometheus 从 Pushgateway 拉取。它主要解决两类问题:
- 短期任务(Short-lived Jobs):例如批处理任务、定时训练任务,任务运行时间短,Prometheus 来不及拉取指标。
- 网络隔离环境:目标服务位于防火墙后,Prometheus 无法直接访问,但目标服务可以主动外连。
# 推送指标到 Pushgateway
echo "inference_job_duration_seconds 42.5" | curl --data-binary @- http://pushgateway:9091/metrics/job/inference_batch
案例拆解:某团队使用 Kubernetes CronJob 运行每日模型评估任务。任务运行约 10 分钟,结束后 Pod 被销毁。由于 Prometheus 的采集周期为 15 秒,任务可能在两次采集之间结束,导致指标丢失。解决方案是:任务在结束时将结果推送到 Pushgateway,Prometheus 从 Pushgateway 拉取指标。这样即使任务已结束,指标仍然保留。
总结复盘:Pull 模型与 Pushgateway 的选择本质上是“谁主动、谁被动”的问题。Pull 模型将控制权集中在 Prometheus,适合稳态服务;Pushgateway 将控制权交给目标服务,适合短生命周期任务。在设计采集方案时,应遵循“默认 Pull,例外 Push”的原则。
PodMonitor 或 ServiceMonitor 来声明 Pull 采集规则。对于批处理任务,使用 Pushgateway 并设置合理的 pushgateway_metrics_ttl 或定期清理机制,避免指标堆积。