3、Prometheus入门:架构、数据模型、指标类型(Counter、Gauge、Histogram、Summary)、Pull模型

Prometheus 是云原生可观测性领域的基石系统,其设计哲学与传统的“推模型”监控系统(如 Zabbix、Nagios)截然不同。理解 Prometheus 的架构、数据模型与指标类型,是构建可靠监控体系的前提。本章将从核心组件入手,剖析其 Pull 模型的优势,并通过实战案例演示如何设计高效的指标采集任务。

Prometheus 核心架构与数据流 采集目标 (Targets) /metrics 端点 Exporter / App (Pod, VM, MySQL...) 服务发现 K8s / Consul / DNS 动态更新目标列表 Prometheus Server Retrieval (拉取) TSDB (时序存储) HTTP API / PromQL Alerting Rules (本地磁盘存储) Pull (HTTP) 目标列表 Alertmanager 分组/抑制/静默 通知: 邮件/钉钉/Webhook Grafana Dashboard 可视化 PromQL 查询 远程存储 (可选) Thanos / VictoriaMetrics / S3 数据模型 metric_name{label="value"} Counter / Gauge / Histogram

上图展示了 Prometheus 的核心架构。与直觉相反,Prometheus 并不主动接收数据,而是通过 HTTP 周期性拉取(Pull)暴露了 /metrics 端点的目标。这种设计使得监控系统的健康状态不依赖于被监控对象的推送能力,且便于在本地进行故障诊断。接下来,我们将深入拆解其数据模型与四种核心指标类型。

3.1 核心原理:Pull 模型与组件协作

Prometheus 的 Pull 模型是其架构的灵魂。Prometheus Server 根据配置的 scrape_configs,定期(默认 15s)向目标地址发起 HTTP GET 请求,路径通常为 /metrics。目标端返回的文本格式数据会被解析并存储至内置的 TSDB(时间序列数据库)。

该模型带来三个关键优势:

  • 健康检查简单:只要 Prometheus 能成功拉取,说明目标进程存活且 HTTP 服务正常;拉取失败则立即触发告警。
  • 易于本地调试:使用 curl http://localhost:9100/metrics 即可查看原始指标,无需额外工具。
  • 避免推送风暴:在微服务规模较大时,推送模型容易因网络抖动导致数据堆积,而 Pull 模型由服务端控制节奏。

组件协作流程如下:服务发现(如 Kubernetes SD)动态维护待拉取的目标列表;Prometheus Server 的 Retrieval 模块根据该列表执行拉取;拉取到的样本经过有效性校验后写入 TSDB;PromQL 引擎负责查询与聚合;Alerting 模块基于规则生成告警并推送至 Alertmanager。

经验提醒:对于短生命周期任务(如 Kubernetes Job),Pull 模型可能错过采集窗口。此时应使用 Pushgateway 作为临时中转,但需注意其单点故障风险,并定期清理过期的指标。

3.2 数据模型:时间序列与标签

Prometheus 的数据模型可以概括为:时间序列 = 指标名 + 标签集合 + 时间戳样本值。每个唯一组合(指标名+标签键值对)定义了一条时间序列。

组成部分 示例 说明
指标名 http_requests_total 必须符合 [a-zA-Z_:][a-zA-Z0-9_:]*,通常包含单位(_total、_seconds)
标签 method="GET", code="200" 用于维度过滤与聚合,是 PromQL 查询的核心
样本 timestamp value 毫秒级时间戳 + float64 数值

标签设计是监控建模的关键。例如,http_requests_total{method="POST", code="500"} 与 http_requests_total{method="GET", code="200"} 是两条不同的序列。高基数标签(如用户 ID、请求 URL)会导致序列数量爆炸,必须避免。

3.3 指标类型:Counter、Gauge、Histogram、Summary

Prometheus 客户端库提供四种核心指标类型,每种类型服务于不同的观测场景。

3.3.1 Counter(计数器)

Counter 只增不减(除非系统重启)。它适用于累计型指标,如请求总数、错误次数、任务完成数。在 PromQL 中,通常使用 rate() 或 increase() 函数计算每秒速率或增量。

# 定义示例
http_requests_total{method="GET", code="200"} 1027
# 查询每秒速率
rate(http_requests_total[5m])

3.3.2 Gauge(仪表盘)

Gauge 表示可增可减的瞬时值,如当前内存使用量、CPU 温度、在线用户数。它直接反映当前状态,无需额外计算。

# 当前内存使用字节数
node_memory_MemAvailable_bytes 838860800

3.3.3 Histogram(直方图)

Histogram 用于观测值的分布情况,例如请求延迟或响应体大小。它通过 累积桶(cumulative bucket) 记录样本落在每个桶中的计数,同时提供总和与总计数。

# HELP http_request_duration_seconds 请求延迟
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.1"} 120
http_request_duration_seconds_bucket{le="0.5"} 450
http_request_duration_seconds_bucket{le="1"} 700
http_request_duration_seconds_bucket{le="+Inf"} 800
http_request_duration_seconds_sum 320.5
http_request_duration_seconds_count 800

通过 histogram_quantile() 函数可以计算分位数(如 P99),但需注意桶边界的选择会影响精度。

3.3.4 Summary(摘要)

Summary 在客户端直接计算分位数(如 0.5、0.9、0.99),并暴露为独立的指标。它不依赖服务端计算,但无法跨实例聚合。

# TYPE http_request_duration_seconds summary
http_request_duration_seconds{quantile="0.5"} 0.23
http_request_duration_seconds{quantile="0.9"} 0.87
http_request_duration_seconds_sum 320.5
http_request_duration_seconds_count 800
类型 适用场景 聚合能力 分位数计算
Counter 累计计数(请求数、错误数) 支持 Sum 不适用
Gauge 瞬时值(温度、内存) 支持 Avg/Max/Min 不适用
Histogram 延迟、大小分布 支持 Sum/Count 服务端计算(近似)
Summary 延迟分布(客户端预计算) 不支持跨实例聚合 客户端计算(精确)

3.4 案例拆解:为订单服务设计监控指标

假设我们有一个订单服务,需要监控其健康状态与性能。以下是具体的实施过程。

背景与目标:该服务部署在 Kubernetes 集群中,日订单量约 50 万。我们需要回答三个问题:QPS 是多少?P99 延迟是否达标?错误率是否上升?

关键输入数据:服务使用 Go 语言编写,已集成 Prometheus 客户端库。

操作步骤:

  1. 定义指标:在代码中创建 Counter 与 Histogram。
var (
    ordersTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "orders_created_total",
            Help: "Total number of created orders.",
        },
        []string{"status"}, // status: "success", "failed"
    )
    orderDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "order_create_duration_seconds",
            Help:    "Time taken to create an order.",
            Buckets: prometheus.DefBuckets, // 默认桶
        },
        []string{},
    )
)
  1. 埋点:在创建订单的函数中记录耗时与结果。
start := time.Now()
err := createOrder()
duration := time.Since(start).Seconds()
orderDuration.WithLabelValues().Observe(duration)
if err != nil {
    ordersTotal.WithLabelValues("failed").Inc()
} else {
    ordersTotal.WithLabelValues("success").Inc()
}
  1. 配置抓取:在 Prometheus 配置中增加 Job。
scrape_configs:
  - job_name: 'order-service'
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_label_app]
        regex: 'order-service'
        action: keep
    metrics_path: /metrics
    scrape_interval: 15s
  1. 验证与查询:在 Grafana 中创建面板,使用 PromQL 查询。
# QPS
sum(rate(orders_created_total[5m]))
# P99 延迟
histogram_quantile(0.99, sum(rate(order_create_duration_seconds_bucket[5m])) by (le))
# 错误率
sum(rate(orders_created_total{status="failed"}[5m])) / sum(rate(orders_created_total[5m]))

结果对比:监控上线后,发现 P99 延迟在高峰期从 0.8s 飙升至 2.1s。通过 Histogram 桶分布定位到是数据库连接池瓶颈,而非代码逻辑问题。优化连接池配置后,P99 降至 0.6s。

风险提示:不要为每个用户或订单 ID 创建标签,这会导致基数爆炸。上述案例中仅使用 status 作为标签,避免了高基数问题。

3.5 常见误区与总结复盘

在实际使用中,以下误区需要特别留意:

  • Counter 误用 Gauge 函数:对 Counter 使用 avg() 没有意义,应使用 rate() 或 increase()。
  • Histogram 桶边界设置不当:桶过少导致分位数误差大;桶过多增加开销。建议根据业务经验设置,如延迟指标可设为 [0.01, 0.05, 0.1, 0.5, 1, 2.5, 5]。
  • 忽略标签一致性:同一指标在不同实例上标签不一致会导致查询结果异常。例如,一个实例有 code="200",另一个实例没有该标签,则 sum() 会出错。
  • Summary 跨实例聚合:Summary 的分位数在客户端计算,不能直接相加。若需要全局分位数,应使用 Histogram。