跳到主要内容

Prometheus Learning Notes

最后更新于:

Prometheus 中文学习笔记

基于Prometheus官网文档,结合Prometheus 3.5 & Alertmanager 0.27整理而成。


一、Introduction (简介)

1.1 Overview (概览)

什么是 Prometheus?

Prometheus 是一个开源的系统监控和告警工具包,最初由 SoundCloud 构建。自 2012 年诞生以来,许多公司和组织都采用了 Prometheus,该项目拥有非常活跃的开发者和用户社区。2016 年,Prometheus 作为第二个托管项目(继 Kubernetes 之后)加入了云原生计算基金会(CNCF)。

核心特性:

  • Prometheus 以时间序列数据的形式收集和存储指标
  • 指标信息与记录时的时间戳一起存储
  • 可选的键值对称为标签(Labels)

主要特性

  1. 多维数据模型

    • 时间序列数据通过指标名称和键值对(标签)进行标识
  2. 灵活的查询语言 PromQL

    • 利用维度性进行强大的数据查询
  3. 不依赖分布式存储

    • 单个服务器节点是自治的
  4. 基于 HTTP 的 Pull 模型

    • 通过 HTTP 拉取方式采集时间序列数据
  5. 支持推送时间序列

    • 通过中间网关(Push Gateway)支持短生命周期的任务
  6. 服务发现

    • 通过服务发现或静态配置发现目标
  7. 多种图形和仪表板支持

什么是指标(Metrics)?

指标是数值测量的通俗术语。时间序列是指随时间变化的记录。

应用场景示例:

  • Web 服务器: 请求时间
  • 数据库: 活跃连接数、活跃查询数
  • 应用性能: 当请求数量高时,应用可能变慢,通过请求计数指标可以确定原因并增加服务器数量

组件架构

Prometheus 生态系统由多个组件组成,其中许多是可选的:

组件说明
Prometheus Server抓取和存储时间序列数据的主服务器
Client Libraries用于为应用程序代码添加监控埋点的客户端库
Push Gateway支持短生命周期任务的推送网关
Exporters用于 HAProxy、StatsD、Graphite 等服务的专用导出器
Alertmanager处理告警的告警管理器
Support Tools各种支持工具

大多数 Prometheus 组件使用 Go 语言编写,易于构建和部署为静态二进制文件。

架构图

graph TD A[Prometheus Server] -->|scrape| B[Jobs/Exporters] A -->|scrape| C[Short-lived Jobs] C -->|push| D[Push Gateway] A -->|scrape| D A --> E[TSDB 本地存储] A -->|PromQL| F[Alertmanager] F -->|通知| G[Email/Pagerduty/etc] A -->|HTTP API| H[Grafana] A -->|HTTP API| I[API Clients] J[Service Discovery] -.发现.-> A K[Static Config] -.配置.-> A

工作流程:

  1. Prometheus 从被监控的任务中抓取指标,可以直接抓取,也可以通过推送网关抓取短生命周期的任务
  2. 将所有抓取的样本存储在本地
  3. 对数据运行规则以聚合和记录新的时间序列,或生成告警
  4. 使用 Grafana 或其他 API 消费者可视化收集的数据

适用场景

✅ 适合使用 Prometheus 的场景:

  • 记录纯数值时间序列数据
  • 面向机器的监控
  • 高度动态的面向服务的架构监控
  • 微服务环境(多维数据收集和查询是其特别优势)
  • 需要高可靠性的场景(每个 Prometheus 服务器是独立的,不依赖网络存储或其他远程服务)

❌ 不适合使用 Prometheus 的场景:

  • 需要 100% 准确性的场景(如按请求计费)
  • Prometheus 更注重可靠性而非绝对准确性
  • 对于需要详细和完整数据的计费系统,建议使用其他系统进行数据收集和分析

1.2 First Steps (入门步骤)

本节将指导你完成 Prometheus 的安装、配置和监控第一个资源的过程。

下载 Prometheus

  1. 下载适合你平台的最新版本 Prometheus
  2. 解压文件:
1tar xvfz prometheus-*.tar.gz
2cd prometheus-*
  1. Prometheus 服务器是一个名为 prometheus 的单一二进制文件(Windows 上为 prometheus.exe
  2. 查看帮助信息:
1./prometheus --help

配置 Prometheus

Prometheus 使用 YAML 格式进行配置。下载包中包含一个示例配置文件 prometheus.yml

配置文件示例:

 1global:
 2  scrape_interval:     15s    # 全局抓取间隔
 3  evaluation_interval: 15s    # 规则评估间隔
 4
 5rule_files:
 6  # - "first.rules"
 7  # - "second.rules"
 8
 9scrape_configs:
10  - job_name: prometheus
11    static_configs:
12      - targets: ['localhost:9090']

配置文件三大块:

配置块说明
globalPrometheus 服务器的全局配置
rule_files指定 Prometheus 服务器要加载的规则文件位置
scrape_configs控制 Prometheus 监控哪些资源

配置详解:

  1. global 块

    • scrape_interval: 控制 Prometheus 抓取目标的频率(默认 15 秒)
    • evaluation_interval: 控制 Prometheus 评估规则的频率
  2. rule_files 块

    • 指定规则文件位置
    • Prometheus 使用规则创建新时间序列和生成告警
  3. scrape_configs 块

    • 定义监控目标
    • 默认配置包含一个名为 prometheus 的任务,抓取 Prometheus 服务器自身暴露的时间序列数据
    • 默认抓取路径: http://localhost:9090/metrics

启动 Prometheus

使用配置文件启动 Prometheus:

1./prometheus --config.file=prometheus.yml

启动后,可以访问以下地址:

  • 状态页面: http://localhost:9090
  • 指标端点: http://localhost:9090/metrics

使用表达式浏览器

  1. 访问表达式浏览器: http://localhost:9090/graph
  2. 选择 “Graph” 标签页中的 “Table” 视图

查询示例:

1# 查询 Prometheus 服务器处理的 /metrics 请求总数
2promhttp_metric_handler_requests_total
3
4# 仅查询 HTTP 状态码 200 的请求
5promhttp_metric_handler_requests_total{code="200"}
6
7# 计算返回的时间序列数量
8count(promhttp_metric_handler_requests_total)

使用图形界面

访问 http://localhost:9090/graph 并使用 “Graph” 标签页。

示例:绘制每秒 HTTP 请求速率

1rate(promhttp_metric_handler_requests_total{code="200"}[1m])

监控其他目标

要更好地了解 Prometheus 的功能,推荐探索其他导出器的文档,例如:

  • 使用 Node Exporter 监控 Linux 或 macOS 主机指标

1.3 Comparison (与其他监控系统对比)

Prometheus vs. Graphite

范围(Scope)

  • Graphite: 被动的时间序列数据库,具有查询语言和图形功能,其他关注点由外部组件处理
  • Prometheus: 完整的监控和趋势系统,包括内置的主动抓取、存储、查询、图形和告警功能

数据模型(Data Model)

特性GraphitePrometheus
指标命名点分隔的组件(隐式编码维度)显式的键值对标签
示例stats.api-server.tracks.post.500 -> 93api_server_http_requests_total{method="POST",handler="/tracks",status="500",instance="sample1"} -> 34
维度支持隐式显式,易于过滤、分组和匹配

存储(Storage)

  • Graphite: 使用 Whisper 格式在本地磁盘存储(RRD 风格数据库),样本需按固定间隔到达
  • Prometheus: 每个时间序列一个本地文件,允许以任意间隔存储样本,新样本简单追加,可长期保留旧数据

总结

  • Prometheus 优势: 更丰富的数据模型和查询语言,更易于运行和集成
  • Graphite 优势: 如果需要集群化解决方案和长期历史数据存储

Prometheus vs. InfluxDB

范围(Scope)

  • 需要将 Kapacitor 与 InfluxDB 一起考虑,以解决与 Prometheus + Alertmanager 相同的问题空间
  • InfluxDB 提供连续查询(continuous queries),相当于 Prometheus 的记录规则
  • Kapacitor 的范围类似于 Prometheus 记录规则、告警规则和 Alertmanager 的通知功能

数据模型/存储

特性InfluxDBPrometheus
标签Tags(第一级)+ Fields(第二级)Labels
时间戳精度纳秒级毫秒级
数据类型float64, int64, bool, stringfloat64(有限的字符串支持)
存储方式LSM 树变体 + WAL,按时间分片每个时间序列一个仅追加文件

架构(Architecture)

  • Prometheus: 服务器彼此独立运行,仅依赖本地存储
  • InfluxDB 开源版: 类似 Prometheus
  • InfluxDB 商业版: 分布式存储集群,存储和查询由多个节点处理

优劣对比

InfluxDB 更优:

  • ✅ 事件日志场景
  • ✅ 商业版提供集群化支持,更适合长期数据存储
  • ✅ 副本之间的最终一致性视图

Prometheus 更优:

  • ✅ 主要用于指标监控
  • ✅ 更强大的查询语言、告警和通知功能
  • ✅ 更高的图形和告警可用性和正常运行时间

Prometheus vs. OpenTSDB

范围(Scope)

  • 与 Graphite 的范围差异相同

数据模型(Data Model)

  • OpenTSDB 的数据模型几乎与 Prometheus 相同
  • 时间序列由一组任意键值对标识(OpenTSDB 的 tags = Prometheus 的 labels)
  • 差异:
    • Prometheus 允许标签值中使用任意字符,OpenTSDB 限制更多
    • OpenTSDB 缺少完整的查询语言,仅允许通过 API 进行简单聚合和数学运算

存储(Storage)

  • OpenTSDB 基于 Hadoop 和 HBase 实现
  • 易于水平扩展,但需要接受运行 Hadoop/HBase 集群的复杂性
  • Prometheus 初期运行更简单,但超过单节点容量后需要显式分片

总结

  • Prometheus 优势: 更丰富的查询语言,能处理更高基数的指标,是完整监控系统的一部分
  • OpenTSDB 优势: 如果已经运行 Hadoop 并且重视长期存储

Prometheus vs. Nagios

范围(Scope)

  • Nagios: 起源于 1990 年代,主要基于脚本退出码进行告警(“checks”)
  • 支持单个告警的静默,但无分组、路由或去重功能

数据模型(Data Model)

  • Nagios 是基于主机的
  • 每个主机可以有一个或多个服务,每个服务可以执行一次检查
  • 没有标签概念或查询语言

存储(Storage)

  • Nagios 本身没有存储,只存储当前检查状态
  • 可以通过插件存储数据用于可视化

架构(Architecture)

  • Nagios 服务器是独立的
  • 所有检查配置通过文件完成

总结

  • Nagios 适用: 小型和/或静态系统的基本监控,黑盒探测足够的场景
  • Prometheus 适用: 白盒监控,或具有动态/云环境的场景

Prometheus vs. Sensu

范围(Scope)

  • Sensu: 开源监控和可观测性管道,专注于处理和告警可观测性数据流(事件流)
  • 提供事件过滤、聚合、转换和处理的可扩展框架
  • Sensu 的事件处理能力类似于 Prometheus 告警规则和 Alertmanager 的范围

数据模型(Data Model)

  • Sensu 事件通过实体名称、事件名称和可选的键值元数据(“labels” 或 “annotations”)进行标识
  • 事件有效载荷可能包含一个或多个指标点(JSON 对象:name, tags, timestamp, value)

存储(Storage)

  • Sensu 将当前和最近的事件状态信息及实时清单数据存储在嵌入式数据库(etcd)或外部 RDBMS(PostgreSQL)中

架构(Architecture)

  • Sensu 部署的所有组件都可以集群化以实现高可用性和提高事件处理吞吐量

优劣对比

Sensu 更优:

  • ✅ 收集和处理混合可观测性数据(包括指标和/或事件)
  • ✅ 整合多个监控工具,需要支持指标和 Nagios 风格插件或检查脚本
  • ✅ 更强大的事件处理平台

Prometheus 更优:

  • ✅ 主要收集和评估指标
  • ✅ 监控同质 Kubernetes 基础设施(100% K8s 工作负载,Prometheus 提供更好的 K8s 集成)
  • ✅ 更强大的查询语言,内置历史数据分析支持

总结

通过以上内容,我们了解了:

  1. Prometheus 的核心概念:时间序列数据、指标、标签
  2. 如何快速开始:下载、配置、启动和基本查询
  3. 与其他监控系统的对比:帮助选择合适的监控方案

接下来将深入学习 Prometheus 的核心概念、服务器配置、查询语言和最佳实践。



二、Concepts (核心概念)

2.1 Data Model (数据模型)

时间序列数据

Prometheus 从根本上将所有数据存储为时间序列:属于同一指标和同一组标签维度的带时间戳的值流。

除了存储的时间序列外,Prometheus 还可以根据查询结果生成临时的派生时间序列。

指标名称和标签

每个时间序列都通过其指标名称和可选的**键值对(标签)**唯一标识。

指标名称(Metric Names)

规则说明
命名规范应指定被测量系统的一般特性(例如 http_requests_total - 收到的 HTTP 请求总数)
字符支持可以使用任何 UTF-8 字符
推荐格式应匹配正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]* 以获得最佳体验和兼容性
保留字符冒号(:)保留用于用户定义的记录规则,导出器或直接埋点不应使用

指标标签(Metric Labels)

标签让你能够捕获同一指标名称的不同实例。例如:所有使用 POST 方法访问 /api/tracks 处理器的 HTTP 请求。这就是 Prometheus 的**“多维数据模型”**。

查询语言允许基于这些维度进行过滤和聚合。

规则说明
字符支持标签名可以使用任何 UTF-8 字符
保留前缀__(两个下划线)开头的标签名必须保留给 Prometheus 内部使用
推荐格式应匹配正则表达式 [a-zA-Z_][a-zA-Z0-9_]* 以获得最佳兼容性
标签值可以包含任何 UTF-8 字符
空值处理具有空标签值的标签被视为等同于不存在的标签

⚠️ 注意: Prometheus v3.0.0 中添加了对指标和标签名称的 UTF-8 支持。建议使用推荐的字符集以获得最佳兼容性。

样本(Samples)

样本构成实际的时间序列数据。每个样本包括:

组成部分说明
float64 或原生直方图值
时间戳毫秒精度的时间戳

表示法(Notation)

给定指标名称和一组标签,时间序列通常使用以下表示法标识:

1<metric name>{<label name>="<label value>", ...}

示例:

1# 标准表示法
2api_http_requests_total{method="POST", handler="/messages"}
3
4# UTF-8 字符需要引号
5{"特殊指标名", label="value"}
6
7# 使用 __name__ 标签(内部表示)
8{__name__="api_http_requests_total", method="POST", handler="/messages"}

💡 这与 OpenTSDB 使用的表示法相同。


2.2 Metric Types (指标类型)

Prometheus 客户端库提供四种核心指标类型。这些类型目前仅在客户端库中区分(以支持特定类型的使用 API)和在传输协议中区分。Prometheus 服务器尚未使用类型信息,而是将所有数据展平为无类型时间序列。

graph TD A[Prometheus 指标类型] --> B[Counter 计数器] A --> C[Gauge 仪表] A --> D[Histogram 直方图] A --> E[Summary 摘要] B --> B1[单调递增] B --> B2[可重置为0] C --> C1[可增可减] C --> C2[适用于温度/内存等] D --> D1[观测值分桶] D --> D2[提供总和与计数] D --> D3[可计算分位数] E --> E1[观测值分位数] E --> E2[滑动时间窗口] E --> E3[提供总和与计数]

Counter (计数器)

Counter 是一个累积指标,表示单个单调递增的计数器,其值只能增加或在重启时重置为零。

适用场景:

  • ✅ 请求服务数
  • ✅ 已完成的任务数
  • ✅ 错误数

❌ 不要使用 Counter:

  • 不要用于可能减少的值(如当前运行的进程数,应使用 Gauge)

示例:

1# HTTP 请求总数
2http_requests_total{method="POST", endpoint="/api/users"}

Gauge (仪表)

Gauge 是表示单个数值的指标,该值可以任意上下波动。

适用场景:

  • ✅ 温度测量
  • ✅ 当前内存使用量
  • ✅ 可以上下波动的"计数"(如并发请求数)

示例:

1# 当前内存使用量(字节)
2memory_usage_bytes{instance="localhost:9090"}
3
4# 当前并发请求数
5http_concurrent_requests{endpoint="/api"}

Histogram (直方图)

Histogram 对观测值(通常是请求持续时间或响应大小等)进行采样,并将其计入可配置的桶中。它还提供所有观测值的总和。

暴露的时间序列:

对于基础指标名称为 <basename> 的直方图,会在抓取时暴露多个时间序列:

时间序列说明
<basename>_bucket{le="<上限>"}"观测桶的累积计数器
<basename>_sum所有观测值的总和
<basename>_count已观测到的事件计数(等同于 <basename>_bucket{le="+Inf"}

使用场景:

  • ✅ 使用 histogram_quantile() 函数计算直方图的分位数
  • ✅ 计算 Apdex 分数
  • ✅ 适合聚合多个实例的直方图

示例:

 1# 原始直方图指标
 2http_request_duration_seconds_bucket{le="0.1"} 1000
 3http_request_duration_seconds_bucket{le="0.5"} 1500
 4http_request_duration_seconds_bucket{le="1.0"} 1800
 5http_request_duration_seconds_bucket{le="+Inf"} 2000
 6http_request_duration_seconds_sum 850
 7http_request_duration_seconds_count 2000
 8
 9# 查询 95 分位数
10histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))

📌 Prometheus v2.40+: 实验性支持原生直方图(Native Histograms),只需一个时间序列,包含动态数量的桶,提供更高分辨率且成本更低。

📌 Prometheus v3.0+: 经典直方图的 le 标签值在摄取期间规范化,遵循 OpenMetrics 规范数字格式。


Summary (摘要)

Summary 与 Histogram 类似,也对观测值进行采样(通常是请求持续时间和响应大小)。它提供观测值的总计数和总和,并可以计算滑动时间窗口上的可配置分位数。

暴露的时间序列:

对于基础指标名称为 <basename> 的摘要,会在抓取时暴露多个时间序列:

时间序列说明
<basename>{quantile="<φ>"}观测事件的流式 φ-分位数(0 ≤ φ ≤ 1)
<basename>_sum所有观测值的总和
<basename>_count已观测到的事件计数

示例:

1# 摘要指标
2http_request_duration_seconds{quantile="0.5"} 0.05
3http_request_duration_seconds{quantile="0.9"} 0.2
4http_request_duration_seconds{quantile="0.99"} 0.5
5http_request_duration_seconds_sum 850
6http_request_duration_seconds_count 2000

📌 Prometheus v3.0+: 分位数标签值在摄取期间规范化,遵循 OpenMetrics 规范数字格式。

Histogram vs Summary 对比:

特性HistogramSummary
分位数计算服务器端(PromQL)客户端(应用端)
聚合能力可聚合多个实例无法聚合
精度桶粒度限制精确
性能开销较高
适用场景需要聚合、预先不知道分位数已知所需分位数、单实例观测

2.3 Jobs and Instances (任务与实例)

基本概念

在 Prometheus 术语中:

  • Instance(实例): 可以抓取的端点,通常对应单个进程
  • Job(任务): 具有相同目的的实例集合(例如为了可扩展性或可靠性而复制的进程)

示例:一个有 4 个副本实例的 API 服务器任务

1job: api-server
2  instance 1: 1.2.3.4:5670
3  instance 2: 1.2.3.4:5671
4  instance 3: 5.6.7.8:5670
5  instance 4: 5.6.7.8:5671

架构关系图:

graph LR A[Job: api-server] --> B[Instance: 1.2.3.4:5670] A --> C[Instance: 1.2.3.4:5671] A --> D[Instance: 5.6.7.8:5670] A --> E[Instance: 5.6.7.8:5671] F[Job: database] --> G[Instance: 10.0.0.1:3306] F --> H[Instance: 10.0.0.2:3306]

自动生成的标签和时间序列

当 Prometheus 抓取目标时,会自动附加一些标签到抓取的时间序列,用于标识被抓取的目标:

标签说明
job目标所属的已配置任务名称
instance被抓取目标 URL 的 <host>:<port> 部分

💡 如果这些标签已存在于抓取的数据中,行为取决于 honor_labels 配置选项。

自动生成的指标

对于每个实例抓取,Prometheus 会在以下时间序列中存储样本:

核心指标:

指标说明
up{job="<job-name>", instance="<instance-id>"}实例是否健康:1 = 可达,0 = 抓取失败
scrape_duration_seconds{job="<job-name>", instance="<instance-id>"}抓取持续时间(秒)
scrape_samples_post_metric_relabeling{job="<job-name>", instance="<instance-id>"}应用指标重新标记后剩余的样本数
scrape_samples_scraped{job="<job-name>", instance="<instance-id>"}目标暴露的样本数
scrape_series_added{job="<job-name>", instance="<instance-id>"}此次抓取中新增的大约序列数(v2.10+)

扩展指标(需启用 extra-scrape-metrics 特性标志):

指标说明
scrape_timeout_seconds{job="<job-name>", instance="<instance-id>"}为目标配置的 scrape_timeout
scrape_sample_limit{job="<job-name>", instance="<instance-id>"}为目标配置的 sample_limit(0 = 无限制)
scrape_body_size_bytes{job="<job-name>", instance="<instance-id>"}最近一次成功抓取的未压缩响应大小(失败返回 0 或 -1)

实用查询示例:

 1# 检查所有实例的健康状态
 2up
 3
 4# 查询所有不健康的实例
 5up == 0
 6
 7# 查询特定任务的所有实例
 8up{job="api-server"}
 9
10# 计算任务的健康实例数
11sum(up{job="api-server"})
12
13# 查询抓取时间超过 1 秒的实例
14scrape_duration_seconds > 1

💡 up 时间序列对于实例可用性监控非常有用!


总结

通过本章节,我们深入了解了 Prometheus 的核心概念:

  1. 数据模型: 时间序列如何通过指标名称和标签唯一标识
  2. 指标类型: Counter、Gauge、Histogram、Summary 的区别和使用场景
  3. 任务与实例: Job 和 Instance 的关系,以及 Prometheus 自动生成的监控指标

这些概念是理解和使用 Prometheus 的基础,为后续学习服务器配置、查询语言和最佳实践奠定了坚实基础。



三、Server (服务器)

3.1 Getting Started (快速开始)

本指南是一个 “Hello World” 风格的教程,展示如何安装、配置和使用一个简单的 Prometheus 实例。

下载和运行 Prometheus

  1. 下载适合你平台的最新版本 Prometheus
  2. 解压并运行:
1tar xvfz prometheus-*.tar.gz
2cd prometheus-*

配置 Prometheus 监控自身

Prometheus 通过抓取指标 HTTP 端点从目标收集指标。由于 Prometheus 以相同方式暴露自身数据,它也可以抓取和监控自己的健康状况。

将以下基本配置保存为 prometheus.yml

 1global:
 2  scrape_interval: 15s  # 默认每 15 秒抓取目标一次
 3  
 4  # 与外部系统通信时附加这些标签
 5  # (federation, remote storage, Alertmanager)
 6  external_labels:
 7    monitor: 'codelab-monitor'
 8
 9# 包含一个要抓取的端点的抓取配置
10# 这里是 Prometheus 自身
11scrape_configs:
12  # job 名称作为标签 `job=<job_name>` 添加到从此配置抓取的任何时间序列
13  - job_name: 'prometheus'
14  
15    # 覆盖全局默认值,每 5 秒抓取此任务的目标
16    scrape_interval: 5s
17  
18    static_configs:
19      - targets: ['localhost:9090']

配置说明:

配置项说明
global.scrape_interval全局抓取间隔,默认 15 秒
global.external_labels与外部系统通信时附加的标签
scrape_configs抓取配置列表
job_name任务名称,将作为 job 标签添加
scrape_interval特定任务的抓取间隔(覆盖全局设置)
static_configs.targets静态配置的目标列表

启动 Prometheus

1# 启动 Prometheus
2# 默认情况下,Prometheus 将数据库存储在 ./data (标志 --storage.tsdb.path)
3./prometheus --config.file=prometheus.yml

启动后访问:

  • 状态页面: http://localhost:9090
  • 指标端点: http://localhost:9090/metrics

使用表达式浏览器

访问 http://localhost:9090/graph 并选择 “Graph” 标签页中的 “Table” 视图。

查询示例:

1# 查询目标抓取间隔
2prometheus_target_interval_length_seconds
3
4# 仅查询 99 分位数延迟
5prometheus_target_interval_length_seconds{quantile="0.99"}
6
7# 计算返回的时间序列数量
8count(prometheus_target_interval_length_seconds)

使用图形界面

访问 http://localhost:9090/graph 并使用 “Graph” 标签页。

示例:绘制每秒创建的块速率

1rate(prometheus_tsdb_head_chunks_created_total[1m])

启动示例目标

使用 Node Exporter 作为示例目标:

1tar -xzvf node_exporter-*.*.tar.gz
2cd node_exporter-*.*
3
4# 在不同终端启动 3 个示例目标
5./node_exporter --web.listen-address 127.0.0.1:8080
6./node_exporter --web.listen-address 127.0.0.1:8081
7./node_exporter --web.listen-address 127.0.0.1:8082

现在有三个目标监听:

  • http://localhost:8080/metrics
  • http://localhost:8081/metrics
  • http://localhost:8082/metrics

配置 Prometheus 监控示例目标

将所有三个端点分组到一个名为 node 的任务中。我们将前两个端点标记为生产目标,第三个表示金丝雀实例。

prometheus.ymlscrape_configs 部分添加:

 1scrape_configs:
 2  - job_name: 'node'
 3  
 4    # 每 5 秒抓取一次
 5    scrape_interval: 5s
 6  
 7    static_configs:
 8      - targets: ['localhost:8080', 'localhost:8081']
 9        labels:
10          group: 'production'
11    
12      - targets: ['localhost:8082']
13        labels:
14          group: 'canary'

访问表达式浏览器,验证是否有 node_cpu_seconds_total 等指标。

配置聚合规则

为了提高效率,Prometheus 可以通过配置的记录规则将表达式预录制到新的持久时间序列中。

示例:记录 CPU 使用率的 5 分钟平均值

创建 prometheus.rules.yml

1groups:
2- name: cpu-node
3  rules:
4  - record: job_instance_mode:node_cpu_seconds:avg_rate5m
5    expr: avg by (job, instance, mode) (rate(node_cpu_seconds_total[5m]))

更新 prometheus.yml 以加载规则:

 1global:
 2  scrape_interval: 15s
 3  evaluation_interval: 15s  # 每 15 秒评估规则
 4  
 5  external_labels:
 6    monitor: 'codelab-monitor'
 7
 8rule_files:
 9  - 'prometheus.rules.yml'
10
11scrape_configs:
12  - job_name: 'prometheus'
13    scrape_interval: 5s
14    static_configs:
15      - targets: ['localhost:9090']
16  
17  - job_name: 'node'
18    scrape_interval: 5s
19    static_configs:
20      - targets: ['localhost:8080', 'localhost:8081']
21        labels:
22          group: 'production'
23      - targets: ['localhost:8082']
24        labels:
25          group: 'canary'

重启 Prometheus 并验证新指标 job_instance_mode:node_cpu_seconds:avg_rate5m 是否可用。

重新加载配置

无需重启进程即可重新加载配置(使用 SIGHUP 信号):

1# Linux 系统
2kill -s SIGHUP <PID>

优雅关闭实例

建议使用信号进行干净关闭:

1# Linux 系统
2kill -s SIGTERM <PID>
3# 或
4kill -s SIGINT <PID>
5# 或在终端按 Control-C

3.2 Installation (安装)

Prometheus 提供多种安装方式,适合不同的使用场景。

安装方式概览

graph TD A[Prometheus 安装方式] --> B[预编译二进制文件] A --> C[源码编译] A --> D[Docker 镜像] B --> B1[下载官方二进制] B --> B2[直接运行] C --> C1[使用 Makefile] C --> C2[自定义构建] D --> D1[Docker Hub] D --> D2[Quay.io] D --> D3[容器化部署]

方式一:使用预编译二进制文件

优点: 简单快速,适合快速测试和开发环境

  1. 访问官方下载页面
  2. 选择适合你平台的版本
  3. 下载并解压
  4. 直接运行 prometheus 二进制文件
1tar xvfz prometheus-*.tar.gz
2cd prometheus-*
3./prometheus --config.file=prometheus.yml

方式二:从源码编译

优点: 可自定义构建选项,获取最新功能

查看相应仓库中的 Makefile 目标进行构建。

1# 通常流程
2git clone https://github.com/prometheus/prometheus.git
3cd prometheus
4make build

方式三:使用 Docker

优点: 容器化部署,易于管理和扩展

所有 Prometheus 服务都提供 Docker 镜像,可从 Quay.ioDocker Hub 获取。

基本运行:

1# 使用示例配置启动 Prometheus
2docker run -p 9090:9090 prom/prometheus

这将使用示例配置启动 Prometheus 并在端口 9090 上暴露。

重要提示:

  • Prometheus 镜像使用卷来存储实际指标
  • 生产部署强烈建议使用命名卷来简化 Prometheus 升级时的数据管理

Docker 高级配置

1. 设置命令行参数

Docker 镜像以多个默认命令行参数启动(参见 Dockerfile)。

如果要添加额外的命令行参数,需要重新添加默认参数,因为它们会被覆盖。

2. 挂载配置文件(Bind-mount)

方法 A:挂载 prometheus.yml 文件

1docker run \
2    -p 9090:9090 \
3    -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \
4    prom/prometheus

方法 B:挂载包含 prometheus.yml 的目录

1docker run \
2    -p 9090:9090 \
3    -v /path/to/config:/etc/prometheus \
4    prom/prometheus

3. 保存 Prometheus 数据

Prometheus 数据存储在容器内的 /prometheus 目录中。每次容器重启时数据都会被清除。

要保存数据,需要为容器设置持久存储:

1# 创建持久卷
2docker volume create prometheus-data
3
4# 使用持久存储启动 Prometheus
5docker run \
6    -p 9090:9090 \
7    -v /path/to/prometheus.yml:/etc/prometheus/prometheus.yml \
8    -v prometheus-data:/prometheus \
9    prom/prometheus

存储位置:

路径说明
/etc/prometheus/配置文件目录
/prometheus/数据存储目录(TSDB)

4. 自定义镜像

将配置烘焙到镜像中,避免在主机上管理文件。适合配置相对静态且跨环境一致的场景。

创建自定义镜像:

创建新目录,包含 prometheus.ymlDockerfile

1FROM prom/prometheus
2ADD prometheus.yml /etc/prometheus/

构建并运行:

1docker build -t my-prometheus .
2docker run -p 9090:9090 my-prometheus

高级选项:

  • 启动时动态渲染配置(使用工具)
  • 使用守护进程定期更新配置

Docker 部署完整示例

 1# 1. 创建持久卷
 2docker volume create prometheus-data
 3docker volume create prometheus-config
 4
 5# 2. 创建配置文件
 6cat > /tmp/prometheus.yml <<EOF
 7global:
 8  scrape_interval: 15s
 9  evaluation_interval: 15s
10
11scrape_configs:
12  - job_name: 'prometheus'
13    static_configs:
14      - targets: ['localhost:9090']
15EOF
16
17# 3. 启动 Prometheus
18docker run -d \
19    --name prometheus \
20    -p 9090:9090 \
21    -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml \
22    -v prometheus-data:/prometheus \
23    prom/prometheus \
24    --config.file=/etc/prometheus/prometheus.yml \
25    --storage.tsdb.path=/prometheus
26
27# 4. 查看日志
28docker logs -f prometheus
29
30# 5. 访问 Web UI
31# http://localhost:9090

安装方式对比

方式优点缺点适用场景
预编译二进制简单快速、无依赖需手动管理更新开发、测试、小规模部署
源码编译可定制、最新功能构建复杂、依赖多需要特定功能、深度定制
Docker容器化、易于管理、可扩展需要 Docker 环境生产环境、云原生架构、K8s

总结

通过本章节,我们学习了:

  1. Getting Started:

    • Prometheus 的基本配置和启动
    • 监控自身和外部目标
    • 使用表达式浏览器和图形界面
    • 配置记录规则进行数据预聚合
  2. Installation:

    • 三种主要安装方式及其适用场景
    • Docker 部署的详细配置和最佳实践
    • 持久化存储和自定义镜像的使用

这些内容为后续深入学习 Prometheus 的配置、查询和告警功能打下了坚实基础。


3.3 Configuration (配置详解)

Prometheus 通过命令行标志和配置文件进行配置。命令行标志配置不可变的系统参数(如存储位置、保留数据量等),而配置文件定义抓取任务、实例及要加载的规则文件。

配置文件基础

配置文件格式: YAML

查看所有命令行标志:

1./prometheus -h

指定配置文件:

1./prometheus --config.file=prometheus.yml

运行时重新加载配置:

1# 方法 1: 发送 SIGHUP 信号
2kill -s SIGHUP <PID>
3
4# 方法 2: HTTP POST (需启用 --web.enable-lifecycle)
5curl -X POST http://localhost:9090/-/reload

💡 如果新配置格式不正确,更改不会被应用。重新加载也会重新加载规则文件。

配置文件结构

graph TD A[prometheus.yml] --> B[global 全局配置] A --> C[runtime 运行时配置] A --> D[rule_files 规则文件] A --> E[scrape_configs 抓取配置] A --> F[alerting 告警配置] A --> G[remote_write 远程写入] A --> H[remote_read 远程读取] A --> I[storage 存储配置] E --> E1[static_configs 静态配置] E --> E2[服务发现] E2 --> E2A[kubernetes_sd_configs] E2 --> E2B[consul_sd_configs] E2 --> E2C[ec2_sd_configs] E2 --> E2D[dns_sd_configs] E2 --> E2E[其他20+种服务发现]

通用占位符说明

占位符说明示例
<boolean>布尔值truefalse
<duration>持续时间1d, 1h30m, 5m, 10s
<filename>当前工作目录中的有效路径prometheus.yml
<float>浮点数0.5, 1.25
<host>主机名或 IP + 可选端口号localhost:9090
<int>整数值100, 5000
<labelname>标签名 (正则:[a-zA-Z_][a-zA-Z0-9_]*)job, instance
<labelvalue>Unicode 字符串任何 UTF-8 字符
<path>有效的 URL 路径/metrics
<scheme>协议方案httphttps
<secret>机密字符串(如密码)password123
<string>常规字符串任意字符串
<size>字节大小(需要单位)512MB, 1GB

Global 全局配置

全局配置在所有其他配置上下文中有效,并作为其他配置节的默认值。

 1global:
 2  # 抓取目标的默认频率
 3  scrape_interval: 15s  # 默认 1m
 4  
 5  # 抓取请求超时时间 (不能大于 scrape_interval)
 6  scrape_timeout: 10s  # 默认 10s
 7  
 8  # 规则评估频率
 9  evaluation_interval: 15s  # 默认 1m
10  
11  # 规则评估时间戳偏移(确保底层指标已收到)
12  rule_query_offset: 0s  # 默认 0s
13  
14  # 与外部系统通信时添加的标签
15  # (federation, remote storage, Alertmanager)
16  external_labels:
17    cluster: 'production'
18    region: 'us-east-1'
19  
20  # PromQL 查询日志文件
21  query_log_file: '/var/log/prometheus/query.log'
22  
23  # 抓取失败日志文件
24  scrape_failure_log_file: '/var/log/prometheus/scrape_failures.log'

关键全局配置项:

配置项说明默认值
scrape_interval抓取目标的频率1m
scrape_timeout抓取超时时间10s
evaluation_interval规则评估频率1m
body_size_limit响应体大小限制0 (无限制)
sample_limit每次抓取样本数限制0 (无限制)
label_limit每个样本标签数限制0 (无限制)
target_limit每个抓取配置的目标数限制0 (无限制)
metric_name_validation_scheme指标名称验证方案utf8

Runtime 运行时配置

1runtime:
2  # 配置 Go 垃圾回收器 GOGC 参数
3  # 降低此数字会增加 CPU 使用率
4  gogc: 75  # 默认 75

Scrape Configs 抓取配置

抓取配置指定一组目标及其抓取参数。一般情况下,一个抓取配置指定一个任务。

基本结构:

 1scrape_configs:
 2  - job_name: 'prometheus'  # 任务名称(必需,唯一)
 3  
 4    # 抓取间隔(覆盖全局设置)
 5    scrape_interval: 5s
 6  
 7    # 抓取超时
 8    scrape_timeout: 5s
 9  
10    # 指标路径
11    metrics_path: /metrics
12  
13    # 协议方案
14    scheme: http
15  
16    # honor_labels 控制标签冲突处理
17    # true: 保留抓取数据中的标签,忽略服务端标签
18    # false: 重命名冲突标签为 exported_<label>
19    honor_labels: false
20  
21    # honor_timestamps 控制是否使用目标的时间戳
22    # true: 使用目标提供的时间戳
23    # false: 忽略目标时间戳
24    honor_timestamps: true
25  
26    # 静态配置的目标列表
27    static_configs:
28      - targets: ['localhost:9090']
29        labels:
30          env: 'production'

完整配置示例:

 1scrape_configs:
 2  # 监控 Prometheus 自身
 3  - job_name: 'prometheus'
 4    scrape_interval: 5s
 5    static_configs:
 6      - targets: ['localhost:9090']
 7  
 8  # 监控 Node Exporter
 9  - job_name: 'node'
10    scrape_interval: 10s
11    static_configs:
12      - targets: 
13          - 'node1:9100'
14          - 'node2:9100'
15        labels:
16          group: 'production'
17      - targets: ['node3:9100']
18        labels:
19          group: 'testing'
20  
21  # 使用 Kubernetes 服务发现
22  - job_name: 'kubernetes-pods'
23    kubernetes_sd_configs:
24      - role: pod
25    relabel_configs:
26      - source_labels: [__meta_kubernetes_pod_label_app]
27        target_label: app

抓取配置关键选项:

选项说明默认值
job_name任务名称(分配给抓取指标的标签)必需
scrape_interval抓取频率继承 global
scrape_timeout抓取超时继承 global
metrics_path指标路径/metrics
scheme协议http
honor_labels是否保留原始标签false
honor_timestamps是否使用目标时间戳true
enable_compression是否请求压缩响应true

HTTP Config HTTP 配置

HTTP 配置允许配置 HTTP 请求的认证和 TLS 设置。

 1scrape_configs:
 2  - job_name: 'secure-app'
 3    # Basic 认证
 4    basic_auth:
 5      username: 'admin'
 6      password: 'secret123'
 7  
 8    # 或使用文件
 9    # basic_auth:
10    #   username_file: /path/to/username
11    #   password_file: /path/to/password
12  
13    # 或使用 Bearer Token
14    # authorization:
15    #   type: Bearer
16    #   credentials: 'mytoken123'
17    #   # 或从文件读取
18    #   # credentials_file: /path/to/token
19  
20    # TLS 配置
21    tls_config:
22      # CA 证书
23      ca_file: /path/to/ca.crt
24      # 客户端证书
25      cert_file: /path/to/client.crt
26      key_file: /path/to/client.key
27      # 跳过证书验证(不建议)
28      insecure_skip_verify: false
29      # 最小 TLS 版本
30      min_version: TLS12
31  
32    # 代理设置
33    proxy_url: http://proxy.example.com:8080
34  
35    # 自定义 HTTP 头
36    http_headers:
37      X-Custom-Header:
38        values: ['custom-value']
39  
40    static_configs:
41      - targets: ['app.example.com:443']
42  
43    scheme: https

HTTP 认证方式对比:

认证方式适用场景示例
Basic Auth简单的用户名/密码认证内部服务、开发环境
Bearer TokenAPI Token 认证Kubernetes, 云服务
OAuth 2.0复杂的授权流程需要刷新 token 的场景
TLS Client CertmTLS 双向认证高安全要求的服务

服务发现 (Service Discovery)

Prometheus 支持 25+ 种服务发现机制,可以自动发现监控目标。

支持的服务发现类型:

graph LR A[服务发现] --> B[云平台] A --> C[容器编排] A --> D[服务注册] A --> E[其他] B --> B1[AWS EC2] B --> B2[Azure] B --> B3[GCE] B --> B4[DigitalOcean] B --> B5[Lightsail] B --> B6[Linode] B --> B7[Hetzner] C --> C1[Kubernetes] C --> C2[Docker] C --> C3[Docker Swarm] C --> C4[Nomad] C --> C5[Marathon] D --> D1[Consul] D --> D2[Eureka] D --> D3[Zookeeper] D --> D4[DNS] E --> E1[HTTP] E --> E2[File] E --> E3[OpenStack]

常用服务发现配置示例:

1. Kubernetes 服务发现

 1scrape_configs:
 2  - job_name: 'kubernetes-pods'
 3    kubernetes_sd_configs:
 4      - role: pod
 5        namespaces:
 6          names:
 7            - default
 8            - production
 9  
10    relabel_configs:
11      # 仅抓取带有 prometheus.io/scrape=true 注解的 Pod
12      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
13        action: keep
14        regex: true
15    
16      # 使用自定义路径
17      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
18        action: replace
19        target_label: __metrics_path__
20        regex: (.+)
21    
22      # 使用自定义端口
23      - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
24        action: replace
25        regex: ([^:]+)(?::\d+)?;(\d+)
26        replacement: $1:$2
27        target_label: __address__

2. Consul 服务发现

 1scrape_configs:
 2  - job_name: 'consul-services'
 3    consul_sd_configs:
 4      - server: 'localhost:8500'
 5        datacenter: 'dc1'
 6        services: ['web', 'api', 'database']
 7        tags: ['prometheus']
 8  
 9    relabel_configs:
10      - source_labels: [__meta_consul_service]
11        target_label: service

3. DNS 服务发现

1scrape_configs:
2  - job_name: 'dns-discovery'
3    dns_sd_configs:
4      - names:
5          - 'tasks.myservice.example.com'
6        type: 'A'
7        port: 9100
8    refresh_interval: 30s

4. EC2 服务发现

 1scrape_configs:
 2  - job_name: 'ec2-nodes'
 3    ec2_sd_configs:
 4      - region: us-east-1
 5        port: 9100
 6        filters:
 7          - name: tag:Environment
 8            values: ['production']
 9          - name: instance-state-name
10            values: ['running']
11  
12    relabel_configs:
13      - source_labels: [__meta_ec2_tag_Name]
14        target_label: instance_name

5. File 服务发现 (最灵活)

1scrape_configs:
2  - job_name: 'file-sd'
3    file_sd_configs:
4      - files:
5          - '/etc/prometheus/targets/*.json'
6          - '/etc/prometheus/targets/*.yaml'
7        refresh_interval: 30s

targets.json 示例:

 1[
 2  {
 3    "targets": ["host1:9100", "host2:9100"],
 4    "labels": {
 5      "env": "production",
 6      "team": "backend"
 7    }
 8  },
 9  {
10    "targets": ["host3:9100"],
11    "labels": {
12      "env": "staging"
13    }
14  }
15]

Relabel Configs 重新标记配置

重新标记是一个强大的工具,可以在抓取之前动态重写目标的标签集。

 1scrape_configs:
 2  - job_name: 'example'
 3    static_configs:
 4      - targets: ['localhost:9090']
 5  
 6    relabel_configs:
 7      # 保留特定标签的目标
 8      - source_labels: [__meta_kubernetes_namespace]
 9        action: keep
10        regex: 'production|staging'
11    
12      # 删除特定标签的目标
13      - source_labels: [__meta_kubernetes_pod_name]
14        action: drop
15        regex: 'test-.*'
16    
17      # 替换标签值
18      - source_labels: [__address__]
19        target_label: instance
20        regex: '(.+):.*'
21        replacement: '$1'
22    
23      # 添加新标签
24      - target_label: cluster
25        replacement: 'us-central'
26    
27      # 保留标签
28      - action: labelkeep
29        regex: '__meta_kubernetes_(namespace|pod_name|pod_ip)'
30    
31      # 删除标签
32      - action: labeldrop
33        regex: '__meta_kubernetes_pod_label_.*'

Relabel 动作类型:

Action说明
replace替换标签值(默认)
keep保留匹配 regex 的目标
drop丢弃匹配 regex 的目标
labelkeep保留匹配 regex 的标签
labeldrop删除匹配 regex 的标签
labelmap将标签名映射到新名称
hashmod计算哈希值用于分片

Alerting 告警配置

 1alerting:
 2  # 告警重新标记
 3  alert_relabel_configs:
 4    - source_labels: [dc]
 5      regex: 'dc1'
 6      target_label: severity
 7      replacement: 'critical'
 8  
 9  # Alertmanager 配置
10  alertmanagers:
11    - static_configs:
12        - targets:
13            - 'alertmanager1:9093'
14            - 'alertmanager2:9093'
15    
16      # 超时设置
17      timeout: 10s
18    
19      # 路径前缀
20      path_prefix: /alertmanager

Remote Write/Read 远程写入/读取

 1# 远程写入(用于长期存储)
 2remote_write:
 3  - url: 'http://remote-storage:9201/write'
 4  
 5    # 写入超时
 6    remote_timeout: 30s
 7  
 8    # 队列配置
 9    queue_config:
10      capacity: 10000
11      max_shards: 50
12      max_samples_per_send: 1000
13  
14    # 写入重新标记(过滤不需要的指标)
15    write_relabel_configs:
16      - source_labels: [__name__]
17        regex: 'go_.*'
18        action: drop
19
20# 远程读取
21remote_read:
22  - url: 'http://remote-storage:9201/read'
23    read_recent: true

Storage 存储配置

 1storage:
 2  tsdb:
 3    # 数据保留时间
 4    retention_time: 15d
 5  
 6    # 最大数据块大小
 7    retention_size: 512GB
 8  
 9  exemplars:
10    # Exemplar 最大存储大小
11    max_exemplars: 100000

完整配置示例

 1# 全局配置
 2global:
 3  scrape_interval: 15s
 4  evaluation_interval: 15s
 5  external_labels:
 6    cluster: 'production'
 7    datacenter: 'us-east-1'
 8
 9# 运行时配置
10runtime:
11  gogc: 75
12
13# 规则文件
14rule_files:
15  - '/etc/prometheus/rules/*.yml'
16
17# 抓取配置
18scrape_configs:
19  # Prometheus 自身
20  - job_name: 'prometheus'
21    static_configs:
22      - targets: ['localhost:9090']
23  
24  # Node Exporters (使用文件服务发现)
25  - job_name: 'node'
26    file_sd_configs:
27      - files:
28          - '/etc/prometheus/targets/nodes/*.json'
29  
30    relabel_configs:
31      - source_labels: [__address__]
32        target_label: instance
33  
34  # Kubernetes Pods
35  - job_name: 'kubernetes-pods'
36    kubernetes_sd_configs:
37      - role: pod
38  
39    relabel_configs:
40      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
41        action: keep
42        regex: true
43      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
44        action: replace
45        target_label: __metrics_path__
46        regex: (.+)
47
48# 告警配置
49alerting:
50  alertmanagers:
51    - static_configs:
52        - targets: ['alertmanager:9093']
53
54# 远程写入
55remote_write:
56  - url: 'http://thanos-receive:19291/api/v1/receive'
57    queue_config:
58      capacity: 10000
59      max_samples_per_send: 1000
60
61# 存储配置
62storage:
63  tsdb:
64    retention_time: 30d
65    retention_size: 1TB

总结

通过本节,我们学习了 Prometheus 配置的核心内容:

  1. 全局配置: 抓取间隔、评估间隔、外部标签等
  2. 抓取配置: 如何定义监控目标、静态配置和服务发现
  3. 服务发现: 支持 25+ 种自动发现机制(Kubernetes、Consul、EC2 等)
  4. 认证与安全: Basic Auth、Bearer Token、OAuth 2.0、TLS 配置
  5. 重新标记: 动态修改标签,实现灵活的目标过滤和标签管理
  6. 告警配置: 配置 Alertmanager 集成
  7. 远程存储: 远程写入/读取,实现长期数据存储

这些配置选项为 Prometheus 提供了极大的灵活性,可以适应各种监控场景和基础设施环境。


3.4 Recording Rules (记录规则)

记录规则允许你预计算频繁使用或计算成本高的表达式,并将结果保存为新的时间序列。查询预计算的结果通常比每次执行原始表达式快得多,特别适用于需要重复查询相同表达式的仪表板。

规则配置基础

Prometheus 支持两种类型的规则:

  • Recording Rules (记录规则): 预计算表达式,保存为新的时间序列
  • Alerting Rules (告警规则): 定义告警条件,触发通知

规则文件格式: YAML

在配置文件中加载规则:

1rule_files:
2  - '/etc/prometheus/rules/*.yml'
3  - '/etc/prometheus/alerts/*.yml'

运行时重新加载规则:

1# 发送 SIGHUP 信号
2kill -s SIGHUP <PID>
3
4# 或使用 HTTP 接口
5curl -X POST http://localhost:9090/-/reload

⚠️ 只有所有规则文件格式正确时,更改才会被应用

语法检查工具:

1promtool check rules /path/to/example.rules.yml

规则文件结构

1groups:
2  - name: <组名>              # 必需,文件内唯一
3    interval: <持续时间>       # 可选,规则评估频率
4    limit: <整数>              # 可选,限制告警/序列数量
5    query_offset: <持续时间>   # 可选,查询时间偏移
6    labels:                    # 可选,添加到所有规则的标签
7      <标签名>: <标签值>
8    rules:
9      - <规则定义>

基本示例:

1groups:
2  - name: example
3    interval: 30s
4    rules:
5      # 记录规则
6      - record: code:prometheus_http_requests_total:sum
7        expr: sum by (code) (prometheus_http_requests_total)

Recording Rule 语法

 1# 记录规则名称(必需,必须是有效的指标名称)
 2record: <string>
 3
 4# PromQL 表达式(必需)
 5# 每次评估时执行,结果保存为新的时间序列
 6expr: <string>
 7
 8# 添加或覆盖的标签(可选)
 9labels:
10  <labelname>: <labelvalue>

完整配置示例:

 1groups:
 2  # 1. HTTP 请求聚合
 3  - name: http_requests_aggregation
 4    interval: 30s
 5    rules:
 6      # 按状态码聚合请求总数
 7      - record: code:prometheus_http_requests_total:sum
 8        expr: sum by (code) (prometheus_http_requests_total)
 9    
10      # 按 job 聚合请求总数
11      - record: job:prometheus_http_requests_total:sum
12        expr: sum by (job) (prometheus_http_requests_total)
13    
14      # 计算每个 job 的请求速率 (5分钟平均)
15      - record: job:prometheus_http_requests_total:rate5m
16        expr: sum by (job) (rate(prometheus_http_requests_total[5m]))
17  
18  # 2. 资源使用率聚合
19  - name: resource_usage
20    interval: 1m
21    rules:
22      # CPU 使用率聚合
23      - record: instance:node_cpu_utilization:rate5m
24        expr: 1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m]))
25    
26      # 内存使用率
27      - record: instance:node_memory_utilization:ratio
28        expr: |
29          1 - (
30            node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes
31          )
32        labels:
33          team: infrastructure
34  
35  # 3. 业务指标聚合
36  - name: business_metrics
37    interval: 5m
38    rules:
39      # 计算 API 请求成功率
40      - record: job:api_success_rate:ratio
41        expr: |
42          sum by (job) (rate(api_requests_total{status=~"2.."}[5m]))
43          /
44          sum by (job) (rate(api_requests_total[5m]))
45    
46      # 计算 99 分位延迟
47      - record: job:request_latency_seconds:p99
48        expr: histogram_quantile(0.99, sum by (job, le) (rate(request_duration_seconds_bucket[5m])))

记录规则命名最佳实践

命名格式: level:metric:operations

  • level: 聚合级别 (如 job, instance, cluster)
  • metric: 原始指标名称
  • operations: 应用的操作 (如 sum, rate5m, ratio)

示例:

规则名称说明
job:http_requests_total:rate5m按 job 聚合的 HTTP 请求 5 分钟速率
instance:node_cpu:utilization按实例聚合的 CPU 使用率
cluster:memory:available_bytes集群级别的可用内存
code:http_errors_total:sum按状态码聚合的错误总数

规则组配置选项

选项说明默认值
name规则组名称(必需,唯一)-
interval规则评估间隔global.evaluation_interval
limit告警/序列数量限制0 (无限制)
query_offset查询时间偏移global.rule_query_offset
labels附加到所有规则的标签-

规则评估性能优化

1. 设置合适的评估间隔

 1groups:
 2  # 高频评估 - 关键指标
 3  - name: critical_metrics
 4    interval: 15s
 5    rules:
 6      - record: instance:up:count
 7        expr: count by (job) (up == 1)
 8  
 9  # 低频评估 - 聚合指标
10  - name: daily_aggregations
11    interval: 5m
12    rules:
13      - record: cluster:cpu_usage:avg24h
14        expr: avg_over_time(cluster:cpu_usage:ratio[24h])

2. 使用查询偏移确保数据可用

1groups:
2  - name: remote_write_metrics
3    # 偏移 1 分钟,确保远程写入的数据已到达
4    query_offset: 1m
5    rules:
6      - record: job:requests_total:rate5m
7        expr: sum by (job) (rate(requests_total[5m]))

3. 限制规则产生的序列数量

1groups:
2  - name: high_cardinality_metrics
3    # 限制最多产生 10000 个时间序列
4    limit: 10000
5    rules:
6      - record: path:http_requests_total:sum
7        expr: sum by (path) (http_requests_total)

3.5 Alerting Rules (告警规则)

告警规则允许你基于 Prometheus 表达式定义告警条件,并在告警触发时发送通知到外部服务(如 Alertmanager)。

告警规则语法

 1# 告警名称(必需,必须是有效的标签值)
 2alert: <string>
 3
 4# PromQL 表达式(必需)
 5# 当表达式结果为真时,告警激活
 6expr: <string>
 7
 8# 持续时间阈值(可选)
 9# 告警在持续触发这段时间后才会 firing
10[ for: <duration> | default = 0s ]
11
12# 保持触发时间(可选)
13# 告警条件消失后继续触发的时间
14[ keep_firing_for: <duration> | default = 0s ]
15
16# 附加标签(可选)
17# 标签值支持模板化
18labels:
19  [ <labelname>: <tmpl_string> ]
20
21# 注解(可选)
22# 用于存储描述、Runbook 链接等信息
23# 注解值支持模板化
24annotations:
25  [ <labelname>: <tmpl_string> ]

告警规则示例

基础告警:

 1groups:
 2  - name: instance_alerts
 3    rules:
 4      # 实例宕机告警
 5      - alert: InstanceDown
 6        expr: up == 0
 7        for: 5m
 8        labels:
 9          severity: critical
10          team: infrastructure
11        annotations:
12          summary: "实例 {{ $labels.instance }} 宕机"
13          description: "{{ $labels.instance }} (job: {{ $labels.job }}) 已宕机超过 5 分钟"

高级告警配置:

 1groups:
 2  - name: application_alerts
 3    labels:
 4      team: backend
 5    rules:
 6      # 1. API 高延迟告警
 7      - alert: APIHighRequestLatency
 8        expr: |
 9          histogram_quantile(0.5, 
10            rate(api_http_request_latencies_second_bucket[5m])
11          ) > 1
12        for: 10m
13        labels:
14          severity: warning
15          service: api
16        annotations:
17          summary: "{{ $labels.instance }} API 延迟过高"
18          description: "实例 {{ $labels.instance }} 的中位数请求延迟超过 1s (当前值: {{ $value }}s)"
19          dashboard: "https://grafana.example.com/d/api-dashboard"
20          runbook: "https://wiki.example.com/runbooks/api-latency"
21    
22      # 2. 错误率过高告警
23      - alert: HighErrorRate
24        expr: |
25          (
26            sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))
27            /
28            sum by (job) (rate(http_requests_total[5m]))
29          ) > 0.05
30        for: 5m
31        keep_firing_for: 10m
32        labels:
33          severity: critical
34        annotations:
35          summary: "{{ $labels.job }} 错误率过高"
36          description: "{{ $labels.job }} 的 5xx 错误率超过 5% (当前: {{ $value | humanizePercentage }})"
37    
38      # 3. 磁盘空间不足告警
39      - alert: DiskSpaceLow
40        expr: |
41          (
42            node_filesystem_avail_bytes{fstype!~"tmpfs|fuse.lxcfs"}
43            /
44            node_filesystem_size_bytes{fstype!~"tmpfs|fuse.lxcfs"}
45          ) < 0.1
46        for: 15m
47        labels:
48          severity: warning
49        annotations:
50          summary: "{{ $labels.instance }} 磁盘空间不足"
51          description: |
52            实例 {{ $labels.instance }} 的挂载点 {{ $labels.mountpoint }} 
53            可用空间低于 10% (当前: {{ $value | humanizePercentage }})
54    
55      # 4. 内存使用率过高告警
56      - alert: HighMemoryUsage
57        expr: |
58          (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 0.9
59        for: 5m
60        labels:
61          severity: warning
62        annotations:
63          summary: "{{ $labels.instance }} 内存使用率过高"
64          description: "实例 {{ $labels.instance }} 内存使用率超过 90% (当前: {{ $value | humanizePercentage }})"

告警状态转换

graph LR A[Inactive
未触发] -->|expr = true| B[Pending
待触发] B -->|持续 for 时间| C[Firing
触发中] B -->|expr = false| A C -->|expr = false
& keep_firing_for = 0| A C -->|expr = false
& keep_firing_for > 0| D[Keep Firing
保持触发] D -->|超过 keep_firing_for| A D -->|expr = true| C

状态说明:

状态说明
Inactive告警表达式结果为假,告警未激活
Pending表达式为真,但未达到 for 持续时间
Firing表达式为真且持续时间超过 for 阈值
Keep Firing表达式已变为假,但在 keep_firing_for 期间内继续触发

告警模板化

告警的 labelsannotations 支持使用 Go 模板语法。

可用变量:

变量说明示例
$labels告警实例的标签键值对{{ $labels.instance }}
$value告警表达式的评估值{{ $value }}
$externalLabels全局配置的外部标签{{ $externalLabels.cluster }}

模板函数:

 1annotations:
 2  # 格式化百分比
 3  usage: "{{ $value | humanizePercentage }}"
 4  
 5  # 格式化数字(添加单位)
 6  memory: "{{ $value | humanize }}B"
 7  
 8  # 格式化为 1024 进制
 9  disk: "{{ $value | humanize1024 }}B"
10  
11  # 格式化时间戳
12  timestamp: "{{ $value | humanizeTimestamp }}"

完整模板示例:

 1groups:
 2  - name: template_examples
 3    rules:
 4      - alert: PodCrashLooping
 5        expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
 6        for: 5m
 7        labels:
 8          severity: warning
 9          namespace: "{{ $labels.namespace }}"
10          pod: "{{ $labels.pod }}"
11        annotations:
12          summary: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 频繁重启"
13          description: |
14            命名空间: {{ $labels.namespace }}
15            Pod: {{ $labels.pod }}
16            容器: {{ $labels.container }}
17            重启速率: {{ $value | humanize }} 次/秒
18            集群: {{ $externalLabels.cluster }}
19          grafana: "https://grafana/d/pods?var-namespace={{ $labels.namespace }}&var-pod={{ $labels.pod }}"

告警最佳实践

1. 合理设置 for 持续时间

1# ❌ 不好 - 可能产生大量瞬时告警
2- alert: HighCPU
3  expr: cpu_usage > 0.8
4  # 没有 for,立即告警
5
6# ✅ 好 - 只在持续高负载时告警
7- alert: HighCPU
8  expr: cpu_usage > 0.8
9  for: 10m  # 持续 10 分钟才告警

2. 使用 keep_firing_for 防止抖动

1- alert: ServiceDown
2  expr: up{job="myservice"} == 0
3  for: 5m
4  keep_firing_for: 10m  # 服务恢复后继续告警 10 分钟
5  annotations:
6    summary: "服务可能存在不稳定问题"

3. 分级告警严重程度

 1groups:
 2  - name: disk_alerts
 3    rules:
 4      # 警告级别
 5      - alert: DiskSpaceWarning
 6        expr: disk_free_percent < 20
 7        for: 30m
 8        labels:
 9          severity: warning
10    
11      # 严重级别
12      - alert: DiskSpaceCritical
13        expr: disk_free_percent < 10
14        for: 15m
15        labels:
16          severity: critical
17    
18      # 紧急级别
19      - alert: DiskSpaceEmergency
20        expr: disk_free_percent < 5
21        for: 5m
22        labels:
23          severity: emergency

4. 提供详细的上下文信息

 1- alert: DatabaseConnectionPoolExhausted
 2  expr: db_connection_pool_usage > 0.95
 3  for: 5m
 4  labels:
 5    severity: critical
 6    component: database
 7  annotations:
 8    summary: "{{ $labels.instance }} 数据库连接池即将耗尽"
 9    description: |
10      数据库连接池使用率: {{ $value | humanizePercentage }}
11      实例: {{ $labels.instance }}
12      数据库: {{ $labels.database }}
13    impact: "可能导致应用无法建立新的数据库连接,影响业务"
14    action: |
15      1. 检查是否存在连接泄漏
16      2. 考虑增加连接池大小
17      3. 检查慢查询
18    runbook: "https://wiki.example.com/runbooks/db-connection-pool"
19    dashboard: "https://grafana.example.com/d/db-dashboard?var-instance={{ $labels.instance }}"

查看告警状态

1. 通过 Prometheus UI

访问 http://prometheus:9090/alerts 查看所有告警的当前状态。

2. 通过 API 查询

1# 查询所有 firing 告警
2curl http://localhost:9090/api/v1/alerts
3
4# 查询 ALERTS 指标
5curl -g 'http://localhost:9090/api/v1/query?query=ALERTS'

3. ALERTS 合成指标

Prometheus 为每个告警自动创建 ALERTS 时间序列:

1# 查询所有 firing 状态的告警
2ALERTS{alertstate="firing"}
3
4# 查询所有 pending 状态的告警
5ALERTS{alertstate="pending"}
6
7# 查询特定告警
8ALERTS{alertname="InstanceDown", alertstate="firing"}

集成 Alertmanager

Prometheus 负责告警检测,Alertmanager 负责告警通知、分组、抑制和静默。

在 prometheus.yml 中配置:

1alerting:
2  alertmanagers:
3    - static_configs:
4        - targets:
5            - 'alertmanager1:9093'
6            - 'alertmanager2:9093'
7      timeout: 10s

告警流程:

graph LR A[Prometheus
评估规则] -->|告警触发| B[Prometheus
发送告警] B --> C[Alertmanager
接收告警] C --> D[分组
Group] D --> E[抑制
Inhibition] E --> F[静默
Silence] F --> G[路由
Route] G --> H1[Email] G --> H2[Slack] G --> H3[PagerDuty] G --> H4[Webhook]

3.6 Template Examples (模板示例)

Prometheus 在告警的注解和标签中支持模板化,也支持在控制台页面中使用模板。模板基于 Go 模板系统,可以执行查询、迭代数据、使用条件语句和格式化数据。

告警字段模板

基本模板:

1- alert: InstanceDown
2  expr: up == 0
3  for: 5m
4  labels:
5    severity: page
6  annotations:
7    summary: "实例 {{ $labels.instance }} 宕机"
8    description: "{{ $labels.instance }} (job: {{ $labels.job }}) 已宕机超过 5 分钟"

⚠️ 告警模板在每次规则迭代时对每个触发的告警执行,应保持查询和模板的轻量化。对于复杂模板,建议链接到控制台页面。


简单迭代

显示实例列表及其状态:

1{{ range query "up" }}
2  {{ .Labels.instance }} {{ .Value }}
3{{ end }}

输出示例:

1localhost:9090 1
2localhost:9100 1
3server1:9100 0

特殊变量 . 包含当前循环迭代的样本值。


显示单个值

获取特定指标值:

1{{ with query "some_metric{instance='someinstance'}" }}
2  {{ . | first | value | humanize }}
3{{ end }}

说明:

  • with 语句用于错误处理,防止查询无结果时出错
  • first 获取第一个结果
  • value 提取数值
  • humanize 格式化数字

使用控制台 URL 参数

动态查询参数:

1{{ with printf "node_memory_MemTotal{job='node',instance='%s'}" .Params.instance | query }}
2  {{ . | first | value | humanize1024 }}B
3{{ end }}

访问方式:

1console.html?instance=hostname

此时 .Params.instance 的值为 hostname


高级迭代示例

显示网络接口流量表格:

 1<table>
 2{{ range printf "node_network_receive_bytes{job='node',instance='%s',device!='lo'}" .Params.instance | query | sortByLabel "device"}}
 3  <tr><th colspan=2>{{ .Labels.device }}</th></tr>
 4  <tr>
 5    <td>接收</td>
 6    <td>{{ with printf "rate(node_network_receive_bytes{job='node',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device | query }}{{ . | first | value | humanize }}B/s{{end}}</td>
 7  </tr>
 8  <tr>
 9    <td>发送</td>
10    <td>{{ with printf "rate(node_network_transmit_bytes{job='node',instance='%s',device='%s'}[5m])" .Labels.instance .Labels.device | query }}{{ . | first | value | humanize }}B/s{{end}}</td>
11  </tr>
12{{ end }}
13</table>

说明:

  • sortByLabel "device" 按设备名称排序
  • range 循环中,. 变为循环变量,因此 .Params.instance 不再可用

定义可重用模板

定义模板:

1{{/* 定义模板 */}}
2{{define "myTemplate"}}
3  执行某些操作
4{{end}}
5
6{{/* 使用模板 */}}
7{{template "myTemplate"}}

多参数模板:

1{{define "myMultiArgTemplate"}}
2  第一个参数: {{.arg0}}
3  第二个参数: {{.arg1}}
4{{end}}
5
6{{/* 使用 args 函数传递多个参数 */}}
7{{template "myMultiArgTemplate" (args 1 2)}}

常用模板函数

函数说明示例输出
humanize格式化数字,添加单位{{ 1234567 | humanize }}1.234567M
humanize1024按 1024 进制格式化{{ 1048576 | humanize1024 }}B1MiB
humanizePercentage格式化为百分比{{ 0.8234 | humanizePercentage }}82.34%
humanizeDuration格式化持续时间{{ 3661 | humanizeDuration }}1h1m1s
humanizeTimestamp格式化时间戳{{ 1609459200 | humanizeTimestamp }}2021-01-01 00:00:00
title首字母大写{{ "hello" | title }}Hello
toUpper转大写{{ "hello" | toUpper }}HELLO
toLower转小写{{ "HELLO" | toLower }}hello
stripPort移除端口号{{ "host:8080" | stripPort }}host

实用模板示例

1. 格式化告警消息:

 1annotations:
 2  summary: |
 3    {{ $labels.job }} 服务异常
 4  
 5  description: |
 6    服务: {{ $labels.job }}
 7    实例: {{ $labels.instance }}
 8    状态: {{ if eq $value 0.0 }}宕机{{ else }}运行中{{ end }}
 9    持续时间: {{ .StartsAt | humanizeDuration }}
10  
11  metrics: |
12    CPU 使用率: {{ with query (printf "instance:cpu_usage:ratio{instance='%s'}" $labels.instance) }}{{ . | first | value | humanizePercentage }}{{ end }}
13    内存使用率: {{ with query (printf "instance:memory_usage:ratio{instance='%s'}" $labels.instance) }}{{ . | first | value | humanizePercentage }}{{ end }}

2. 动态生成 Runbook 链接:

1annotations:
2  runbook: |
3    https://wiki.example.com/runbooks/{{ $labels.alertname | toLower | reReplaceAll " " "-" }}

3. 条件格式化:

 1annotations:
 2  severity_emoji: |
 3    {{ if eq $labels.severity "critical" }}🔴{{ else if eq $labels.severity "warning" }}⚠️{{ else }}ℹ️{{ end }}
 4  
 5  message: |
 6    {{ .Annotations.severity_emoji }} {{ $labels.alertname }}
 7  
 8    {{ if gt $value 0.9 }}
 9      紧急! 使用率超过 90%
10    {{ else if gt $value 0.7 }}
11      警告: 使用率超过 70%
12    {{ else }}
13      注意: 使用率为 {{ $value | humanizePercentage }}
14    {{ end }}

4. 表格格式化:

1annotations:
2  details: |
3    | 指标 | 当前值 | 阈值 |
4    |------|--------|------|
5    | CPU 使用率 | {{ with query "instance:cpu_usage:ratio" }}{{ . | first | value | humanizePercentage }}{{ end }} | 80% |
6    | 内存使用率 | {{ with query "instance:memory_usage:ratio" }}{{ . | first | value | humanizePercentage }}{{ end }} | 80% |
7    | 磁盘使用率 | {{ with query "instance:disk_usage:ratio" }}{{ . | first | value | humanizePercentage }}{{ end }} | 80% |

模板调试技巧

1. 使用 promtool 验证模板:

1promtool check rules rules.yml

2. 在告警注解中输出调试信息:

1annotations:
2  debug: |
3    Labels: {{ $labels }}
4    Value: {{ $value }}
5    External Labels: {{ $externalLabels }}

3. 测试查询结果:

1annotations:
2  query_result: |
3    {{ with query "up{instance='localhost:9090'}" }}
4      结果数量: {{ . | len }}
5      第一个值: {{ . | first | value }}
6    {{ else }}
7      查询无结果
8    {{ end }}

总结

通过本节,我们学习了 Prometheus 规则和模板的核心内容:

Recording Rules (记录规则):

  1. 预计算频繁使用或计算昂贵的表达式
  2. 提供命名最佳实践 (level:metric:operations)
  3. 支持规则分组、评估间隔和限制配置
  4. 适用于仪表板、告警和长期存储优化

Alerting Rules (告警规则):

  1. 基于 PromQL 表达式定义告警条件
  2. 支持告警状态转换 (Inactive → Pending → Firing)
  3. 提供 forkeep_firing_for 控制告警行为
  4. 与 Alertmanager 集成实现通知路由

Template Examples (模板示例):

  1. 基于 Go 模板系统的强大模板化能力
  2. 支持在告警标签、注解和控制台中使用
  3. 提供丰富的模板函数 (humanize、格式化等)
  4. 可定义可重用模板,提高配置复用性

这些功能组合使用,能够构建强大的监控和告警系统,满足复杂的生产环境需求。


4. Querying (查询)

4.1 Basics (基础)

Prometheus 提供了一种功能强大的查询语言 PromQL (Prometheus Query Language),允许用户实时选择和聚合时间序列数据。

PromQL 查询类型

1. Instant Query (即时查询)

  • 在单个时间点评估
  • 返回该时刻的数据
  • UI 中使用 “Table” 标签页

2. Range Query (范围查询)

  • 在起始和结束时间之间的等间隔步长评估
  • 相当于在多个时间戳运行即时查询
  • UI 中使用 “Graph” 标签页
graph LR A[PromQL 查询] --> B[Instant Query
即时查询] A --> C[Range Query
范围查询] B --> D[返回单个时间点数据
Table视图] C --> E[返回时间范围数据
Graph视图]

表达式数据类型

PromQL 表达式可以评估为以下四种类型之一:

数据类型说明示例用途
Instant Vector``(即时向量)包含单个时间戳的一组时间序列,每个序列一个样本当前 CPU 使用率
Range Vector``(范围向量)包含一段时间内数据点的一组时间序列过去5分钟的请求数据
Scalar``(标量)简单的浮点数值阈值、常数
String``(字符串)简单的字符串值(当前未使用)-

查询类型限制:

  • 即时查询: 支持所有数据类型
  • 范围查询: 只支持 Scalar 和 Instant Vector

4. Querying (查询)

本部分详细介绍 PromQL 查询语言的基础知识、操作符、函数和实用示例。由于内容较多,以下是简明版本,完整文档请参考官方文档。

4.1 Basics (基础)

核心概念:

  • Instant Query: 即时查询,返回单个时间点数据
  • Range Query: 范围查询,返回时间范围内数据

数据类型:

  1. Instant Vector (即时向量) - 时间序列集合,每个序列一个样本
  2. Range Vector (范围向量) - 时间序列集合,包含一段时间的数据
  3. Scalar (标量) - 浮点数值
  4. String (字符串) - 字符串值

选择器语法:

 1# 基本选择
 2http_requests_total
 3
 4# 标签过滤
 5http_requests_total{job="prometheus", method="GET"}
 6
 7# 正则匹配
 8http_requests_total{job=~".*server", status!~"4.."}
 9
10# 范围向量
11http_requests_total[5m]
12
13# 时间修饰符
14http_requests_total offset 5m
15http_requests_total @ 1609746000

标签匹配运算符:

  • = : 等于
  • != : 不等于
  • =~ : 正则匹配
  • !~ : 正则不匹配

4.2 Operators (操作符)

算术操作符: + - * / % ^

比较操作符: == != > < >= <=

逻辑/集合操作符:

  • and - 交集
  • or - 并集
  • unless - 补集

向量匹配:

1# One-to-One 匹配
2method_code:http_errors:rate5m{code="500"} 
3  / ignoring(code) 
4  method:http_requests:rate5m
5
6# Many-to-One 匹配
7method_code:http_errors:rate5m 
8  / ignoring(code) group_left 
9  method:http_requests:rate5m

聚合操作符:

  • sum, avg, min, max, count
  • topk, bottomk, quantile
  • stddev, stdvar, group
  • count_values
1# 按标签聚合
2sum by (job) (http_requests_total)
3sum without (instance) (http_requests_total)
4
5# 取最大的 k 个值
6topk(5, http_requests_total)
7
8# 分位数
9quantile(0.95, http_request_duration_seconds)

4.3 Functions (函数)

速率函数:

1rate(http_requests_total[5m])           # 平均速率
2irate(http_requests_total[5m])          # 瞬时速率
3increase(http_requests_total[1h])       # 总增量

时间聚合函数:

1avg_over_time(node_cpu_usage[5m])
2max_over_time(node_cpu_usage[24h])
3min_over_time(node_cpu_usage[24h])
4sum_over_time(http_requests_total[1h])
5quantile_over_time(0.9, latency[5m])

直方图函数:

 1# 计算 P90 延迟
 2histogram_quantile(0.9, 
 3  rate(http_request_duration_seconds_bucket[10m])
 4)
 5
 6# 计算平均值
 7histogram_avg(rate(http_request_duration_seconds[5m]))
 8
 9# 计算区间占比
10histogram_fraction(0, 0.2, rate(http_request_duration_seconds[1h]))

数学函数:

1abs(v)            # 绝对值
2ceil(v)           # 向上取整
3floor(v)          # 向下取整
4round(v)          # 四舍五入
5sqrt(v)           # 平方根
6ln(v), log2(v), log10(v)   # 对数
7exp(v)            # 指数
8clamp_min(v, min), clamp_max(v, max)  # 钳位

缺失数据检测:

1absent(up{job="critical-service"})               # 检测指标缺失
2absent_over_time(http_requests_total{job="api"}[1h])  # 时间范围内缺失

预测函数:

1predict_linear(node_filesystem_free_bytes[1h], 4*3600)  # 预测未来值
2deriv(node_temperature_celsius[1h])     # 计算导数

标签操作:

1label_replace(v, "dst", "$1", "src", "(.*):.*")  # 替换标签
2label_join(v, "dst", ",", "src1", "src2")        # 连接标签

时间函数:

1time()                    # 当前时间戳
2timestamp(v)              # 样本时间戳
3year(), month(), day_of_month(), hour(), minute()  # 时间组件

4.4 Examples (示例)

1. 计算请求速率

1# QPS
2sum(rate(http_requests_total[5m]))
3
4# 按服务聚合
5sum by (service) (rate(http_requests_total[5m]))

2. 计算成功率/错误率

1# 成功率
2sum(rate(http_requests_total{status=~"2.."}[5m]))
3/
4sum(rate(http_requests_total[5m]))
5
6# 错误率
7sum by (service) (rate(http_requests_total{status=~"5.."}[5m]))
8/
9sum by (service) (rate(http_requests_total[5m]))

3. 计算资源使用率

 1# CPU 使用率
 2100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
 3
 4# 内存使用率
 5(node_memory_MemTotal_bytes - node_memory_MemAvail able_bytes) 
 6/ node_memory_MemTotal_bytes * 100
 7
 8# 磁盘使用率
 9(node_filesystem_size_bytes - node_filesystem_avail_bytes) 
10/ node_filesystem_size_bytes * 100

4. 计算延迟分位数

1# P50/P90/P99 延迟
2histogram_quantile(0.5, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
3histogram_quantile(0.9, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
4histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

5. Top K 查询

1# CPU 使用率最高的 5 个实例
2topk(5, rate(node_cpu_seconds_total[5m]))
3
4# 请求量最大的 10 个服务
5topk(10, sum by (service) (rate(http_requests_total[5m])))

6. 预测和趋势

1# 预测 4 小时后磁盘是否会满
2predict_linear(node_filesystem_avail_bytes[1h], 4*3600) < 0
3
4# 流量环比增长
5(rate(http_requests_total[5m]) - rate(http_requests_total[5m] offset 1h))
6/ rate(http_requests_total[5m] offset 1h)

7. 计算 Uptime

1# 运行时长(秒)
2time() - node_boot_time_seconds
3
4# 转换为天
5(time() - node_boot_time_seconds) / 86400

8. 统计计数

1# 运行中的实例数
2count(up == 1)
3
4# 每个应用的实例数
5count by (app) (up)
6
7# 不同版本的实例数
8count_values("version", build_version)

9. SLA 可用性

1# 过去 30 天的可用性百分比
2sum(rate(http_requests_total{status=~"2..|3.."}[30d]))
3/
4sum(rate(http_requests_total[30d]))
5* 100

10. 告警相关查询

 1# 检测服务宕机
 2up{job="critical-service"} == 0
 3
 4# 检测指标缺失
 5absent(up{job="critical-service"})
 6
 7# 高错误率
 8(sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))) > 0.05
 9
10# CPU 持续高负载
11avg(rate(node_cpu_seconds_total{mode!="idle"}[5m])) > 0.8

PromQL 最佳实践

1. 性能优化

  • ✅ 使用记录规则预计算复杂查询
  • ✅ 先聚合后查询,减少时间序列数量
  • ✅ 避免高基数标签
  • ✅ 使用合适的时间范围

2. 准确性

  • ✅ Counter 使用 rate()increase()
  • ✅ Gauge 使用 delta() 或直接查询
  • ✅ 计算速率时先 rate() 再聚合
  • ✅ 注意操作符优先级,必要时使用括号

3. 可读性

  • ✅ 使用有意义的记录规则名称
  • ✅ 添加注释说明查询目的
  • ✅ 将复杂查询分解为多个步骤
  • ✅ 保持一致的格式和缩进

4. 告警查询

  • ✅ 使用 rate() 而非 irate() (更稳定)
  • ✅ 设置合适的 for 持续时间避免抖动
  • ✅ 使用 absent() 检测缺失指标
  • ✅ 考虑使用布尔运算符提高可读性

4.5 API (HTTP API)

Prometheus 提供了完整的 HTTP API 用于查询数据和管理服务器。当前稳定版本API位于 /api/v1 端点。

API 响应格式

所有 API 返回 JSON 格式:

 1{
 2  "status": "success" | "error",
 3  "data": <...>,
 4  
 5  // 错误时包含
 6  "errorType": "<string>",
 7  "error": "<string>",
 8  
 9  // 警告信息(可选)
10  "warnings": ["<string>"],
11  "infos": ["<string>"]
12}

HTTP 状态码:

  • 200 - 成功
  • 400 - 参数错误
  • 422 - 表达式无法执行
  • 503 - 查询超时或中止

1. 表达式查询 API

即时查询 (Instant Query)

1# GET 请求
2GET /api/v1/query
3
4# 参数
5query=<string>        # PromQL 表达式
6time=<timestamp>      # 评估时间戳(可选,默认当前时间)
7timeout=<duration>    # 超时时间(可选)
8limit=<number>        # 返回序列数量限制(可选)

示例:

1# 查询 up 指标在特定时间点的值
2curl 'http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z'
3
4# 查询所有实例的 CPU 使用率
5curl 'http://localhost:9090/api/v1/query?query=rate(node_cpu_seconds_total[5m])'

响应示例:

 1{
 2  "status": "success",
 3  "data": {
 4    "resultType": "vector",
 5    "result": [
 6      {
 7        "metric": {
 8          "__name__": "up",
 9          "job": "prometheus",
10          "instance": "localhost:9090"
11        },
12        "value": [1435781451.781, "1"]
13      }
14    ]
15  }
16}

范围查询 (Range Query)

1GET /api/v1/query_range
2
3# 参数
4query=<string>    # PromQL 表达式
5start=<timestamp> # 开始时间
6end=<timestamp>   # 结束时间
7step=<duration>   # 查询步长
8timeout=<duration># 超时(可选)
9limit=<number>    # 序列数量限制(可选)

示例:

1# 查询过去 30 秒的 up 指标,步长 15s
2curl 'http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s'

2. 元数据查询 API

查询时间序列 (Series)

1GET /api/v1/series
2
3# 参数
4match[]=<selector>  # 序列选择器(必需,可重复)
5start=<timestamp>   # 开始时间(可选)
6end=<timestamp>     # 结束时间(可选)
7limit=<number>      # 返回数量限制(可选)

示例:

1# 查询匹配的时间序列
2curl -g 'http://localhost:9090/api/v1/series?match[]=up&match[]=process_start_time_seconds{job="prometheus"}'

查询标签名称

1GET /api/v1/labels
2
3# 参数
4start=<timestamp>     # 开始时间(可选)
5end=<timestamp>       # 结束时间(可选)
6match[]=<selector>    # 过滤选择器(可选)
7limit=<number>        # 返回数量限制(可选)

示例:

1curl 'http://localhost:9090/api/v1/labels'

响应:

 1{
 2  "status": "success",
 3  "data": [
 4    "__name__",
 5    "instance",
 6    "job",
 7    "method",
 8    "status"
 9  ]
10}

查询标签值

1GET /api/v1/label/<label_name>/values
2
3# 示例
4curl 'http://localhost:9090/api/v1/label/job/values'

3. 目标和规则查询

查询抓取目标

1GET /api/v1/targets
2
3# 参数
4state=<active|dropped|any>  # 过滤目标状态(可选)
5scrapePool=<name>            # 过滤抓取池(可选)

示例:

1curl 'http://localhost:9090/api/v1/targets?state=active'

查询规则

1GET /api/v1/rules
2
3# 参数
4type=<alert|record>       # 过滤规则类型(可选)
5rule_name[]=<name>        # 过滤规则名称(可选)
6rule_group[]=<name>       # 过滤规则组(可选)
7file[]=<path>             # 过滤文件路径(可选)
8exclude_alerts=<bool>     # 排除告警(可选)

查询告警

1GET /api/v1/alerts
2
3# 返回所有活跃告警
4curl 'http://localhost:9090/api/v1/alerts'

4. 状态和配置 API

查询配置

1GET /api/v1/status/config
2
3# 返回当前加载的配置文件(YAML格式)

查询启动标志

1GET /api/v1/status/flags
2
3# 返回 Prometheus 启动参数

查询运行时信息

1GET /api/v1/status/runtimeinfo
2
3# 返回运行时信息(启动时间、时间序列数量、内存等)

查询构建信息

1GET /api/v1/status/buildinfo
2
3# 返回版本、Git 版本号、Go 版本等

查询 TSDB 统计

1GET /api/v1/status/tsdb
2
3# 参数
4limit=<number>  # 限制返回项数(默认10)
5
6# 返回时间序列数据库的基数统计

5. 管理 API (需启用 –web.enable-admin-api)

创建快照

1POST /api/v1/admin/tsdb/snapshot
2
3# 参数
4skip_head=<bool>  # 跳过 head block(可选)

删除序列数据

1POST /api/v1/admin/tsdb/delete_series
2
3# 参数
4match[]=<selector>  # 序列选择器(必需,可重复)
5start=<timestamp>   # 开始时间(可选)
6end=<timestamp>     # 结束时间(可选)

清理墓碑

1POST /api/v1/admin/tsdb/clean_tombstones
2
3# 清理已删除的数据,释放磁盘空间

6. 实用工具 API

格式化查询

1GET /api/v1/format_query
2
3# 参数
4query=<string>  # PromQL 表达式
5
6# 返回格式化后的查询字符串
7curl 'http://localhost:9090/api/v1/format_query?query=foo/bar'

解析查询为 AST

1GET /api/v1/parse_query
2
3# 参数
4query=<string>  # PromQL 表达式
5
6# 返回查询的抽象语法树(JSON格式)

7. 常用 API 调用示例

1. 查询当前 CPU 使用率

1curl -g 'http://localhost:9090/api/v1/query' \
2  --data-urlencode 'query=100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)'

2. 查询过去 1 小时的请求速率

1curl -g 'http://localhost:9090/api/v1/query_range' \
2  --data-urlencode 'query=rate(http_requests_total[5m])' \
3  --data-urlencode "start=$(date -u -d '1 hour ago' +%s)" \
4  --data-urlencode "end=$(date -u +%s)" \
5  --data-urlencode 'step=60s'

3. 查询特定 job 的所有标签

1curl -g 'http://localhost:9090/api/v1/series?match[]=up{job="prometheus"}'

4. 查询所有 job 的标签值

1curl 'http://localhost:9090/api/v1/label/job/values'

5. 查询当前活跃的抓取目标

1curl 'http://localhost:9090/api/v1/targets?state=active' | jq '.data.activeTargets[] | {instance: .labels.instance, health: .health}'

6. 查询所有 firing 状态的告警

1curl 'http://localhost:9090/api/v1/alerts' | jq '.data.alerts[] | select(.state=="firing")'

7. 获取 Prometheus 版本信息

1curl 'http://localhost:9090/api/v1/status/buildinfo' | jq '.data.version'

8. 查询 TSDB 中的时间序列数量

1curl 'http://localhost:9090/api/v1/status/runtimeinfo' | jq '.data.timeSeriesCount'

8. API 使用最佳实践

1. 使用 POST 方式发送复杂查询

对于长查询字符串,使用 POST 避免 URL 长度限制:

1curl -X POST 'http://localhost:9090/api/v1/query' \
2  --data-urlencode 'query=sum by (job) (rate(http_requests_total{status=~"5..",path=~"/api/.*"}[5m]))'

2. 合理设置超时

1curl 'http://localhost:9090/api/v1/query?query=complex_query&timeout=30s'

3. 使用 limit 限制返回数据量

1curl 'http://localhost:9090/api/v1/query?query=up&limit=100'

4. URL 编码参数

使用 --data-urlencode 或手动编码:

1# 使用 --data-urlencode
2curl -G 'http://localhost:9090/api/v1/query' \
3  --data-urlencode 'query=http_requests_total{method="GET"}'
4
5# 手动编码
6curl 'http://localhost:9090/api/v1/query?query=http_requests_total%7Bmethod%3D%22GET%22%7D'

5. 处理 JSON 响应

使用 jq 处理 JSON:

1# 提取数据部分
2curl 'http://localhost:9090/api/v1/query?query=up' | jq '.data'
3
4# 提取所有指标值
5curl 'http://localhost:9090/api/v1/query?query=up' | jq '.data.result[].value[1]'
6
7# 检查状态
8curl 'http://localhost:9090/api/v1/query?query=up' | jq -r '.status'

6. 错误处理

1response=$(curl -s 'http://localhost:9090/api/v1/query?query=invalid_query')
2status=$(echo "$response" | jq -r '.status')
3
4if [ "$status" = "error" ]; then
5  echo "Error: $(echo "$response" | jq -r '.error')"
6  echo "Type: $(echo "$response" | jq -r '.errorType')"
7fi

9. 编程语言客户端示例

Python 示例:

 1import requests
 2import json
 3from datetime import datetime, timedelta
 4
 5# 基础配置
 6PROMETHEUS_URL = "http://localhost:9090"
 7
 8# 即时查询
 9def instant_query(query):
10    url = f"{PROMETHEUS_URL}/api/v1/query"
11    params = {"query": query}
12    response = requests.get(url, params=params)
13    return response.json()
14
15# 范围查询
16def range_query(query, start, end, step="15s"):
17    url = f"{PROMETHEUS_URL}/api/v1/query_range"
18    params = {
19        "query": query,
20        "start": start.timestamp(),
21        "end": end.timestamp(),
22        "step": step
23    }
24    response = requests.get(url, params=params)
25    return response.json()
26
27# 使用示例
28if __name__ == "__main__":
29    # 查询当前 CPU 使用率
30    result = instant_query('rate(node_cpu_seconds_total[5m])')
31    print(json.dumps(result, indent=2))
32  
33    # 查询过去 1 小时的数据
34    end = datetime.now()
35    start = end - timedelta(hours=1)
36    result = range_query('up', start, end, '60s')
37    print(json.dumps(result, indent=2))

Go 示例:

 1package main
 2
 3import (
 4    "context"
 5    "fmt"
 6    "time"
 7  
 8    "github.com/prometheus/client_golang/api"
 9    v1 "github.com/prometheus/client_golang/api/prometheus/v1"
10)
11
12func main() {
13    client, err := api.NewClient(api.Config{
14        Address: "http://localhost:9090",
15    })
16    if err != nil {
17        panic(err)
18    }
19  
20    v1api := v1.NewAPI(client)
21    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
22    defer cancel()
23  
24    // 即时查询
25    result, warnings, err := v1api.Query(ctx, "up", time.Now())
26    if err != nil {
27        panic(err)
28    }
29    if len(warnings) > 0 {
30        fmt.Printf("Warnings: %v\n", warnings)
31    }
32    fmt.Printf("Result: %v\n", result)
33  
34    // 范围查询
35    r := v1.Range{
36        Start: time.Now().Add(-time.Hour),
37        End:   time.Now(),
38        Step:  time.Minute,
39    }
40    result, warnings, err = v1api.QueryRange(ctx, "rate(http_requests_total[5m])", r)
41    if err != nil {
42        panic(err)
43    }
44    fmt.Printf("Result: %v\n", result)
45}

API 快速参考

API 端点方法用途
/api/v1/queryGET/POST即时查询
/api/v1/query_rangeGET/POST范围查询
/api/v1/seriesGET/POST查询时间序列
/api/v1/labelsGET/POST查询标签名称
/api/v1/label/<name>/valuesGET查询标签值
/api/v1/targetsGET查询抓取目标
/api/v1/rulesGET查询规则
/api/v1/alertsGET查询告警
/api/v1/status/configGET查询配置
/api/v1/status/flagsGET查询标志
/api/v1/status/runtimeinfoGET查询运行时信息
/api/v1/status/buildinfoGET查询构建信息
/api/v1/status/tsdbGET查询 TSDB 统计
/api/v1/admin/tsdb/snapshotPOST创建快照
/api/v1/admin/tsdb/delete_seriesPOST删除序列
/api/v1/admin/tsdb/clean_tombstonesPOST清理墓碑

5. Storage (存储)

Prometheus 包含本地磁盘时间序列数据库,同时可选地与远程存储系统集成。

5.1 本地存储

磁盘布局

Block 结构:

  • 采样数据按 2 小时 分组为 block
  • 每个 block 包含:
    • chunks/ - 时间序列样本数据(最多 512MB/文件)
    • index - 索引文件(指标名称和标签到时间序列的映射)
    • meta.json - 元数据文件
    • tombstones - 删除记录

目录结构示例:

 1./data
 2├── 01BKGV7JBM69T2G1BGBGM6KB12/    # 2小时 block
 3│   ├── chunks/
 4│   │   └── 000001
 5│   ├── index
 6│   ├── tombstones
 7│   └── meta.json
 8├── chunks_head/                    # Head block (内存中)
 9│   └── 000001
10└── wal/                            # 写前日志
11    ├── 000000002
12    └── checkpoint.00000001/
13        └── 00000000

WAL (Write-Ahead Log)

特性:

  • 保护内存中的当前 block 防止崩溃丢失
  • 文件大小: 128MB/段
  • 最少保留: 3 个 WAL 文件
  • 高流量服务器可能保留更多(至少 2 小时原始数据)

启用 WAL 压缩:

1--storage.tsdb.wal-compression
2# 可将 WAL 大小减半,CPU 开销很小
3# v2.11.0 引入,v2.20.0 默认启用

压缩 (Compaction)

  • 初始 2 小时 blocks 会在后台压缩成更大的 blocks
  • 最大 block 大小: 保留时间的 10%31 天(取较小值)

存储配置参数

参数说明默认值
--storage.tsdb.path数据库目录路径data/
--storage.tsdb.retention.time样本保留时间15d
--storage.tsdb.retention.size最大存储字节数0(禁用)
--storage.tsdb.wal-compression启用 WAL 压缩true(v2.20+)

支持的时间单位: y, w, d, h, m, s, ms

支持的存储单位: B, KB, MB, GB, TB, PB, EB


容量规划

估算公式:

1所需磁盘空间 = 保留时间(秒) × 每秒采样数 × 每样本字节数

每样本平均大小: 1-2 bytes

示例计算:

1# 假设: 10万时间序列,15秒抓取间隔,保留15天
2采样率 = 100,000 / 15 = 6,666 samples/s
3所需空间 = 15天 * 86400秒 * 6,666 * 2 bytes 
4         ≈ 17.3 GB

降低采样率的方法:

  1. 减少时间序列数量(更少的目标或每个目标更少的序列)
  2. 增加抓取间隔
  3. 使用记录规则预聚合

保留策略

时间保留:

1--storage.tsdb.retention.time=30d

大小保留:

1--storage.tsdb.retention.size=50GB

建议:

  • 如果同时设置时间和大小保留,首先触发的生效
  • 大小保留建议设置为磁盘空间的 80-85%
  • 过期 block 清理在后台进行,最多可能需要 2 小时

数据完整性

文件系统要求:

  • ✅ 必须使用 POSIX 兼容的文件系统
  • 不支持 NFS(包括 AWS EFS)
  • ✅ 强烈推荐使用 本地文件系统

备份建议:

  • 使用快照 API 进行备份
  • 非快照备份可能丢失最后 2 小时的数据(上次 WAL 同步以来)

数据恢复:

如果本地存储损坏:

  1. 备份存储目录
  2. 从备份恢复损坏的 block 目录
  3. 最后手段: 删除损坏的文件/WAL

5.2 远程存储集成

Prometheus 通过以下方式与远程存储系统集成:

四种集成方式

graph LR A[Prometheus 服务器] -->|Remote Write| B[远程存储] C[其他客户端] -->|Remote Write| A B -->|Remote Read| A A -->|Remote Read| D[查询客户端]
  1. Remote Write (写出): Prometheus 将采集的样本写入远程 URL
  2. Remote Write Receiver (接收): Prometheus 接收其他客户端的样本
  3. Remote Read (读取): Prometheus 从远程 URL 读取样本数据
  4. Remote Read Server (提供): Prometheus 为客户端提供样本数据

协议特性

编码方式:

  • Snappy 压缩的 Protocol Buffer
  • 通过 HTTP 传输

版本:

  • Write 协议: 1.0 (稳定) + 2.0 (实验性)
  • Read 协议: 尚未稳定

配置位置: prometheus.ymlremote_writeremote_read 部分


Remote Write Receiver

启用方式:

1prometheus --web.enable-remote-write-receiver

端点: /api/v1/write

⚠️ 注意: 这不是高效的样本采集方式,仅用于特定低流量场景,不适合替代抓取机制


Remote Read 限制

  • Prometheus 仅从远程端获取原始序列数据
  • 所有 PromQL 评估仍在 Prometheus 本地进行
  • 存在可扩展性限制(所有数据需先加载到查询服务器)
  • 完全分布式 PromQL 评估目前不可行

5.3 数据回填 (Backfilling)

OpenMetrics 格式回填

用途: 从其他监控系统或时间序列数据库迁移数据

使用方法:

1# 从 OpenMetrics 格式创建 blocks
2promtool tsdb create-blocks-from openmetrics <input_file> [<output_dir>]
3
4# 移动生成的 blocks 到 Prometheus 数据目录
5mv <output_dir>/* /path/to/prometheus/data/

注意事项:

  • ⚠️ 不要回填最近 3 小时的数据(可能与 head block 重叠)
  • 每个 block 包含 2 小时的数据
  • 需要启用 --storage.tsdb.allow-overlapping-blocks (v2.38 及以下)
  • 回填数据受保留策略约束

调整 Block 大小:

1# 使用更大的 block 持续时间(适合回填大量历史数据)
2promtool tsdb create-blocks-from openmetrics input.txt \
3  --max-block-duration=7d

⚠️ 注意: 大 block 会影响基于时间的保留策略效果


Recording Rules 回填

用途: 为新创建的记录规则生成历史数据

使用方法:

1promtool tsdb create-blocks-from rules \
2  --start 1617079873 \
3  --end 1617097873 \
4  --url http://prometheus:9090 \
5  rules.yaml rules2.yaml

限制:

  • 重复运行会创建重复数据
  • 所有规则都会被评估
  • 同组规则无法看到其他规则的结果
  • 告警会被忽略

6. Federation (联邦)

Federation 允许一个 Prometheus 服务器从另一个 Prometheus 服务器抓取选定的时间序列。

6.1 使用场景

1. 分层联邦 (Hierarchical Federation)

用于扩展到拥有数十个数据中心和数百万节点的环境。

graph TD A[全局 Prometheus
聚合数据] --> B[数据中心1 Prometheus
详细数据] A --> C[数据中心2 Prometheus
详细数据] A --> D[数据中心3 Prometheus
详细数据] B --> E[实例级监控] C --> F[实例级监控] D --> G[实例级监控]

架构特点:

  • 下层服务器: 采集详细数据(实例级)
  • 上层服务器: 采集聚合数据(job级)
  • 提供全局聚合视图 + 本地详细视图

2. 跨服务联邦 (Cross-Service Federation)

一个服务的 Prometheus 从另一个服务的 Prometheus 抓取数据,实现跨服务查询和告警。

示例场景:

  • 集群调度器: 提供资源使用信息(CPU、内存)
  • 服务实例: 提供应用特定指标
  • 通过联邦合并两类指标到一个服务器

6.2 配置 Federation

Federation 端点: /federate

必需参数: match[] - 选择要暴露的时间序列

选择器示例:

  • up
  • {job="api-server"}
  • {__name__=~"job:.*"}

完整配置示例:

 1scrape_configs:
 2  - job_name: 'federate'
 3    scrape_interval: 15s
 4  
 5    # 不覆盖源服务器暴露的标签
 6    honor_labels: true
 7  
 8    # Federation 端点
 9    metrics_path: '/federate'
10  
11    # 选择要联邦的指标
12    params:
13      'match[]':
14        - '{job="prometheus"}'           # 所有 prometheus job 的指标
15        - '{__name__=~"job:.*"}'         # 所有 job: 开头的指标(聚合指标)
16        - '{__name__=~"instance:.*"}'    # 所有 instance: 开头的指标
17  
18    static_configs:
19      - targets:
20          - 'source-prometheus-1:9090'
21          - 'source-prometheus-2:9090'
22          - 'source-prometheus-3:9090'

关键配置说明:

配置项说明必需
honor_labels: true保留源服务器的标签,不覆盖
metrics_path: '/federate'Federation 端点路径
params.match[]时间序列选择器(可多个)

最佳实践:

1. 只联邦必要的指标

1params:
2  'match[]':
3    # ❌ 不好 - 联邦所有指标
4    - '{__name__=~".+"}'
5  
6    # ✅ 好 - 只联邦聚合指标
7    - '{__name__=~"job:.*"}'
8    - '{__name__=~"instance:.*"}'

2. 使用记录规则进行预聚合

下层服务器创建记录规则:

 1groups:
 2  - name: federation_aggregation
 3    interval: 30s
 4    rules:
 5      # 为联邦准备的聚合指标
 6      - record: job:http_requests_total:rate5m
 7        expr: sum by (job) (rate(http_requests_total[5m]))
 8    
 9      - record: instance:node_cpu:avg
10        expr: avg by (instance) (rate(node_cpu_seconds_total[5m]))

上层服务器联邦这些指标:

1params:
2  'match[]':
3    - '{__name__=~"job:.*"}'
4    - '{__name__=~"instance:.*"}'

3. 合理设置抓取间隔

1scrape_interval: 30s  # Federation 可以使用较长间隔

原生直方图支持:

要通过 federation 抓取原生直方图,需要:

1# 在抓取服务器启用原生直方图
2prometheus --enable-feature=native-histograms

⚠️ 注意: 如果联邦指标包含混合样本类型(float64、counter histogram、gauge histogram),会违反 protobuf 格式规则,但 Prometheus 仍能正确采集


7. Management API (管理 API)

Prometheus 提供了一组管理 API 用于自动化和集成。

7.1 健康检查 API

健康检查 (Health Check)

1GET  /-/healthy
2HEAD /-/healthy

用途: 检查 Prometheus 是否正在运行

返回: 始终返回 200

示例:

1curl -s http://localhost:9090/-/healthy
2# 返回 "Prometheus Server is Healthy."
3
4# 或仅检查状态码
5curl -I http://localhost:9090/-/healthy

就绪检查 (Readiness Check)

1GET  /-/ready
2HEAD /-/ready

用途: 检查 Prometheus 是否准备好处理查询

返回: 准备好时返回 200,否则返回 503

示例:

 1# 检查就绪状态
 2curl -s http://localhost:9090/-/ready
 3
 4# Kubernetes 就绪探针配置
 5readinessProbe:
 6  httpGet:
 7    path: /-/ready
 8    port: 9090
 9  initialDelaySeconds: 30
10  periodSeconds: 5

区别:

  • /-/healthy: 服务进程是否存活
  • /-/ready: 服务是否准备好处理流量(WAL 重放完成等)

7.2 配置管理 API

重新加载配置

1POST /-/reload
2PUT  /-/reload

用途: 触发配置和规则文件重新加载

前提: 需启用 --web.enable-lifecycle 标志

示例:

1# 重新加载配置
2curl -X POST http://localhost:9090/-/reload
3
4# 或使用 PUT
5curl -X PUT http://localhost:9090/-/reload

替代方法 - 发送 SIGHUP 信号:

1# 查找 Prometheus 进程 ID
2ps aux | grep prometheus
3
4# 发送 SIGHUP 信号
5kill -HUP <PID>

优雅关闭

1POST /-/quit
2PUT  /-/quit

用途: 触发 Prometheus 优雅关闭

前提: 需启用 --web.enable-lifecycle 标志

示例:

1# 优雅关闭 Prometheus
2curl -X POST http://localhost:9090/-/quit

替代方法 - 发送 SIGTERM 信号:

1# 优雅关闭
2kill -TERM <PID>
3
4# 或使用 systemd
5systemctl stop prometheus

7.3 使用场景

1. Kubernetes 健康和就绪探针

 1apiVersion: v1
 2kind: Pod
 3metadata:
 4  name: prometheus
 5spec:
 6  containers:
 7  - name: prometheus
 8    image: prom/prometheus:latest
 9    ports:
10    - containerPort: 9090
11  
12    # 存活探针
13    livenessProbe:
14      httpGet:
15        path: /-/healthy
16        port: 9090
17      initialDelaySeconds: 30
18      periodSeconds: 10
19      timeoutSeconds: 5
20      failureThreshold: 3
21  
22    # 就绪探针
23    readinessProbe:
24      httpGet:
25        path: /-/ready
26        port: 9090
27      initialDelaySeconds: 30
28      periodSeconds: 5
29      timeoutSeconds: 3
30      successThreshold: 1
31      failureThreshold: 3

2. 自动配置更新脚本

 1#!/bin/bash
 2
 3# 更新 Prometheus 配置文件
 4cp /path/to/new/prometheus.yml /etc/prometheus/prometheus.yml
 5
 6# 验证配置
 7if promtool check config /etc/prometheus/prometheus.yml; then
 8  echo "Config valid, reloading..."
 9  
10  # 重新加载配置
11  curl -X POST http://localhost:9090/-/reload
12  
13  if [ $? -eq 0 ]; then
14    echo "Successfully reloaded configuration"
15  else
16    echo "Failed to reload configuration"
17    # 恢复旧配置
18    cp /path/to/backup/prometheus.yml /etc/prometheus/prometheus.yml
19    exit 1
20  fi
21else
22  echo "Invalid config, not reloading"
23  exit 1
24fi

3. 监控脚本

 1import requests
 2import time
 3
 4PROMETHEUS_URL = "http://localhost:9090"
 5
 6def check_health():
 7    try:
 8        response = requests.get(f"{PROMETHEUS_URL}/-/healthy", timeout=5)
 9        return response.status_code == 200
10    except:
11        return False
12
13def check_ready():
14    try:
15        response = requests.get(f"{PROMETHEUS_URL}/-/ready", timeout=5)
16        return response.status_code == 200
17    except:
18        return False
19
20# 持续监控
21while True:
22    health = check_health()
23    ready = check_ready()
24  
25    print(f"Health: {'✓' if health else '✗'} | Ready: {'✓' if ready else '✗'}")
26  
27    if health and not ready:
28        print("⚠️  Prometheus is healthy but not ready (possibly replaying WAL)")
29    elif not health:
30        print("🔴 Prometheus is not healthy!")
31  
32    time.sleep(10)

4. CI/CD 集成

 1# GitLab CI 示例
 2deploy:
 3  stage: deploy
 4  script:
 5    # 部署新配置
 6    - kubectl apply -f prometheus-config.yaml
 7  
 8    # 等待 ConfigMap 更新
 9    - sleep 5
10  
11    # 触发重新加载
12    - |
13      kubectl exec -n monitoring prometheus-0 -- \
14        wget --post-data="" -O- http://localhost:9090/-/reload
15  
16    # 验证重新加载成功
17    - |
18      kubectl exec -n monitoring prometheus-0 -- \
19        wget -O- http://localhost:9090/-/ready

总结

Storage (存储):

  1. 本地存储采用 2 小时 block 结构
  2. WAL 保护内存数据防止崩溃
  3. 支持时间和大小两种保留策略
  4. 可通过 Remote Write/Read 集成远程存储
  5. 支持数据回填用于迁移和规则历史数据生成

Federation (联邦):

  1. 实现 Prometheus 服务器之间的数据共享
  2. 支持分层和跨服务两种场景
  3. 使用 /federate 端点和 match[] 参数
  4. 建议只联邦聚合指标以提高效率

Management API (管理API):

  1. /-/healthy - 健康检查(存活探针)
  2. /-/ready - 就绪检查(就绪探针)
  3. /-/reload - 重新加载配置
  4. /-/quit - 优雅关闭
  5. 后两者需启用 --web.enable-lifecycle

8. Command Line (命令行工具)

8.1 Prometheus 命令

Prometheus 监控服务器启动命令。

基本用法

1prometheus [flags]

核心启动参数

配置文件:

1# 指定配置文件
2--config.file=prometheus.yml          # 默认: prometheus.yml
3
4# 配置自动重载间隔
5--config.auto-reload-interval=30s     # 默认: 30s

Web 服务器:

 1# 监听地址(可重复指定)
 2--web.listen-address=0.0.0.0:9090     # 默认: 0.0.0.0:9090
 3
 4# 外部访问 URL
 5--web.external-url=http://prometheus.example.com
 6
 7# TLS/认证配置
 8--web.config.file=/path/to/web-config.yml
 9
10# 页面标题
11--web.page-title="Production Prometheus"
12
13# CORS 源
14--web.cors.origin='https?://(domain1|domain2).com'

启用功能:

 1# 启用生命周期 API(重载/关闭)
 2--web.enable-lifecycle
 3
 4# 启用管理 API
 5--web.enable-admin-api
 6
 7# 启用 Remote Write 接收器
 8--web.enable-remote-write-receiver
 9
10# 启用 OTLP 接收器
11--web.enable-otlp-receiver

存储配置

本地存储 (Server 模式):

 1# 数据目录
 2--storage.tsdb.path=data/
 3
 4# 时间保留
 5--storage.tsdb.retention.time=15d     # 单位: y,w,d,h,m,s,ms
 6
 7# 大小保留
 8--storage.tsdb.retention.size=50GB    # 单位: B,KB,MB,GB,TB,PB,EB
 9
10# 禁用锁文件
11--storage.tsdb.no-lockfile

Agent 模式存储:

1# Agent 数据目录
2--storage.agent.path=data-agent/
3
4# WAL 压缩
5--storage.agent.wal-compression=true
6
7# 保留时间
8--storage.agent.retention.min-time=0h
9--storage.agent.retention.max-time=0h

远程存储:

1# 刷新截止时间
2--storage.remote.flush-deadline=1m
3
4# Remote Read 限制
5--storage.remote.read-sample-limit=50000000
6--storage.remote.read-concurrent-limit=10

查询配置

 1# 查询超时
 2--query.timeout=2m
 3
 4# 最大并发查询数
 5--query.max-concurrency=20
 6
 7# 最大样本数
 8--query.max-samples=50000000
 9
10# 回溯时长
11--query.lookback-delta=5m

规则和告警配置

规则评估:

 1# 最大并发规则评估数
 2--rules.max-concurrent-evals=4
 3
 4# 告警 for 容错时间
 5--rules.alert.for-outage-tolerance=1h
 6
 7# 告警 for 宽限期
 8--rules.alert.for-grace-period=10m
 9
10# 告警重发延迟
11--rules.alert.resend-delay=1m

Alertmanager:

1# 通知队列容量
2--alertmanager.notification-queue-capacity=10000
3
4# 批量通知大小
5--alertmanager.notification-batch-size=256
6
7# 关闭时清空队列
8--alertmanager.drain-notification-queue-on-shutdown=true

特性标志

 1--enable-feature=feature1,feature2
 2
 3# 可用特性:
 4# - exemplar-storage              示例存储
 5# - native-histograms             原生直方图
 6# - promql-experimental-functions 实验性 PromQL 函数
 7# - extra-scrape-metrics          额外抓取指标
 8# - memory-snapshot-on-shutdown   关闭时内存快照
 9# - auto-gomaxprocs               自动 GOMAXPROCS
10# - otlp-deltatocumulative        OTLP Delta 转累积

日志配置

1# 日志级别
2--log.level=info                # 选项: debug, info, warn, error
3
4# 日志格式
5--log.format=logfmt             # 选项: logfmt, json

Agent 模式

1# 以 Agent 模式运行
2--agent
3
4# Agent 模式特点:
5# - 仅写入 Remote Write,不存储本地数据
6# - 不支持查询
7# - 更轻量级

完整启动示例

生产环境配置:

 1prometheus \
 2  --config.file=/etc/prometheus/prometheus.yml \
 3  --web.listen-address=0.0.0.0:9090 \
 4  --web.external-url=https://prometheus.example.com \
 5  --web.enable-lifecycle \
 6  --web.enable-admin-api \
 7  --storage.tsdb.path=/var/lib/prometheus \
 8  --storage.tsdb.retention.time=30d \
 9  --storage.tsdb.retention.size=50GB \
10  --query.timeout=2m \
11  --query.max-concurrency=20 \
12  --log.level=info \
13  --log.format=json

Agent 模式:

1prometheus \
2  --agent \
3  --config.file=/etc/prometheus/agent.yml \
4  --storage.agent.path=/var/lib/prometheus-agent \
5  --web.listen-address=0.0.0.0:9090 \
6  --enable-feature=native-histograms

8.2 Promtool 工具

Prometheus 监控系统的工具箱,用于验证、测试、查询和调试。

基本用法

1promtool [flags] <command> [args...]

常用命令列表

命令说明
check检查资源有效性
query对 Prometheus 服务器执行查询
test单元测试
tsdbTSDB 相关命令
debug获取调试信息
push推送数据到 Prometheus
promqlPromQL 格式化和编辑(实验性)

1. Check 命令 - 验证配置

检查配置文件:

 1# 检查 Prometheus 配置
 2promtool check config prometheus.yml
 3
 4# 只检查语法
 5promtool check config --syntax-only prometheus.yml
 6
 7# 启用 linting
 8promtool check config --lint=all prometheus.yml
 9
10# Agent 模式配置
11promtool check config --agent agent.yml

检查规则文件:

1# 检查规则文件
2promtool check rules /path/to/rules/*.yml
3
4# 启用 linting
5promtool check rules --lint=all rules.yml
6
7# 忽略未知字段
8promtool check rules --ignore-unknown-fields rules.yml

检查 Web 配置:

1promtool check web-config web-config.yml

检查服务发现:

1# 执行服务发现并查看结果
2promtool check service-discovery prometheus.yml job_name
3
4# 设置超时
5promtool check service-discovery --timeout=30s prometheus.yml job_name

检查 Prometheus 健康状态:

1# 健康检查
2promtool check healthy --url=http://localhost:9090
3
4# 就绪检查
5promtool check ready --url=http://localhost:9090

检查指标格式:

1# 检查指标有效性
2cat metrics.prom | promtool check metrics
3
4# 从远程检查
5curl -s http://localhost:9090/metrics | promtool check metrics

2. Query 命令 - 执行查询

即时查询:

1# 基本即时查询
2promtool query instant http://localhost:9090 'up'
3
4# 指定时间
5promtool query instant \
6  --time='2024-01-01T12:00:00Z' \
7  http://localhost:9090 \
8  'rate(http_requests_total[5m])'

范围查询:

1promtool query range \
2  --start='2024-01-01T00:00:00Z' \
3  --end='2024-01-01T01:00:00Z' \
4  --step=60s \
5  http://localhost:9090 \
6  'rate(http_requests_total[5m])'

查询时间序列:

1# 查询匹配的序列
2promtool query series \
3  --match='{job="prometheus"}' \
4  --match='{__name__=~"job:.*"}' \
5  --start='2024-01-01T00:00:00Z' \
6  --end='2024-01-01T01:00:00Z' \
7  http://localhost:9090

查询标签值:

1# 查询标签值
2promtool query labels \
3  --match='{job="prometheus"}' \
4  http://localhost:9090 \
5  job

分析查询:

1# 分析直方图使用模式
2promtool query analyze \
3  --server=http://localhost:9090 \
4  --type=histogram \
5  --duration=1h \
6  --match='{__name__=~"http_request_duration.*"}'

3. Test 命令 - 单元测试

测试规则:

 1# 运行规则单元测试
 2promtool test rules test.yml
 3
 4# 运行特定测试组
 5promtool test rules --run='TestGroup.*' test.yml
 6
 7# 启用调试
 8promtool test rules --debug test.yml
 9
10# 显示差异
11promtool test rules --diff test.yml
12
13# 输出 JUnit XML
14promtool test rules --junit=results.xml test.yml

测试文件示例:

 1# test.yml
 2rule_files:
 3  - rules.yml
 4
 5evaluation_interval: 1m
 6
 7tests:
 8  - interval: 1m
 9    input_series:
10      - series: 'http_requests_total{job="api",instance="0"}'
11        values: '0 100 200 300 400'
12  
13    alert_rule_test:
14      - eval_time: 5m
15        alertname: HighRequestRate
16        exp_alerts:
17          - exp_labels:
18              severity: warning
19              job: api
20            exp_annotations:
21              summary: "High request rate"

4. TSDB 命令 - 数据库操作

列出 Blocks:

1# 列出所有 blocks
2promtool tsdb list data/
3
4# 人类可读格式
5promtool tsdb list -r data/

分析 TSDB:

 1# 分析数据库
 2promtool tsdb analyze data/
 3
 4# 扩展分析
 5promtool tsdb analyze --extended data/
 6
 7# 限制结果数量
 8promtool tsdb analyze --limit=50 data/
 9
10# 分析特定序列
11promtool tsdb analyze --match='{job="api"}' data/

导出数据:

 1# 导出为 OpenMetrics 格式
 2promtool tsdb dump-openmetrics \
 3  --min-time=1609459200000 \
 4  --max-time=1609545600000 \
 5  --match='{job="prometheus"}' \
 6  data/
 7
 8# 导出原始数据
 9promtool tsdb dump \
10  --min-time=1609459200000 \
11  --max-time=1609545600000 \
12  data/

回填数据:

 1# 从 OpenMetrics 文件创建 blocks
 2promtool tsdb create-blocks-from openmetrics \
 3  input.txt \
 4  data/
 5
 6# 为记录规则回填
 7promtool tsdb create-blocks-from rules \
 8  --start=1609459200 \
 9  --end=1609545600 \
10  --url=http://localhost:9090 \
11  --output-dir=data/ \
12  --eval-interval=15s \
13  rules.yml

性能基准测试:

1promtool tsdb bench write \
2  --metrics=10000 \
3  --scrapes=3000 \
4  --out=benchout \
5  samples.json

5. Debug 命令 - 调试信息

获取性能分析数据:

1promtool debug pprof http://localhost:9090

获取指标:

1promtool debug metrics http://localhost:9090

获取所有调试信息:

1promtool debug all http://localhost:9090

6. Push 命令 - 推送数据

 1# 推送指标到 Remote Write 端点
 2promtool push metrics \
 3  --label=job=test \
 4  --label=instance=local \
 5  --timeout=30s \
 6  http://localhost:9090/api/v1/write \
 7  metrics.txt
 8
 9# 从标准输入读取
10cat metrics.txt | promtool push metrics http://localhost:9090/api/v1/write

7. PromQL 命令 - 格式化 (实验性)

需要 --experimental 标志。

格式化查询:

1promtool --experimental promql format \
2  'sum(rate(http_requests_total[5m]))by(job)'
3
4# 输出:
5# sum by(job) (rate(http_requests_total[5m]))

编辑标签匹配器:

 1# 设置标签
 2promtool --experimental promql label-matchers set \
 3  --type='=' \
 4  'up' \
 5  'job' \
 6  'prometheus'
 7# 输出: up{job="prometheus"}
 8
 9# 删除标签
10promtool --experimental promql label-matchers delete \
11  'up{job="prometheus",instance="localhost"}' \
12  'instance'
13# 输出: up{job="prometheus"}

实用 Shell 脚本示例

自动化配置验证:

 1#!/bin/bash
 2# validate-config.sh
 3
 4set -e
 5
 6CONFIG_FILE="prometheus.yml"
 7RULES_DIR="/etc/prometheus/rules"
 8
 9echo "Validating Prometheus configuration..."
10if promtool check config "$CONFIG_FILE"; then
11    echo "✓ Configuration is valid"
12else
13    echo "✗ Configuration is invalid"
14    exit 1
15fi
16
17echo "Validating rules..."
18for rule_file in "$RULES_DIR"/*.yml; do
19    if promtool check rules "$rule_file"; then
20        echo "✓ $rule_file is valid"
21    else
22        echo "✗ $rule_file is invalid"
23        exit 1
24    fi
25done
26
27echo "All checks passed!"

CI/CD 集成示例:

 1#!/bin/bash
 2# ci-validate.sh
 3
 4# 验证所有配置
 5find . -name "*.yml" -type f | while read -r file; do
 6    if [[ $file == *"prometheus"* ]]; then
 7        promtool check config "$file" || exit 1
 8    elif [[ $file == *"rules"* ]]; then
 9        promtool check rules "$file" || exit 1
10    fi
11done
12
13# 运行单元测试
14promtool test rules tests/*.yml || exit 1
15
16echo "✓ All validations passed"

查询性能测试:

 1#!/bin/bash
 2# query-benchmark.sh
 3
 4PROMETHEUS_URL="http://localhost:9090"
 5QUERIES=(
 6    "up"
 7    "rate(http_requests_total[5m])"
 8    "histogram_quantile(0.99, sum by(le) (rate(http_request_duration_seconds_bucket[5m])))"
 9)
10
11for query in "${QUERIES[@]}"; do
12    echo "Testing: $query"
13    time promtool query instant "$PROMETHEUS_URL" "$query"
14    echo "---"
15done

命令行工具最佳实践

1. 配置验证

  • ✅ 在重载配置前始终使用 promtool check config
  • ✅ 在 CI/CD 中集成配置验证
  • ✅ 使用 --lint 检查常见问题

2. 规则测试

  • ✅ 为所有规则编写单元测试
  • ✅ 在 CI/CD 中运行 promtool test rules
  • ✅ 使用 --debug 排查测试问题

3. TSDB 维护

  • ✅ 定期使用 tsdb analyze 检查基数问题
  • ✅ 使用 tsdb list 监控 block 大小
  • ✅ 在迁移前使用 tsdb dump 导出数据

4. 查询优化

  • ✅ 使用 query analyze 分析查询模式
  • ✅ 在生产环境测试前先用 promtool query 验证
  • ✅ 使用 --format 参数获取不同格式的输出

5. 安全性

  • ✅ 使用 --http.config.file 配置 TLS 客户端
  • ✅ 避免在命令行中暴露敏感信息
  • ✅ 使用环境变量或配置文件传递凭证

9. Migration (迁移指南)

Prometheus 3.0 迁移指南

Prometheus 3.0 包含重大变更。本指南帮助从 2.x 迁移到 3.0+。

已移除的特性标志

以下特性标志已移除并成为默认行为:

旧特性标志新行为说明
promql-at-modifier默认启用@ 修饰符
promql-negative-offset默认启用负偏移支持
new-service-discovery-manager默认启用新服务发现管理器
expand-external-labels默认启用外部标签支持环境变量 ${var}
no-default-scrape-port默认启用不再自动添加端口
agent使用 --agent独立的 Agent 标志
remote-write-receiver使用 --web.enable-remote-write-receiver独立的标志
auto-gomemlimit默认启用自动设置 GOMEMLIMIT
auto-gomaxprocs默认启用自动设置 GOMAXPROCS

⚠️ 注意: 继续使用这些标志会记录警告


配置变更

1. 经典直方图配置重命名:

1# v2.x
2scrape_configs:
3  - job_name: 'app'
4    scrape_classic_histograms: true  # ❌ 旧名称
5
6# v3.0
7scrape_configs:
8  - job_name: 'app'
9    always_scrape_classic_histograms: true  # ✅ 新名称

2. Remote Write HTTP/2 默认值变更:

1# v3.0 默认为 false,需显式启用
2remote_write:
3  - url: https://remote-storage.example.com
4    http_config:
5      enable_http2: true  # 显式启用 HTTP/2

PromQL 变更

1. 正则表达式匹配换行符

1# v3.0 中 . 匹配换行符
2{label=~".*"}  # 现在匹配包含 \n 的字符串
3
4# 如需 v2 行为,使用 [^\n]
5{label=~"foo[^\n]*"}  # 不匹配换行符

2. 范围选择器变更 (左开右闭)

1# 假设样本间隔 1 分钟
2foo[5m]  # v2: 可能返回 5 或 6 个样本
3         # v3: 始终返回 5 个样本

影响:

  • 子查询受影响最大
  • foo[1m:1m] 在 v3 中只返回 1 个点(不足以计算 rate)
  • 修复: 扩展窗口 foo[2m:1m] 返回 2 个点

3. holt_winters 函数重命名:

1# v2.x
2holt_winters(metric[5m], 0.5, 0.5)  # ❌
3
4# v3.0 (需启用实验性函数)
5double_exponential_smoothing(metric[5m], 0.5, 0.5)  # ✅
6
7# 启动参数
8prometheus --enable-feature=promql-experimental-functions

抓取协议变更

Prometheus 3.0 对 Content-Type 头更严格:

v2.x 行为:

  • 缺少 Content-Type → 默认为文本格式
  • 可能导致数据错误

v3.0 行为:

  • 缺少/无效 Content-Type → 抓取失败
  • 需配置 fallback 协议:
1scrape_configs:
2  - job_name: 'legacy_app'
3    fallback_scrape_protocol: PrometheusText0.0.4

其他重要变更

1. TSDB 格式和降级

  • TSDB 格式在 v2.55 已变更
  • v3.0 只能降级到 v2.55+,不能更低
  • 建议: 先升级到 v2.55 测试,再升级到 v3.0

2. UTF-8 指标名称支持

 1# 保留旧验证行为(全局)
 2global:
 3  metric_name_validation_scheme: legacy
 4
 5# 或按 job 配置
 6scrape_configs:
 7  - job_name: 'new_app'
 8    metric_name_validation_scheme: utf8
 9  - job_name: 'legacy_app'
10    metric_name_validation_scheme: legacy

3. 日志格式变更

1# v2.x
2ts=2024-10-23T22:01:06.074Z caller=main.go:627 level=info msg="..."
3
4# v3.0
5time=2024-10-24T00:03:07.542+02:00 level=INFO source=main.go:640 msg="..."

4. lequantile 标签归一化

 1# v2.x
 2my_histogram{le="1"}  # 文本格式
 3my_histogram{le="1.0"}  # protobuf 格式
 4
 5# v3.0 统一归一化
 6my_histogram{le="1.0"}  # 所有格式
 7
 8# 需更新查询
 9# ❌ le="1"
10# ✅ le="1.0"

5. Alertmanager API v1 移除

1# v2.x
2alerting:
3  alertmanagers:
4    - api_version: v1  # ❌ 不再支持
5
6# v3.0 (需要 Alertmanager 0.16.0+)
7alerting:
8  alertmanagers:
9    - api_version: v2  # ✅

10. Feature Flags (特性标志)

使用 --enable-feature 启用实验性或破坏性变更的特性。

1prometheus --enable-feature=feature1,feature2

可用特性标志

1. exemplar-storage - 示例存储

1--enable-feature=exemplar-storage
  • 存储 OpenMetrics 示例(如 trace_id)
  • 固定大小循环缓冲区
  • 每个示例约 100 字节内存
  • 持久化到 WAL

2. native-histograms - 原生直方图

1--enable-feature=native-histograms
  • 采集原生直方图(高分辨率直方图)
  • 实验性: 可能有破坏性变更
  • 使用 protobuf 格式
  • 可与经典直方图共存

配置示例:

1scrape_configs:
2  - job_name: 'app'
3    always_scrape_classic_histograms: true  # 同时保留经典直方图

3. extra-scrape-metrics - 额外抓取指标

1--enable-feature=extra-scrape-metrics

添加以下指标:

  • scrape_timeout_seconds - 配置的超时时间
  • scrape_sample_limit - 样本限制
  • scrape_body_size_bytes - 响应体大小

用途:

1# 检查接近超时的目标
2scrape_duration_seconds / scrape_timeout_seconds > 0.9
3
4# 检查接近限制的目标
5scrape_samples_post_metric_relabeling / (scrape_sample_limit > 0) > 0.9

4. memory-snapshot-on-shutdown - 关闭时内存快照

1--enable-feature=memory-snapshot-on-shutdown
  • 关闭时保存内存快照到磁盘
  • 重启时快速恢复
  • 减少 WAL 重放时间

5. concurrent-rule-eval - 并发规则评估

1--enable-feature=concurrent-rule-eval
  • 同组内无依赖的规则并发执行
  • 配合 --rules.max-concurrent-rule-evals=4 使用
  • 提高规则评估速度

6. auto-reload-config - 自动重载配置

1--enable-feature=auto-reload-config
2--config.auto-reload-interval=30s
  • 自动检测配置变更
  • 定期检查文件校验和
  • 原子更新文件以避免问题

7. otlp-deltatocumulative - OTLP Delta 转换

1--enable-feature=otlp-deltatocumulative
  • 将 OTLP delta 指标转换为累积
  • 内存状态存储增量
  • 重启后从零开始(导致计数器重置)

8. promql-experimental-functions - 实验性 PromQL 函数

1--enable-feature=promql-experimental-functions

启用实验性函数(如 double_exponential_smoothing)。


9. type-and-unit-labels - 类型和单位标签

1--enable-feature=type-and-unit-labels
  • 注入 __type____unit__ 保留标签
  • 从元数据获取类型和单位信息
  • 支持基于类型/单位的查询

10. old-ui - 旧版 UI

1--enable-feature=old-ui

使用 Prometheus 2.x 的旧版 Web UI。


其他特性:

  • promql-per-step-stats - 每步统计
  • created-timestamp-zero-ingestion - Created 时间戳注入
  • delayed-compaction - 延迟压缩
  • promql-delayed-name-removal - 延迟 __name__ 移除
  • promql-duration-expr - 持续时间算术表达式
  • otlp-native-delta-ingestion - OTLP 原生 Delta
  • metadata-wal-records - 元数据 WAL 记录
  • use-uncached-io - 非缓存 I/O (Linux)

11. Security (安全)

安全模型概述

⚠️ 重要: Prometheus HTTP 端点不应暴露到公网,包括:

  • /metrics 端点
  • API 端点 (/api/v1/*)
  • /pprof 调试端点

安全假设

Prometheus:

  • ✅ 不可信用户可访问 HTTP 端点和日志
  • ✅ 不可信用户可查询所有时间序列数据
  • ❌ 只有可信用户可修改配置、规则文件、命令行参数

目标:

  • 不可信用户可运行抓取目标
  • 默认情况下目标不能伪装成其他目标
  • honor_labels: true 会移除此保护

管理 API

v2.0+ 安全控制:

1# 启用管理 API (删除时间序列等)
2--web.enable-admin-api
3
4# 启用生命周期 API (重载/关闭)
5--web.enable-lifecycle

端点:

  • /api/*/admin/* - 管理功能
  • /-/reload - 重载配置
  • /-/quit - 关闭服务

组件安全

Alertmanager:

  • 任何访问者可创建、解析告警
  • 任何访问者可创建、修改、删除静默

Pushgateway:

  • 任何访问者可创建、修改、删除指标
  • 通常与 honor_labels 一起使用
  • 可伪造任意时间序列

Exporters:

  • SNMP/Blackbox Exporter 从 URL 参数获取目标
  • 可能泄露认证信息(如 HTTP Basic Auth、SNMP community)

认证、授权和加密

TLS 支持:

1# prometheus.yml
2tls_config:
3  cert_file: server.crt
4  key_file: server.key
5  client_ca_file: ca.crt
6  
7# 最低 TLS 1.2
8# 目标: Qualys SSL Labs A 级

HTTP Basic 认证:

1basic_auth:
2  username: admin
3  password_file: /path/to/password
4
5# 密码使用 bcrypt 哈希存储
6# 建议使用 TLS,否则明文传输

客户端认证:

1tls_config:
2  insecure_skip_verify: false  # 不跳过 SSL 验证

API 安全

CSRF 防护:

  • 无内置 CSRF 保护(为兼容 cURL 等工具)
  • 建议在反向代理层阻止可变端点

XSS 防护:

  • 非可变端点设置 CORS 头
  • 示例: Access-Control-Allow-Origin

PromQL 注入:

1# ❌ 危险 - 用户输入未转义
2up{job="<user_input>"}
3
4# 如果 user_input = "} or some_metric{zzz="
5# 结果: up{job=""} or some_metric{zzz=""}

Grafana 注意事项:

  • Dashboard 权限 ≠ 数据源权限
  • 代理模式下用户可运行任意查询

秘密管理

秘密字段:

  • 配置中标记为秘密的字段不会暴露在日志/API
  • 不要在其他字段存放秘密
  • 保护磁盘文件权限

外部秘密:

  • 依赖的秘密(如 AWS_SECRET_KEY)可能被泄露
  • 用户需自行保护

DoS 防护

缓解措施:

  • 部分负载和查询限制
  • 过多/昂贵查询会导致组件崩溃

用户责任:

  • 提供足够资源 (CPU、RAM、磁盘、IOPS、文件描述符、带宽)
  • 监控所有组件
  • 自动重启失败组件

漏洞报告

公开漏洞:

  • 在 GitHub 提交 Bug 报告

未公开漏洞:

  • 私下报告给 MAINTAINERS
  • 抄送: prometheus-team@googlegroups.com
  • 7 天内修复核心组件(Prometheus、Alertmanager、Node Exporter 等)

扫描工具用户注意:

  • 提交前分析报告,避免误报
  • 使用开源工具(如 govulncheck)验证

12. Overview (概览)

什么是 Prometheus?

Prometheus 是开源的系统监控和告警工具包,最初在 SoundCloud 构建。2016 年加入 CNCF,成为继 Kubernetes 之后的第二个托管项目。

核心特点:

  • 采集并存储指标为时间序列数据
  • 指标 + 时间戳 + 可选键值对标签

主要特性

  1. 多维数据模型 - 指标名称和键值对标识时间序列
  2. PromQL - 灵活的查询语言
  3. 无依赖分布式存储 - 单服务器节点自治
  4. Pull 模型 - 通过 HTTP 拉取时间序列
  5. Push 支持 - 通过中间网关支持推送
  6. 服务发现 - 静态配置或服务发现
  7. 图形和仪表板 - 多种可视化支持

什么是指标?

指标: 数值测量

时间序列: 随时间变化的记录

示例:

  • Web 服务器: 请求时间
  • 数据库: 活动连接数、活动查询数

作用: 理解应用行为,诊断问题,指导扩容


组件生态

graph TB A[Prometheus Server
抓取并存储数据] --> B[时间序列数据库] C[客户端库] --> D[应用程序] E[Pushgateway
短期任务] --> A F[Exporters
HAProxy/StatsD等] --> A A --> G[Alertmanager
告警处理] A --> H[Grafana
可视化] I[服务发现] --> A

核心组件:

  1. Prometheus Server - 抓取和存储时间序列
  2. 客户端库 - 应用程序埋点
  3. Pushgateway - 短期任务支持
  4. Exporters - 第三方服务(HAProxy、StatsD 等)
  5. Alertmanager - 告警处理

语言: 大多数组件用 Go 编写,易于部署


架构

Prometheus 从已埋点的作业抓取指标:

  • 直接抓取
  • 通过 Pushgateway(短期任务)

存储样本并运行规则:

  • 聚合和记录新时间序列
  • 生成告警

Grafana 或其他 API 消费者可视化数据。


适用场景

适合:

  • ✅ 记录纯数值时间序列
  • ✅ 机器中心监控
  • ✅ 高度动态的面向服务架构
  • ✅ 微服务环境
  • ✅ 多维数据收集和查询

可靠性设计:

  • 独立服务器,不依赖网络存储
  • 故障期间可查看统计信息
  • 不需要复杂基础设施

不适用场景

不适合:

  • ❌ 需要 100% 准确性(如按请求计费)
  • ❌ 采集的数据可能不够详细和完整
  • ❌ 建议使用其他系统收集计费数据
  • ✅ Prometheus 用于其余监控

13. Alertmanager (告警管理器)

核心功能

Alertmanager 处理客户端(如 Prometheus)发送的告警,负责:

  • 去重 (Deduplication)
  • 分组 (Grouping)
  • 路由 (Routing)
  • 静默 (Silencing)
  • 抑制 (Inhibition)

核心概念

1. Grouping (分组)

目的: 将相似告警归类为单个通知

场景:

数百个服务实例 → 网络分区 → 一半实例无法访问数据库

无分组: 发送数百条告警

有分组: 发送一条告警,列出所有受影响实例

配置:

1route:
2  group_by: ['cluster', 'alertname']
3  group_wait: 10s
4  group_interval: 10s
5  repeat_interval: 1h
6  receiver: 'team-ops'

2. Inhibition (抑制)

目的: 某些告警触发时,抑制其他相关告警

场景:

整个集群不可达 → 数百个实例告警

抑制规则: 集群不可达告警触发时,抑制该集群的所有其他告警

配置:

1inhibit_rules:
2  - source_match:
3      severity: 'critical'
4      alertname: 'ClusterDown'
5    target_match:
6      severity: 'warning'
7    equal: ['cluster']

3. Silences (静默)

目的: 在给定时间内静默告警

配置方式: Web UI 配置

示例:

  • 计划维护期间静默告警
  • 基于标签匹配器
  • 支持等式和正则表达式

Web UI 操作:

1# 访问 Alertmanager UI
2http://alertmanager:9093
3
4# 创建静默
5Silences → Create Silence → 设置匹配器和时间

高可用性

集群配置:

1# Alertmanager 1
2alertmanager \
3  --cluster.peer=alertmanager-2:9094 \
4  --cluster.peer=alertmanager-3:9094
5
6# Alertmanager 2
7alertmanager \
8  --cluster.peer=alertmanager-1:9094 \
9  --cluster.peer=alertmanager-3:9094

Prometheus 配置:

1# ⚠️ 不要负载均衡,而是提供完整列表
2alerting:
3  alertmanagers:
4    - static_configs:
5        - targets:
6          - alertmanager-1:9093
7          - alertmanager-2:9093
8          - alertmanager-3:9093

配置示例

完整路由树:

 1route:
 2  # 根路由
 3  receiver: 'default-receiver'
 4  group_by: ['cluster', 'alertname']
 5  group_wait: 10s
 6  group_interval: 10s
 7  repeat_interval: 12h
 8  
 9  # 子路由
10  routes:
11    - match:
12        severity: critical
13      receiver: 'pager'
14      continue: true
15  
16    - match:
17        severity: warning
18      receiver: 'slack'
19  
20    - match_re:
21        service: ^(foo|bar)$
22      receiver: 'team-ops'
23      group_by: ['service']
24
25receivers:
26  - name: 'default-receiver'
27    email_configs:
28      - to: 'team@example.com'
29  
30  - name: 'pager'
31    pagerduty_configs:
32      - service_key: '<key>'
33  
34  - name: 'slack'
35    slack_configs:
36      - api_url: '<webhook_url>'
37        channel: '#alerts'

通知接收器

支持的接收器:

  • Email
  • PagerDuty
  • Slack
  • Webhook
  • OpsGenie
  • VictorOps
  • WeChat
  • Pushover
  • 等等…

Webhook 示例:

1receivers:
2  - name: 'webhook'
3    webhook_configs:
4      - url: 'http://example.com/webhook'
5        send_resolved: true

最佳实践

1. 合理分组

  • 按集群、服务、严重程度分组
  • 避免过度分组导致通知风暴

2. 使用抑制

  • 上游问题抑制下游告警
  • 减少噪音

3. 静默管理

  • 维护期间使用静默
  • 设置合理的静默时间
  • 避免永久静默

4. 高可用部署

  • 至少 3 个 Alertmanager 实例
  • Prometheus 配置完整的 Alertmanager 列表
  • 不要负载均衡

5. 测试通知

  • 使用 amtool 测试配置
  • 验证路由规则
  • 测试所有接收器

14. Alerting Configuration (Alertmanager 配置)

Alertmanager 通过命令行标志和配置文件进行配置。

14.1 配置文件基础

启动命令:

1alertmanager --config.file=alertmanager.yml

重载配置:

1# 发送 SIGHUP 信号
2kill -HUP <pid>
3
4# 或使用 HTTP 端点
5curl -X POST http://localhost:9093/-/reload

14.2 全局配置

 1global:
 2  # SMTP 配置
 3  smtp_from: 'alertmanager@example.com'
 4  smtp_smarthost: 'smtp.gmail.com:587'
 5  smtp_auth_username: 'alerts@example.com'
 6  smtp_auth_password: 'password'
 7  smtp_require_tls: true
 8  
 9  # Slack 配置
10  slack_api_url: 'https://hooks.slack.com/services/xxx'
11  
12  # PagerDuty 配置
13  pagerduty_url: 'https://events.pagerduty.com/v2/enqueue'
14  
15  # OpsGenie 配置
16  opsgenie_api_key: 'xxx'
17  opsgenie_api_url: 'https://api.opsgenie.com/'
18  
19  # 解析超时
20  resolve_timeout: 5m
21  
22  # HTTP 客户端配置
23  http_config:
24    tls_config:
25      insecure_skip_verify: false

14.3 路由配置 (Route)

路由定义告警如何分组和发送到接收器。

核心参数:

参数说明默认值
receiver接收器名称必需
group_by分组标签-
group_wait分组等待时间30s
group_interval组内新告警间隔5m
repeat_interval重复发送间隔4h
continue继续匹配兄弟节点false
matchers标签匹配器-

完整示例:

 1route:
 2  # 根路由 - 匹配所有告警
 3  receiver: 'default-receiver'
 4  group_by: ['cluster', 'alertname']
 5  group_wait: 30s
 6  group_interval: 5m
 7  repeat_interval: 4h
 8  
 9  # 子路由
10  routes:
11    # 数据库告警
12    - receiver: 'database-pager'
13      group_wait: 10s
14      matchers:
15        - service=~"mysql|cassandra"
16  
17    # 前端团队告警
18    - receiver: 'frontend-pager'
19      group_by: ['product', 'environment']
20      matchers:
21        - team="frontend"
22  
23    # 非工作时间静默
24    - receiver: 'dev-pager'
25      matchers:
26        - service="inhouse-service"
27      mute_time_intervals:
28        - offhours
29        - holidays
30      continue: true
31  
32    # 工作时间激活
33    - receiver: 'on-call-pager'
34      matchers:
35        - service="inhouse-service"
36      active_time_intervals:
37        - offhours
38        - holidays

分组策略:

1# 按所有标签分组(禁用聚合)
2group_by: ['...']
3
4# 按特定标签分组
5group_by: ['cluster', 'alertname', 'severity']

14.4 时间间隔 (Time Intervals)

定义静默或激活路由的时间范围。

基本示例:

 1time_intervals:
 2  # 非工作时间
 3  - name: offhours
 4    time_intervals:
 5      - times:
 6          - start_time: '17:00'
 7            end_time: '09:00'
 8        weekdays: ['monday:friday']
 9      - weekdays: ['saturday', 'sunday']
10    location: 'Asia/Shanghai'
11  
12  # 节假日
13  - name: holidays
14    time_intervals:
15      - days_of_month: ['1']
16        months: ['january', 'may', 'october']
17      - days_of_month: ['25']
18        months: ['december']

时间字段:

  • times - 时间范围 (HH:MM - HH:MM)
  • weekdays - 星期几 (sunday - saturday)
  • days_of_month - 月份中的天 (1-31, 支持负数)
  • months - 月份 (january-december 或 1-12)
  • years - 年份 (如 2024:2025)
  • location - 时区 (如 Asia/Shanghai, UTC, Local)

14.5 抑制规则 (Inhibition)

当某个告警触发时,抑制其他相关告警。

基本示例:

 1inhibit_rules演
 2  # 集群宕机时抑制所有其他告警
 3  - source_matchers:
 4      - alertname="ClusterDown"
 5      - severity="critical"
 6    target_matchers:
 7      - severity="warning"
 8    equal: ['cluster']
 9  
10  # 节点宕机时抑制该节点的所有告警
11  - source_matchers:
12      - alertname="NodeDown"
13    target_matchers:
14      - alertname!="NodeDown"
15    equal: ['instance']

字段说明:

  • source_matchers - 源告警匹配器(抑制器)
  • target_matchers - 目标告警匹配器(被抑制)
  • equal - 必须相等的标签列表

14.6 标签匹配器 (Matchers)

UTF-8 匹配器 (推荐):

1matchers:
2  - alertname = "HighCPU"           # 等于
3  - severity != "info"              # 不等于
4  - service =~ "api|web"            # 正则匹配
5  - instance !~ "test.*"            # 正则不匹配
6  - foo = "bar,baz"                 # 可包含特殊字符
7  - team = "前端"                   # 支持 UTF-8

组合匹配器:

 1# YAML 列表形式
 2matchers:
 3  - alertname = "Watchdog"
 4  - severity =~ "warning|critical"
 5
 6# 短格式
 7matchers: [ 'alertname = "Watchdog"', 'severity =~ "warning|critical"' ]
 8
 9# PromQL 风格
10matchers: [ '{alertname="Watchdog", severity=~"warning|critical"}' ]

UTF-8 严格模式:

1# 启用 UTF-8 严格模式
2alertmanager --enable-feature="utf8-strict-mode"
3
4# 验证配置
5amtool check-config alertmanager.yml

14.7 接收器 (Receivers)

Email 接收器

 1receivers:
 2  - name: 'email-team'
 3    email_configs:
 4      - to: 'team@example.com'
 5        from: 'alertmanager@example.com'
 6        smarthost: 'smtp.gmail.com:587'
 7        auth_username: 'alerts@example.com'
 8        auth_password: 'password'
 9        require_tls: true
10        headers:
11          Subject: '{{ .GroupLabels.alertname }}: {{ .Status }}'
12        html: '{{ template "email.default.html" . }}'

Slack 接收器

1receivers:
2  - name: 'slack-alerts'
3    slack_configs:
4      - api_url: 'https://hooks.slack.com/services/xxx'
5        channel: '#alerts'
6        title: '{{ .GroupLabels.alertname }}'
7        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'
8        color: '{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}'
9        send_resolved: true

PagerDuty 接收器

1receivers:
2  - name: 'pagerduty-critical'
3    pagerduty_configs:
4      - routing_key: 'xxx'
5        severity: 'critical'
6        description: '{{ .GroupLabels.alertname }}'
7        details:
8          firing: '{{ template "pagerduty.default.instances" .Alerts.Firing }}'
9          num_firing: '{{ .Alerts.Firing | len }}'

Webhook 接收器

 1receivers:
 2  - name: 'webhook-receiver'
 3    webhook_configs:
 4      - url: 'http://example.com/webhook'
 5        send_resolved: true
 6        max_alerts: 0  # 0 = 无限制
 7        http_config:
 8          basic_auth:
 9            username: 'user'
10            password: 'pass'

Webhook JSON 格式:

 1{
 2  "version": "4",
 3  "groupKey": "<string>",
 4  "status": "firing|resolved",
 5  "receiver": "<string>",
 6  "groupLabels": {},
 7  "commonLabels": {},
 8  "commonAnnotations": {},
 9  "externalURL": "<string>",
10  "alerts": [
11    {
12      "status": "firing|resolved",
13      "labels": {},
14      "annotations": {},
15      "startsAt": "<rfc3339>",
16      "endsAt": "<rfc3339>",
17      "generatorURL": "<string>",
18      "fingerprint": "<string>"
19    }
20  ]
21}

其他接收器

OpsGenie:

1opsgenie_configs:
2  - api_key: 'xxx'
3    message: '{{ .GroupLabels.alertname }}'
4    priority: 'P1'
5    responders:
6      - type: 'team'
7        name: 'ops-team'

Telegram:

1telegram_configs:
2  - bot_token: 'xxx'
3    chat_id: 123456789
4    message: '🔥 {{ .GroupLabels.alertname }}'
5    parse_mode: 'HTML'

WeChat (微信):

1wechat_configs:
2  - api_secret: 'xxx'
3    corp_id: 'xxx'
4    agent_id: '1000002'
5    to_user: 'user123'
6    message_type: 'markdown'

Pushover:

1pushover_configs:
2  - user_key: 'xxx'
3    token: 'xxx'
4    priority: '2'  # 紧急
5    retry: 30s
6    expire: 1h

14.8 HTTP 配置

TLS 配置:

1http_config:
2  tls_config:
3    ca_file: /path/to/ca.crt
4    cert_file: /path/to/client.crt
5    key_file: /path/to/client.key
6    server_name: 'example.com'
7    insecure_skip_verify: false

Basic 认证:

1http_config:
2  basic_auth:
3    username: 'admin'
4    password: 'secret'

Bearer Token:

1http_config:
2  authorization:
3    type: 'Bearer'
4    credentials: 'token123'

OAuth2:

1http_config:
2  oauth2:
3    client_id: 'xxx'
4    client_secret: 'xxx'
5    token_url: 'https://auth.example.com/token'
6    scopes: ['read', 'write']

代理配置:

1http_config:
2  proxy_url: 'http://proxy.example.com:8080'
3  no_proxy: '127.0.0.1,localhost'

14.9 完整配置示例

 1global:
 2  resolve_timeout: 5m
 3  smtp_smarthost: 'smtp.gmail.com:587'
 4  smtp_from: 'alertmanager@example.com'
 5  smtp_auth_username: 'alerts@example.com'
 6  smtp_auth_password: 'password'
 7  slack_api_url: 'https://hooks.slack.com/services/xxx'
 8
 9templates:
10  - '/etc/alertmanager/templates/*.tmpl'
11
12route:
13  receiver: 'default'
14  group_by: ['cluster', 'alertname']
15  group_wait: 10s
16  group_interval: 10s
17  repeat_interval: 12h
18  
19  routes:
20    # Critical 告警立即发送
21    - receiver: 'pagerduty'
22      matchers:
23        - severity="critical"
24      group_wait: 0s
25      repeat_interval: 5m
26      continue: true
27  
28    # Warning 告警发送到 Slack
29    - receiver: 'slack'
30      matchers:
31        - severity="warning"
32  
33    # 数据库告警
34    - receiver: 'database-team'
35      matchers:
36        - service=~"mysql|postgres"
37      mute_time_intervals:
38        - weekends
39
40receivers:
41  - name: 'default'
42    email_configs:
43      - to: 'team@example.com'
44  
45  - name: 'pagerduty'
46    pagerduty_configs:
47      - routing_key: 'xxx'
48  
49  - name: 'slack'
50    slack_configs:
51      - channel: '#alerts'
52        title: '{{ .GroupLabels.alertname }}'
53  
54  - name: 'database-team'
55    email_configs:
56      - to: 'dba@example.com'
57
58inhibit_rules:
59  - source_matchers:
60      - severity="critical"
61    target_matchers:
62      - severity="warning"
63    equal: ['cluster', 'alertname']
64
65time_intervals:
66  - name: weekends
67    time_intervals:
68      - weekdays: ['saturday', 'sunday']

14.10 模板变量

常用变量:

 1# 告警状态
 2{{ .Status }}  # firing 或 resolved
 3
 4# 标签
 5{{ .GroupLabels.alertname }}  # 分组标签
 6{{ .CommonLabels.instance }}  # 公共标签
 7
 8# 注解
 9{{ .CommonAnnotations.summary }}
10{{ .CommonAnnotations.description }}
11
12# 告警列表
13{{ range .Alerts }}
14  Instance: {{ .Labels.instance }}
15  Summary: {{ .Annotations.summary }}
16{{ end }}
17
18# 告警数量
19{{ .Alerts.Firing | len }}  # Firing 数量
20{{ .Alerts.Resolved | len }}  # Resolved 数量

14.11 验证和测试

验证配置:

1# 检查配置文件
2amtool check-config alertmanager.yml
3
4# UTF-8 严格模式检查
5amtool check-config alertmanager.yml --enable-feature="utf8-strict-mode"

测试告警:

 1# 发送测试告警
 2amtool alert add alertname="TestAlert" severity="warning" instance="localhost"
 3
 4# 查看告警
 5amtool alert query
 6
 7# 创建静默
 8amtool silence add alertname="TestAlert" --duration=1h --comment="Testing"
 9
10# 查看静默
11amtool silence query

14.12 最佳实践

1. 合理的分组策略

1# ✅ 好 - 按集群和告警名称分组
2group_by: ['cluster', 'alertname']
3
4# ❌ 差 - 禁用聚合(产生大量通知)
5group_by: ['...']

2. 合理的时间间隔

1group_wait: 10s       # 等待更多相同组的告警
2group_interval: 5m    # 组内新告警等待时间
3repeat_interval: 4h   # 重复发送间隔(应为 group_interval 的倍数)

3. 使用抑制规则

1# 上游故障抑制下游告警
2inhibit_rules:
3  - source_matchers:
4      - alertname="APIDown"
5    target_matchers:
6      - alertname=~".*Slow|.*Error"
7    equal: ['service']

4. 多级路由

 1routes:
 2  # Critical → PagerDuty + Slack
 3  - receiver: 'pagerduty'
 4    matchers:
 5      - severity="critical"
 6    continue: true
 7  
 8  - receiver: 'slack'
 9    matchers:
10      - severity="critical"

5. 使用模板文件

1templates:
2  - '/etc/alertmanager/templates/*.tmpl'

6. 定期测试

  • 定期发送测试告警
  • 验证所有接收器工作正常
  • 测试抑制和静默规则

配置快速参考

路由参数:

  • receiver - 接收器名称
  • group_by - 分组标签
  • group_wait - 初始等待 (默认: 30s)
  • group_interval - 组内间隔 (默认: 5m)
  • repeat_interval - 重复间隔 (默认: 4h)
  • matchers - 匹配器列表
  • continue - 继续匹配 (默认: false)
  • mute_time_intervals - 静默时间段
  • active_time_intervals - 激活时间段

抑制参数:

  • source_matchers - 源告警匹配器
  • target_matchers - 目标告警匹配器
  • equal - 相等标签列表

接收器类型:

  • email_configs - 邮件
  • slack_configs - Slack
  • pagerduty_configs - PagerDuty
  • webhook_configs - Webhook
  • opsgenie_configs - OpsGenie
  • telegram_configs - Telegram
  • wechat_configs - 微信
  • pushover_configs - Pushover
  • discord_configs - Discord
  • msteams_configs - Microsoft Teams
  • victorops_configs - VictorOps
  • sns_configs - AWS SNS
  • webex_configs - Webex

15. Notification Examples (通知示例)

以下是使用 Go 模板系统的 Alertmanager 通知配置示例。

15.1 自定义 Slack 通知

添加 Wiki 链接:

 1global:
 2  slack_api_url: 'https://hooks.slack.com/services/xxx'
 3
 4route:
 5  receiver: 'slack-notifications'
 6  group_by: [alertname, datacenter, app]
 7
 8receivers:
 9  - name: 'slack-notifications'
10    slack_configs:
11      - channel: '#alerts'
12        text: 'https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}'

效果: 根据告警的 appalertname 标签,自动生成对应的 Wiki 文档链接。


15.2 访问注解 (Annotations)

Prometheus 告警规则:

 1groups:
 2  - name: Instances
 3    rules:
 4      - alert: InstanceDown
 5        expr: up == 0
 6        for: 5m
 7        labels:
 8          severity: page
 9        annotations:
10          summary: 'Instance {{ $labels.instance }} down'
11          description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes.'

Alertmanager 接收器:

1receivers:
2  - name: 'team-x'
3    slack_configs:
4      - channel: '#alerts'
5        text: |
6          <!channel>
7          summary: {{ .CommonAnnotations.summary }}
8          description: {{ .CommonAnnotations.description }}

关键点:

  • {{ .CommonAnnotations.summary }} - 访问公共注解中的摘要
  • {{ .CommonAnnotations.description }} - 访问公共注解中的描述
  • <!channel> - Slack 的 @channel 提及

15.3 遍历所有告警

 1receivers:
 2  - name: 'default-receiver'
 3    slack_configs:
 4      - channel: '#alerts'
 5        title: |
 6          {{ range .Alerts }}{{ .Annotations.summary }}
 7          {{ end }}
 8        text: |
 9          {{ range .Alerts }}{{ .Annotations.description }}
10          {{ end }}

效果: 遍历所有接收到的告警,每个告警的摘要和描述各占一行。


15.4 定义可复用模板

步骤 1: 创建模板文件 /etc/alertmanager/templates/myorg.tmpl

 1{{ define "slack.myorg.text" }}
 2https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}
 3{{ end }}
 4
 5{{ define "slack.myorg.title" }}
 6🚨 [{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}
 7{{ end }}
 8
 9{{ define "email.subject" }}
10[{{ .Status }}] {{ .GroupLabels.alertname }} ({{ .Alerts.Firing | len }} firing)
11{{ end }}

步骤 2: 在配置中引用模板

 1global:
 2  slack_api_url: 'https://hooks.slack.com/services/xxx'
 3
 4templates:
 5  - '/etc/alertmanager/templates/myorg.tmpl'
 6
 7route:
 8  receiver: 'slack-notifications'
 9  group_by: [alertname, datacenter, app]
10
11receivers:
12  - name: 'slack-notifications'
13    slack_configs:
14      - channel: '#alerts'
15        title: '{{ template "slack.myorg.title" . }}'
16        text: '{{ template "slack.myorg.text" . }}'

15.5 更多实用模板示例

1. 显示告警数量和列表

 1title: |
 2  {{ .GroupLabels.alertname }} ({{ .Alerts.Firing | len }} firing, {{ .Alerts.Resolved | len }} resolved)
 3
 4text: |
 5  🔥 *Firing:*
 6  {{ range .Alerts.Firing }}
 7  • {{ .Labels.instance }}: {{ .Annotations.summary }}
 8  {{ end }}
 9  
10  ✅ *Resolved:*
11  {{ range .Alerts.Resolved }}
12  • {{ .Labels.instance }}: {{ .Annotations.summary }}
13  {{ end }}

2. 根据严重程度设置颜色

1slack_configs:
2  - channel: '#alerts'
3    color: |
4      {{ if eq .Status "firing" }}
5        {{ if eq .CommonLabels.severity "critical" }}danger{{ else if eq .CommonLabels.severity "warning" }}warning{{ else }}#439FE0{{ end }}
6      {{ else }}good{{ end }}

3. 格式化时间

1text: |
2  Started: {{ .Alerts.Firing | len }} alerts at {{ .CommonAnnotations.startsAt }}
3  {{ range .Alerts }}
4  - {{ .Labels.instance }} ({{ .StartsAt.Format "2006-01-02 15:04:05" }})
5  {{ end }}

4. 添加 Prometheus 查询链接

1text: |
2  {{ range .Alerts }}
3  Instance: {{ .Labels.instance }}
4  Query: {{ .GeneratorURL }}
5  {{ end }}

5. Email 通知模板

 1receivers:
 2  - name: 'email-team'
 3    email_configs:
 4      - to: 'team@example.com'
 5        headers:
 6          Subject: '{{ template "email.subject" . }}'
 7        html: |
 8          <!DOCTYPE html>
 9          <html>
10          <head>
11            <style>
12              .firing { background-color: #ff0000; color: white; }
13              .resolved { background-color: #00ff00; color: black; }
14            </style>
15          </head>
16          <body>
17            <h2>{{ .GroupLabels.alertname }}</h2>
18            <p><strong>Status:</strong> {{ .Status }}</p>
19            <p><strong>Severity:</strong> {{ .CommonLabels.severity }}</p>
20          
21            <h3>Firing Alerts ({{ .Alerts.Firing | len }})</h3>
22            <ul>
23            {{ range .Alerts.Firing }}
24              <li class="firing">
25                <strong>{{ .Labels.instance }}</strong><br/>
26                {{ .Annotations.description }}
27              </li>
28            {{ end }}
29            </ul>
30          
31            <h3>Resolved Alerts ({{ .Alerts.Resolved | len }})</h3>
32            <ul>
33            {{ range .Alerts.Resolved }}
34              <li class="resolved">
35                <strong>{{ .Labels.instance }}</strong><br/>
36                Resolved at {{ .EndsAt.Format "2006-01-02 15:04:05" }}
37              </li>
38            {{ end }}
39            </ul>
40          </body>
41          </html>

6. PagerDuty 自定义详情

1pagerduty_configs:
2  - routing_key: 'xxx'
3    description: '{{ .GroupLabels.alertname }}'
4    details:
5      firing: '{{ template "pagerduty.default.instances" .Alerts.Firing }}'
6      num_firing: '{{ .Alerts.Firing | len }}'
7      num_resolved: '{{ .Alerts.Resolved | len }}'
8      cluster: '{{ .CommonLabels.cluster }}'
9      runbook: 'https://runbooks.example.com/{{ .GroupLabels.alertname }}'

7. Webhook JSON 自定义

1webhook_configs:
2  - url: 'http://example.com/webhook'
3    send_resolved: true

接收到的 JSON 可通过代码处理:

 1@app.route('/webhook', methods=['POST'])
 2def webhook():
 3    data = request.json
 4  
 5    for alert in data['alerts']:
 6        print(f"Alert: {alert['labels']['alertname']}")
 7        print(f"Instance: {alert['labels']['instance']}")
 8        print(f"Summary: {alert['annotations']['summary']}")
 9        print(f"Status: {alert['status']}")
10  
11    return '', 200

15.6 模板函数

常用函数:

 1# 字符串操作
 2{{ .CommonLabels.alertname | toUpper }}    # 大写
 3{{ .CommonLabels.alertname | toLower }}    # 小写
 4{{ .CommonLabels.alertname | title }}      # 首字母大写
 5
 6# 列表操作
 7{{ .Alerts.Firing | len }}                 # 长度
 8{{ index .Alerts 0 }}                      # 索引
 9
10# 条件判断
11{{ if eq .Status "firing" }}Firing{{ else }}Resolved{{ end }}
12{{ if gt (.Alerts.Firing | len) 5 }}Too many alerts!{{ end }}
13
14# 时间格式化
15{{ .StartsAt.Format "2006-01-02 15:04:05" }}
16{{ .EndsAt.Format "15:04" }}

15.7 完整模板文件示例

/etc/alertmanager/templates/custom.tmpl

 1{{ define "slack.title" }}
 2[{{ .Status | toUpper }}] {{ .GroupLabels.alertname }}
 3{{ end }}
 4
 5{{ define "slack.text" }}
 6*Cluster:* {{ .CommonLabels.cluster }}
 7*Severity:* {{ .CommonLabels.severity }}
 8
 9{{ if gt (len .Alerts.Firing) 0 }}
10🔥 *Firing ({{ .Alerts.Firing | len }}):*
11{{ range .Alerts.Firing }}
12   *{{ .Labels.instance }}*
13    {{ .Annotations.description }}
14    <{{ .GeneratorURL }}|View Query>
15{{ end }}
16{{ end }}
17
18{{ if gt (len .Alerts.Resolved) 0 }}
19 *Resolved ({{ .Alerts.Resolved | len }}):*
20{{ range .Alerts.Resolved }}
21   {{ .Labels.instance }}
22{{ end }}
23{{ end }}
24{{ end }}
25
26{{ define "email.subject" }}
27[{{ .Status }}] {{ .GroupLabels.alertname }} - {{ .CommonLabels.cluster }}
28{{ end }}
29
30{{ define "email.html" }}
31<h2>{{ .GroupLabels.alertname }}</h2>
32<table>
33  <tr><td>Status:</td><td>{{ .Status }}</td></tr>
34  <tr><td>Severity:</td><td>{{ .CommonLabels.severity }}</td></tr>
35  <tr><td>Cluster:</td><td>{{ .CommonLabels.cluster }}</td></tr>
36  <tr><td>Firing:</td><td>{{ .Alerts.Firing | len }}</td></tr>
37  <tr><td>Resolved:</td><td>{{ .Alerts.Resolved | len }}</td></tr>
38</table>
39
40<h3>Details</h3>
41{{ range .Alerts }}
42<div style="border-left: 3px solid {{ if eq .Status "firing" }}red{{ else }}green{{ end }}; padding-left: 10px; margin: 10px 0;">
43  <strong>{{ .Labels.instance }}</strong><br/>
44  {{ .Annotations.description }}<br/>
45  <small>Started: {{ .StartsAt.Format "2006-01-02 15:04:05" }}</small>
46</div>
47{{ end }}
48{{ end }}

16. Alertmanager Management API

Alertmanager 提供管理 API 用于自动化和集成。

16.1 健康检查

端点:

1GET  /-/healthy
2HEAD /-/healthy

用途: 检查 Alertmanager 是否运行

返回: 始终返回 200

示例:

1# 检查健康状态
2curl http://localhost:9093/-/healthy
3
4# 仅检查状态码
5curl -I http://localhost:9093/-/healthy

Kubernetes 存活探针:

1livenessProbe:
2  httpGet:
3    path: /-/healthy
4    port: 9093
5  initialDelaySeconds: 30
6  periodSeconds: 10

16.2 就绪检查

端点:

1GET  /-/ready
2HEAD /-/ready

用途: 检查 Alertmanager 是否准备好处理流量

返回: 准备好时返回 200,否则返回 503

示例:

1curl http://localhost:9093/-/ready

Kubernetes 就绪探针:

1readinessProbe:
2  httpGet:
3    path: /-/ready
4    port: 9093
5  initialDelaySeconds: 10
6  periodSeconds: 5

16.3 重载配置

端点:

1POST /-/reload

用途: 触发配置文件重新加载

示例:

1curl -X POST http://localhost:9093/-/reload

替代方法 - SIGHUP 信号:

1# 查找进程 ID
2ps aux | grep alertmanager
3
4# 发送 SIGHUP 信号
5kill -HUP <PID>
6
7# 或使用 systemd
8systemctl reload alertmanager

16.4 自动化脚本示例

配置更新脚本:

 1#!/bin/bash
 2# update-alertmanager-config.sh
 3
 4CONFIG_FILE="/etc/alertmanager/alertmanager.yml"
 5BACKUP_DIR="/etc/alertmanager/backups"
 6ALERTMANAGER_URL="http://localhost:9093"
 7
 8# 创建备份
 9timestamp=$(date +%Y%m%d_%H%M%S)
10cp "$CONFIG_FILE" "$BACKUP_DIR/alertmanager_${timestamp}.yml"
11
12# 验证新配置
13if amtool check-config "$CONFIG_FILE"; then
14    echo "✅ Configuration is valid"
15  
16    # 重载配置
17    if curl -X POST "${ALERTMANAGER_URL}/-/reload"; then
18        echo "✅ Configuration reloaded successfully"
19      
20        # 验证就绪状态
21        sleep 2
22        if curl -f "${ALERTMANAGER_URL}/-/ready"; then
23            echo "✅ Alertmanager is ready"
24        else
25            echo "⚠️  Alertmanager not ready, check logs"
26            exit 1
27        fi
28    else
29        echo "❌ Failed to reload configuration"
30        exit 1
31    fi
32else
33    echo "❌ Configuration is invalid"
34    exit 1
35fi

健康监控脚本:

 1#!/usr/bin/env python3
 2import requests
 3import time
 4import sys
 5
 6ALERTMANAGER_URL = "http://localhost:9093"
 7
 8def check_health():
 9    try:
10        response = requests.get(f"{ALERTMANAGER_URL}/-/healthy", timeout=5)
11        return response.status_code == 200
12    except:
13        return False
14
15def check_ready():
16    try:
17        response = requests.get(f"{ALERTMANAGER_URL}/-/ready", timeout=5)
18        return response.status_code == 200
19    except:
20        return False
21
22def main():
23    while True:
24        healthy = check_health()
25        ready = check_ready()
26      
27        status = "✅" if healthy and ready else "❌"
28        print(f"{status} Health: {healthy} | Ready: {ready}")
29      
30        if not healthy:
31            print("🔴 Alertmanager is not healthy!")
32            sys.exit(1)
33      
34        time.sleep(30)
35
36if __name__ == "__main__":
37    main()

CI/CD 集成示例:

 1# GitLab CI
 2deploy-alertmanager:
 3  stage: deploy
 4  script:
 5    # 验证配置
 6    - amtool check-config alertmanager.yml
 7  
 8    # 部署配置
 9    - kubectl create configmap alertmanager-config \
10        --from-file=alertmanager.yml \
11        --dry-run=client -o yaml | kubectl apply -f -
12  
13    # 触发重载
14    - kubectl exec -n monitoring alertmanager-0 -- \
15        wget --post-data="" -O- http://localhost:9093/-/reload
16  
17    # 验证就绪
18    - sleep 5
19    - kubectl exec -n monitoring alertmanager-0 -- \
20        wget -O- http://localhost:9093/-/ready

Docker Compose 健康检查:

 1version: '3.8'
 2services:
 3  alertmanager:
 4    image: prom/alertmanager:latest
 5    container_name: alertmanager
 6    ports:
 7      - "9093:9093"
 8    healthcheck:
 9      test: ["CMD", "wget", "--spider", "-q", "http://localhost:9093/-/healthy"]
10      interval: 30s
11      timeout: 10s
12      retries: 3
13      start_period: 40s

16.5 API 使用最佳实践

1. 在配置更新前验证

1# ✅ 好 - 先验证再重载
2amtool check-config alertmanager.yml && \
3  curl -X POST http://localhost:9093/-/reload
4
5# ❌ 差 - 直接重载可能导致错误
6curl -X POST http://localhost:9093/-/reload

2. 使用就绪检查而非健康检查

1# ✅ 好 - 使用就绪检查确保可以处理流量
2curl http://localhost:9093/-/ready
3
4# ⚠️  健康检查只表明进程存活,不代表可以处理请求
5curl http://localhost:9093/-/healthy

3. 重载后验证

1# 重载配置
2curl -X POST http://localhost:9093/-/reload
3
4# 等待并验证
5sleep 2
6curl http://localhost:9093/-/ready || echo "Reload may have failed"

4. 自动重启失败的实例

1# systemd service 配置
2[Service]
3Restart=always
4RestartSec=5s

5. 日志监控

1# 监控重载操作
2journalctl -u alertmanager -f | grep -i "reload"
3
4# 或 Docker logs
5docker logs -f alertmanager | grep -i "reload"

总结

Notification Examples (通知示例):

  • 自定义 Slack 通知文本和格式
  • 访问和使用告警注解
  • 遍历所有告警
  • 定义可复用模板文件
  • 丰富的模板函数和示例

Management API (管理API):

  • /-/healthy - 健康检查
  • /-/ready - 就绪检查
  • /-/reload - 重载配置
  • 自动化脚本和 CI/CD 集成
  • 最佳实践

这两个章节提供了实用的模板和自动化方案,帮助运维人员高效管理 Alertmanager!


17. Practices (最佳实践)

17.1 Naming (命名规范)

指标和标签命名约定不是强制的,但可作为风格指南和最佳实践集合。

指标名称规范

必须 (MUST):

  1. 符合数据模型的有效字符
  2. 具有应用前缀(命名空间)
1# ✅ 好
2prometheus_notifications_total    # Prometheus 服务器特定
3process_cpu_seconds_total         # 客户端库导出
4http_request_duration_seconds     # 所有 HTTP 请求
5
6# ❌ 差
7notifications_total               # 缺少前缀
  1. 使用单一单位(不混用秒和毫秒,秒和字节)
  2. 使用基础单位(秒、字节、米,而非毫秒、兆字节、千米)
  3. 具有复数形式的单位后缀
 1# ✅ 好
 2http_request_duration_seconds
 3node_memory_usage_bytes
 4http_requests_total                # 无单位累积计数
 5process_cpu_seconds_total          # 带单位累积计数
 6foobar_build_info                  # 元数据伪指标
 7data_pipeline_last_processed_timestamp_seconds
 8
 9# ❌ 差
10http_request_duration_ms          # 应使用秒
11node_memory_usage_megabytes       # 应使用字节
12http_request_total                # 单数形式

建议 (SHOULD):

  1. 按词法排序时的便利分组
1# ✅ 好 - 公共组件在前,相关指标排在一起
2prometheus_tsdb_head_truncations_closed_total
3prometheus_tsdb_head_truncations_established_total
4prometheus_tsdb_head_truncations_failed_total
5prometheus_tsdb_head_truncations_total
6
7# 也可以 - 更易读但可能分散
8prometheus_tsdb_head_closed_truncations_total
9prometheus_tsdb_head_established_truncations_total
  1. 跨所有标签维度表示相同的逻辑测量对象
1# ✅ 好 - sum() 或 avg() 有意义
2request_duration_seconds
3bytes_transferred
4resource_usage_ratio
5
6# ❌ 差 - sum() 无意义(混合了不同类型)
7queue_capacity_and_size  # 混合容量和当前大小

为什么在名称中包含单位和类型?

Prometheus 强烈建议包含单位和类型,原因:

  1. 指标消费可靠性和 UX

    • 纯 YAML 配置中可直接理解指标类型和单位
    • 有助于事件响应时快速理解 PromQL 表达式
  2. 避免指标冲突

    • 随着采用增长,缺少单位信息会导致序列冲突
    • 例如 process_cpu 可能是秒或毫秒

标签规范

使用标签区分测量对象的特征:

1# ✅ 好
2api_http_requests_total{operation="create|update|delete"}
3api_request_duration_seconds{stage="extract|transform|load"}
4
5# ❌ 差 - 标签名在指标名中
6api_http_requests_create_total
7api_http_requests_update_total
8api_http_requests_delete_total

注意事项:

⚠️ 警告: 每个唯一的键值标签对组合代表一个新时间序列,会显著增加存储数据量。

不要使用高基数标签:

  • ❌ 用户 ID
  • ❌ 邮箱地址
  • ❌ 其他无界值集合

基础单位

类别基础单位备注
Timeseconds
Temperaturecelsius首选 celsius 而非 kelvin(实用性)
Lengthmeters
Bytesbytes
Bitsbytes始终使用 bytes 避免混淆
Percentratio值为 0-1(而非 0-100),后缀为 _ratio
Voltagevolts
Electric currentamperes
Energyjoules
Power-优先导出 counter of joules,用 rate(joules[5m]) 得到瓦特
Massgrams优先 grams 而非 kilograms 以避免 kilo 前缀问题

百分比示例:

1# ✅ 好
2disk_usage_ratio  # 值范围 0.0 - 1.0
3cpu_usage_ratio
4
5# 或使用模式 A_per_B
6requests_per_second
7errors_per_requests

17.2 Instrumentation (埋点)

如何埋点

简短答案: 埋点一切!

  • 每个库、子系统和服务至少应有几个指标
  • 埋点应是代码的组成部分
  • 在使用指标的同一文件中实例化指标类

三种服务类型

1. Online-Serving Systems (在线服务系统)

  • 人类或系统期待即时响应
  • 例如:数据库、HTTP 请求

关键指标:

  • 执行的查询数
  • 错误数
  • 延迟
  • 进行中的请求数(可选)

最佳实践:

  • ✅ 在客户端和服务器端都监控
  • ✅ 记录请求结束时(而非开始时)
  • ✅ 只在栈中一个点监控延迟(避免重复告警)

2. Offline Processing (离线处理)

  • 无人主动等待响应
  • 通常批量处理

关键指标(每个阶段):

  • 进入的项目数
  • 进行中的项目数
  • 最后处理时间
  • 发出的项目数

心跳技巧: 发送带时间戳的虚拟项目通过系统,导出最近心跳时间戳。


3. Batch Jobs (批处理任务)

  • 不连续运行
  • 抓取困难

关键指标:

  • 最后成功时间 (✅ 最重要)
  • 各阶段耗时
  • 总运行时间
  • 最后完成时间
  • 处理的记录总数

推送到 PushGateway:

1# Batch job 结束时推送指标
2echo "batch_job_last_success $(date +%s)" | \
3  curl --data-binary @- http://pushgateway:9091/metrics/job/batch_job

最佳实践:

  • 批处理运行超过几分钟 → 也使用 pull 监控
  • 运行频率超过每 15 分钟 → 考虑转为守护进程

子系统埋点

Libraries (库):

 1# ✅ 好 - 库自动埋点
 2import prometheus_client as prom
 3
 4db_query_duration = prom.Histogram(
 5    'db_query_duration_seconds',
 6    'Database query duration',
 7    ['database', 'operation']  # 区分不同资源
 8)
 9
10@db_query_duration.labels('users', 'select').time()
11def query_users():
12    # ...

Logging (日志):

  • 每一行日志代码 → 对应一个 counter
  • 多个相关日志 → 可共享一个 counter
  • 导出 info/error/warning 总行数

Failures (失败):

1http_requests_total = Counter('http_requests_total', 'Total requests', ['status'])
2http_request_errors = Counter('http_request_errors_total', 'Failed requests', ['type'])
3
4# 记录总数和失败数,方便计算失败率
5http_requests_total.labels(status='200').inc()
6http_request_errors.labels(type='timeout').inc()

Threadpools (线程池):

  • 排队请求数
  • 使用中的线程数
  • 总线程数
  • 处理的任务数
  • 任务耗时
  • 排队等待时间

Caches (缓存):

  • 总查询数
  • 命中数
  • 总延迟
  • 后端系统的查询数、错误数、延迟

需要注意的事项

1. 使用标签

1# ✅ 好 - 单个指标 + 标签
2http_responses_total{code="500"}
3http_responses_total{code="403"}
4http_responses_total{code="200"}
5
6# ❌ 差 - 多个指标
7http_responses_500_total
8http_responses_403_total
9http_responses_200_total

规则: 指标名称的任何部分都不应程序化生成(使用标签代替)


2. 不要过度使用标签

成本:

  • 每个 labelset 增加 RAM、CPU、磁盘和网络开销

指导原则:

  • 保持基数 < 10
  • 超过 10 的指标,整个系统限制在少数几个
  • 大多数指标应无标签

示例:

1# ✅ 可接受 - node_exporter 在 10k 节点上
2# 每个节点约 10 个文件系统 = 100k 时间序列
3node_filesystem_avail
4
5# ❌ 不可接受 - 添加用户配额
6# 10k 用户 × 10k 节点 = 1 亿时间序列 (太多!)
7node_filesystem_user_quota

最佳实践:

  • ✅ 从无标签开始
  • ✅ 根据具体用例逐步添加标签

3. Counter vs. Gauge, Summary vs. Histogram

简单规则:

  • 值能下降 → Gauge
  • 值只能上升 → Counter
 1# Counter - 只能增加(或重置)
 2http_requests_total = Counter(...)         # ✅
 3bytes_transferred_total = Counter(...)     # ✅
 4
 5# Gauge - 可上可下
 6memory_usage_bytes = Gauge(...)            # ✅
 7in_progress_requests = Gauge(...)          # ✅
 8temperature_celsius = Gauge(...)           # ✅
 9
10# ❌ 错误用法
11rate(gauge_metric[5m])  # 不要对 Gauge 使用 rate()

4. 时间戳,而非"时间间隔"

1# ✅ 好 - 导出 Unix 时间戳
2last_success_timestamp_seconds = Gauge(...)
3last_success_timestamp_seconds.set(time.time())
4
5# 查询时计算间隔
6time() - last_success_timestamp_seconds
7
8# ❌ 差 - 导出间隔(需要更新逻辑)
9time_since_last_success_seconds = Gauge(...)

5. 避免缺失指标

问题: 直到事件发生才出现的时间序列难以处理

解决方案: 预先导出默认值(如 0)

1# ✅ 好 - 大多数客户端库自动导出 0
2errors_total = Counter('errors_total', 'Errors', ['type'])
3# 即使从未调用,也会显示为 0
4
5# 手动初始化所有已知标签组合
6for error_type in ['timeout', 'connection', 'parse']:
7    errors_total.labels(type=error_type)

6. 内循环优化

性能关键代码 (> 100k 调用/秒):

  • Java counter 增量: 12-17ns
  • 限制内循环中的指标更新
  • 避免标签(或缓存标签查找结果)
  • 注意涉及时间/持续时间的指标(可能涉及系统调用)

最佳实践: 使用基准测试确定影响


17.3 Histograms (直方图)

注意: 本文档早于原生直方图(v2.40 实验性,v3.8 稳定)。

Count 和 Sum

Histograms 和 Summaries 都采样观测值,跟踪:

  • 观测数量 (_count 后缀) - Counter
  • 观测值总和 (_sum 后缀) - Counter (如无负值)

计算平均值:

1# 过去 5 分钟的平均请求时长
2  rate(http_request_duration_seconds_sum[5m])
3/
4  rate(http_request_duration_seconds_count[5m])

Apdex 分数

SLO 示例: 95% 请求在 300ms 内

1# 300ms 内的请求比例
2  sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
3/
4  sum(rate(http_request_duration_seconds_count[5m])) by (job)

Apdex 分数计算:

  • 目标: 300ms
  • 容忍: 1.2s
1(
2    sum(rate(http_request_duration_seconds_bucket{le="0.3"}[5m])) by (job)
3  +
4    sum(rate(http_request_duration_seconds_bucket{le="1.2"}[5m])) by (job)
5) / 2 / sum(rate(http_request_duration_seconds_count[5m])) by (job)

注意:除以 2 是因为桶是累积的


Quantiles (分位数)

φ-quantile: φ*N 排名的观测值 (0 ≤ φ ≤ 1)

  • 0.5-quantile = 中位数
  • 0.95-quantile = 95th 百分位

Histogram vs. Summary:

特性HistogramSummary
配置选择适合范围的桶选择所需 φ-quantiles 和滑动窗口
客户端性能非常便宜(仅增加计数器)昂贵(流式分位数计算)
服务器性能需计算分位数低成本
时间序列数每个桶一个每个分位数一个
分位数误差受桶宽度限制受配置值限制
规范Ad-hoc(PromQL)客户端预配置
聚合✅ 可聚合❌ 通常不可聚合

关键区别 - 聚合:

1# ❌ 差 - Summary 平均分位数无统计意义
2avg(http_request_duration_seconds{quantile="0.95"})
3
4# ✅ 好 - Histogram 可正确聚合
5histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

选择建议

两条经验法则:

  1. 需要聚合 → Histogram

  2. 其他情况:

    • 知道值的范围和分布 → Histogram
    • 需要精确分位数,无论范围和分布 → Summary

分位数误差

Histogram 误差示例:

桶配置: {le="0.1"}, {le="0.2"}, {le="0.3"}, {le="0.45"}

  • 真实 95th 是 220ms
  • Histogram 计算为 295ms(线性插值在 200-300ms 桶中)

Summary 误差示例:

配置: 0.95±0.01 (94th 到 96th 之间)

  • 如果分布范围宽,94th(270ms) 和 96th(330ms) 差异大
  • 可能在 SLO 内外都不确定

结论:

  • Histogram: 控制观测值维度的误差
  • Summary: 控制 φ 维度的误差

17.4 Alerting (告警最佳实践)

核心原则

简单化告警:

  • 仅告警症状(symptoms),而非每个可能原因
  • 告警应关联终端用户痛点
  • 告警应链接到相关控制台
  • 允许容错以适应小波动

告警命名

推荐: 使用 CamelCase

1# ✅ 推荐
2- alert: HighRequestLatency
3- alert: DatabaseDown
4- alert: ServiceUnavailable
5
6# 也可以,但不推荐
7- alert: high_request_latency

告警内容

Online-Serving Systems:

 1groups:
 2  - name: latency_errors
 3    rules:
 4      # ✅ 好 - 在栈顶层告警高延迟
 5      - alert: HighLatency
 6        expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
 7        for: 5m
 8    
 9      # ✅ 好 - 告警用户可见错误
10      - alert: HighErrorRate
11        expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
12        for: 5m
13    
14      # ❌ 差 - 不要在多个层级告警延迟
15      # 如果用户延迟正常,底层慢不需要告警

Offline Processing:

1- alert: ProcessingStalled
2  expr: time() - pipeline_last_processed_timestamp_seconds > 3600
3  for: 10m
4  annotations:
5    summary: "Pipeline {{ $labels.pipeline }} stalled"

Batch Jobs:

1- alert: BatchJobFailed
2  expr: time() - batch_job_last_success_timestamp_seconds > 14400  # 4 hours
3  for: 0m
4  annotations:
5    summary: "Batch job {{ $labels.job }} hasn't succeeded in 4 hours"
6
7# 批处理运行间隔 4h,耗时 1h
8# 阈值至少 2 倍完整运行时间(10h 合理)

容量告警

 1- alert: DiskSpaceWarning
 2  expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.15
 3  for: 30m
 4  labels:
 5    severity: warning
 6
 7- alert: DiskSpaceCritical
 8  expr: node_filesystem_avail_bytes / node_filesystem_size_bytes < 0.05
 9  for: 10m
10  labels:
11    severity: critical

元监控 (Metamonitoring)

监控监控基础设施本身:

1# ✅ 好 - 症状检测(黑盒测试)
2- alert: AlertingPipelineBroken
3  expr: up{job="blackbox-test"} == 0
4  # 测试: 告警从 PushGateway → Prometheus → Alertmanager → Email
5
6# 也可以 - 单组件告警
7- alert: PrometheusDown
8  expr: up{job="prometheus"} == 0

补充白盒监控:

  • 外部黑盒监控捕获不可见问题
  • 内部系统完全失败时的备用方案

17.5 Recording Rules (记录规则)

命名约定

格式: level:metric:operations

  • level - 聚合级别和标签
  • metric - 指标名称(从 counter 去除 _total,使用 rate/irate 时)
  • operations - 操作列表(最新操作在前)

示例:

1# level = instance_path (有 instance 和 path 标签)
2# metric = requests
3# operations = rate5m (5分钟 rate)
4- record: instance_path:requests:rate5m
5  expr: rate(requests_total[5m])
6
7# level = path (只有 path 标签,聚合掉 instance)
8- record: path:requests:rate5m
9  expr: sum without (instance)(instance_path:requests:rate5m)

命名规则

简化操作:

  • 省略 _sum(如有其他操作)
  • 合并结合操作 (min_min = min)
  • 无明显操作时使用 sum
  • 比率操作用 _per_ 分隔,操作称为 ratio

平均观测大小:

  • 去除 _count/_sum 后缀
  • mean 替换 rate

聚合规则

规则 1: 聚合比率时,分别聚合分子和分母

1# ❌ 差 - 不要平均比率或平均的平均
2avg(instance:requests_per_errors:ratio)
3
4# ✅ 好 - 分别聚合后再除
5  sum(instance:requests:rate5m)
6/
7  sum(instance:errors:rate5m)

规则 2: 始终指定 without子句

1# ✅ 好 - 保留其他标签(job 等)
2sum without (instance)(instance_path:requests:rate5m)
3
4# ❌ 差 - 可能丢失有用标签
5sum(instance_path:requests:rate5m)

完整示例

聚合请求率:

1# Step 1: 实例+路径级别
2- record: instance_path:requests:rate5m
3  expr: rate(requests_total{job="myjob"}[5m])
4
5# Step 2: 路径级别(聚合掉 instance)
6- record: path:requests:rate5m
7  expr: sum without (instance)(instance_path:requests:rate5m{job="myjob"})

失败率比例:

 1# Step 1: 失败率
 2- record: instance_path:request_failures:rate5m
 3  expr: rate(request_failures_total{job="myjob"}[5m])
 4
 5# Step 2: 实例+路径级别比率
 6- record: instance_path:request_failures_per_requests:ratio_rate5m
 7  expr: |2
 8      instance_path:request_failures:rate5m{job="myjob"}
 9    /
10      instance_path:requests:rate5m{job="myjob"}
11
12# Step 3: 路径级别比率(正确聚合)
13- record: path:request_failures_per_requests:ratio_rate5m
14  expr: |2
15      sum without (instance)(instance_path:request_failures:rate5m{job="myjob"})
16    /
17      sum without (instance)(instance_path:requests:rate5m{job="myjob"})
18
19# Step 4: Job 级别比率
20- record: job:request_failures_per_requests:ratio_rate5m
21  expr: |2
22      sum without (instance, path)(instance_path:request_failures:rate5m{job="myjob"})
23    /
24      sum without (instance, path)(instance_path:requests:rate5m{job="myjob"})

平均延迟(Summary):

 1- record: instance_path:request_latency_seconds_count:rate5m
 2  expr: rate(request_latency_seconds_count{job="myjob"}[5m])
 3
 4- record: instance_path:request_latency_seconds_sum:rate5m
 5  expr: rate(request_latency_seconds_sum{job="myjob"}[5m])
 6
 7# mean 替换 rate(因为是平均观测大小)
 8- record: instance_path:request_latency_seconds:mean5m
 9  expr: |2
10      instance_path:request_latency_seconds_sum:rate5m{job="myjob"}
11    /
12      instance_path:request_latency_seconds_count:rate5m{job="myjob"}
13
14- record: path:request_latency_seconds:mean5m
15  expr: |2
16      sum without (instance)(instance_path:request_latency_seconds_sum:rate5m{job="myjob"})
17    /
18      sum without (instance)(instance_path:request_latency_seconds_count:rate5m{job="myjob"})

平均速率(avg 函数):

1- record: job:request_latency_seconds_count:avg_rate5m
2  expr: avg without (instance, path)(instance_path:request_latency_seconds_count:rate5m{job="myjob"})

验证规则:

  • 聚合时,without 子句中的标签从输出 level 中移除
  • 无聚合时,level 始终匹配
  • 如果不匹配,规则可能有错误

17.6 Pushing Metrics (推送指标)

何时使用 Pushgateway?

仅限特定情况,不建议盲目使用

Pushgateway 的问题

问题 1: 单点故障和瓶颈

  • 多实例通过单个 Pushgateway → 成为瓶颈

问题 2: 失去自动健康监控

  • 丢失 Prometheus 的 up 指标(每次抓取生成)

问题 3: 永不遗忘

  • Pushgateway 永远不会忘记推送的序列
  • 必须手动通过 API 删除
  • 实例重命名/删除后指标仍然存在
  • 需要手动同步生命周期

唯一有效用例

Service-Level Batch Jobs:

  • 与特定机器/实例语义无关的批处理
  • 例如:为整个服务删除用户的批处理

特点:

  • 指标不应包含 machineinstance 标签
  • 减少管理 Pushgateway 中过期指标的负担
1# ✅ 好 - Service-level batch job
2echo "user_cleanup_total 150" | \
3  curl --data-binary @- http://pushgateway:9091/metrics/job/user_cleanup
4
5# ❌ 差 - Machine-specific batch job
6echo "backup_size_bytes 1073741824" | \
7  curl --data-binary @- http://pushgateway:9091/metrics/job/backup/instance/server1

替代策略

1. 防火墙/NAT 问题:

1# 将 Prometheus 移到网络屏障内
2# 或使用 PushProx

PushProx 架构:

  • Proxy 在网络外部
  • Targets 主动连接到 proxy
  • Prometheus 通过 proxy 拉取

2. 机器相关批处理任务:

使用 Node Exporter 的 textfile collector:

1# ✅ 好 - 使用 textfile collector
2cat > /var/lib/node_exporter/textfile_collector/backup.prom << EOF
3# HELP backup_last_success_timestamp Last successful backup time
4# TYPE backup_last_success_timestamp gauge
5backup_last_success_timestamp $(date +%s)
6# HELP backup_size_bytes Size of last backup
7# TYPE backup_size_bytes gauge
8backup_size_bytes 1073741824
9EOF

优点:

  • 自动生命周期管理
  • 机器宕机时指标自动消失
  • 无需管理 Pushgateway

最佳实践总结

Pushgateway 使用检查清单:

  • 是 service-level batch job?(不是特定机器)
  • 指标不包含 instance/machine 标签?
  • 可以手动管理指标生命周期?

如果任何一项为 No → 使用替代方案:

  • 机器相关批处理 → Node Exporter textfile collector
  • 防火墙问题 → PushProx 或移动 Prometheus
  • 其他情况 → 重新考虑架构

Practices 快速参考

Naming (命名):

  • 应用前缀 + 基础单位 + 复数后缀
  • 基数 < 10,避免高基数标签

Instrumentation (埋点):

  • Counter vs Gauge:值能下降用 Gauge
  • 导出时间戳,而非时间间隔
  • 避免缺失指标,预初始化

Histograms:

  • 需要聚合 → Histogram
  • 需要精确分位数 → Summary

Alerting:

  • 告警症状而非原因
  • 关联用户痛点
  • 在栈顶层告警

Recording Rules:

  • 格式:level:metric:operations
  • 始终用 without 子句
  • 聚合比率时分别聚合分子和分母

Pushing:

  • 仅用于 service-level batch jobs
  • 机器相关任务 → textfile collector
  • 防火墙问题 → PushProx


18. Exporters (导出器列表)

以下是 Prometheus 官方和第三方 Exporter 列表,涵盖各种系统、服务和应用。

Databases (数据库)

名称链接
Aerospike exporterGitHub
AWS RDS exporterGitHub
ClickHouse exporterGitHub
Consul exporter 🏅GitHub
Couchbase exporterGitHub
CouchDB exporterGitHub
Druid exporterGitHub
Elasticsearch exporterGitHub
EventStore exporterGitHub
IoTDB exporterGitHub
KDB+ exporterGitHub
Memcached exporter 🏅GitHub
MongoDB exporterGitHub
MongoDB query exporterGitHub
MongoDB Node.js Driver exporterGitHub
MSSQL server exporterGitHub
MySQL router exporterGitHub
MySQL server exporter 🏅GitHub
OpenTSDB exporterGitHub
Oracle DB exporterGitHub
PgBouncer exporterGitHub
PostgreSQL exporterGitHub
Presto exporterGitHub
ProxySQL exporterGitHub
RavenDB exporterGitHub
Redis exporterGitHub
RethinkDB exporterGitHub
SQL exporterGitHub
Tarantool metric libraryGitHub
Twemproxy exporterGitHub

名称链接
apcupsd exporterGitHub
BIG-IP exporterGitHub
Bosch Sensortec BMP/BME exporterGitHub
Collins exporterGitHub
Dell Hardware OMSA exporterGitHub
Disk usage exporterGitHub
Fortigate exporterGitHub
IBM Z HMC exporterGitHub
IoT Edison exporterGitHub
InfiniBand exporterGitHub
IPMI exporterGitHub
knxd exporterGitHub
Modbus exporterGitHub
Netgear Cable Modem exporterGitHub
Netgear Router exporterGitHub
Network UPS Tools (NUT) exporterGitHub
Node/system metrics exporter 🏅GitHub
NVIDIA GPU exporterGitHub
ProSAFE exporterGitHub
SmartRAID exporterGitLab
Waveplus Radon Sensor exporterGitHub
Weathergoose Climate Monitor exporterGitHub
Windows exporterGitHub
Intel® Optane™ PMem Controller exporterGitHub

Issue Trackers & CI/CD (问题跟踪 & 持续集成)

名称链接
Bamboo exporterGitHub
Bitbucket exporterGitHub
Confluence exporterGitHub
Jenkins exporterGitHub
JIRA exporterGitHub

Messaging Systems (消息系统)

名称链接
Beanstalkd exporterGitHub
EMQ exporterGitHub
Gearman exporterGitHub
IBM MQ exporterGitHub
Kafka exporterGitHub
NATS exporterGitHub
NSQ exporterGitHub
Mirth Connect exporterGitHub
MQTT blackbox exporterGitHub
MQTT2PrometheusGitHub
RabbitMQ exporterGitHub
RabbitMQ Management Plugin exporterGitHub
RocketMQ exporterGitHub
Solace exporterGitHub

Storage (存储)

名称链接
Ceph exporterGitHub
Ceph RADOSGW exporterGitHub
Gluster exporterGitHub
GPFS exporterGitHub
Hadoop HDFS FSImage exporterGitHub
HPE CSI info metrics providerDocs
HPE storage array exporterGitHub
Lustre exporterGitHub
NetApp E-Series exporterGitHub
Pure Storage exporterGitHub
ScaleIO exporterGitHub
Tivoli Storage Manager/IBM Spectrum ProtectGitHub

HTTP (HTTP 服务器)

名称链接
Apache exporterGitHub
HAProxy exporter 🏅GitHub
Nginx metric libraryGitHub
Nginx VTS exporterGitHub
Passenger exporterGitHub
Squid exporterGitHub
Tinyproxy exporterGitHub
Varnish exporterGitHub
WebDriver exporterGitHub

APIs (API 服务)

名称链接
AWS ECS exporterGitHub
AWS Health exporterGitHub
AWS SQS exporterGitHub
AWS SQS Prometheus exporterGitHub
Azure Health exporterGitHub
BigBlueButton exporterGitHub
Cloudflare exporterGitLab
Cryptowat exporterGitHub
DigitalOcean exporterGitHub
Docker Cloud exporterGitHub
Docker Hub exporterGitHub
Fastly exporterGitHub
GitHub exporterGitHub
Gmail exporterGitHub
GraphQL exporterGitHub
InstaClustr exporterGitHub
Mozilla Observatory exporterGitHub
OpenWeatherMap exporterGitHub
Pagespeed exporterGitHub
Rancher exporterGitHub
Speedtest exporterGitHub
Tankerkönig API exporterGitHub

Logging (日志)

名称链接
Fluentd exporterGitHub
Google’s mtail log data extractorGitHub
Grok exporterGitHub

FinOps (成本管理)

名称链接
AWS Cost exporterGitHub
Azure Cost exporterGitHub
Kubernetes Cost exporterGitHub

Miscellaneous (其他)

名称链接
ACT Fibernet exporterGit
BIND exporterGitHub
BIND query exporterGitHub
Bitcoind exporterGitHub
Blackbox exporter 🏅GitHub
Bungeecord exporterGitHub
BOSH exporterGitHub
cAdvisorGitHub
Cachet exporterGitHub
ccache exporterGitHub
c-lightning exporterGitHub
DHCPD leases exporterGitHub
Dovecot exporterGitHub
Dnsmasq exporterGitHub
eBPF exporterGitHub
eBPF network traffic exporterGitHub
Ethereum Client exporterGitHub
FFmpeg exporterGitHub
File statistics exporterGitHub
JFrog Artifactory exporterGitHub
Hostapd exporterGitHub
IBM Security Verify Access exporterGitLab
IPsec exporterGitHub
ipset exporterGitHub
IRCd exporterGitHub
Linux HA ClusterLabs exporterGitHub
JMeter pluginGitHub
JSON exporterGitHub
Kannel exporterGitHub
Kemp LoadBalancer exporterGitHub
Kibana exporterGitHub
kube-state-metricsGitHub
Locust exporterGitHub
Meteor JS web framework exporterAtmosphere
Minecraft exporter moduleGitHub
Minecraft exporterGitHub
NetBird exporterGitHub
Nomad exporterGitLab
nftables exporterGitHub
OpenStack exporterGitHub
OpenStack blackbox exporterGitHub
OpenVPN exporterGitHub
oVirt exporterGitHub
Pact Broker exporterGitHub
PHP-FPM exporterGitHub
PowerDNS exporterGitHub
Podman exporterGitHub
Prefect2 exporterGitHub
Process exporterGitHub
rTorrent exporterGitHub
Rundeck exporterGitHub
SABnzbd exporterGitHub
SAML exporterGitHub
Script exporterGitHub
Shield exporterGitHub
Smokeping proberGitHub
SMTP/Maildir MDA blackbox proberGitHub
SoftEther exporterGitHub
SSH exporterGitHub
Teamspeak3 exporterGitHub
Transmission exporterGitHub
Unbound exporterGitHub
WireGuard exporterGitHub
Xen exporterGitHub

图例:

  • 🏅 = 官方维护的 Exporter

参考链接:


统计:

  • 数据库: 30个
  • 硬件: 24个
  • CI/CD: 5个
  • 消息系统: 14个
  • 存储: 12个
  • HTTP: 9个
  • APIs: 22个
  • 日志: 3个
  • FinOps: 3个
  • 其他: 70+个

总计: 200+ 个 Exporter



附录: Golang Exporter 完整示例

概述

这是一个完整的 Golang Exporter 示例,展示了如何使用 Prometheus 客户端库实现:

  • Gauge - 内存使用、CPU 核心数等
  • Histogram - HTTP 请求延迟分布

完整代码

  1package main
  2
  3import (
  4 "context"
  5 "errors"
  6 "flag"
  7 "fmt"
  8 "log"
  9 "math/rand"
 10 "net/http"
 11 "os"
 12 "os/signal"
 13 "strconv"
 14 "syscall"
 15 "time"
 16
 17 "github.com/prometheus/client_golang/prometheus"
 18 "github.com/prometheus/client_golang/prometheus/promhttp"
 19 "github.com/shirou/gopsutil/cpu"
 20 "github.com/shirou/gopsutil/mem"
 21 "github.com/shirou/gopsutil/net"
 22)
 23
 24var listenPort string
 25
 26// ExporterMetrics 存储指标的结构体
 27type ExporterMetrics struct {
 28 connectInfo *prometheus.Desc
 29 memInfo     *prometheus.Desc
 30 cpuNums     *prometheus.Desc
 31}
 32
 33// Describe 将所有可能的指标描述符发送到提供的通道
 34func (collector *ExporterMetrics) Describe(ch chan<- *prometheus.Desc) {
 35 ch <- collector.connectInfo
 36 ch <- collector.memInfo
 37 ch <- collector.cpuNums
 38}
 39
 40// Collect 用于 Prometheus 收集指标
 41func (collector *ExporterMetrics) Collect(ch chan<- prometheus.Metric) {
 42 collector.collectMemoryMetrics(ch)
 43}
 44
 45// collectMemoryMetrics 收集内存使用信息
 46func (collector *ExporterMetrics) collectMemoryMetrics(ch chan<- prometheus.Metric) {
 47 memoryStats, err := mem.VirtualMemory()
 48 if err != nil {
 49  log.Printf("Error collecting memory stats: %v\n", err)
 50  return
 51 }
 52
 53 ch <- prometheus.MustNewConstMetric(
 54  collector.memInfo, prometheus.GaugeValue, float64(memoryStats.Free), "free",
 55 )
 56 ch <- prometheus.MustNewConstMetric(
 57  collector.memInfo, prometheus.GaugeValue, float64(memoryStats.Used), "used",
 58 )
 59 ch <- prometheus.MustNewConstMetric(
 60  collector.memInfo, prometheus.GaugeValue, float64(memoryStats.Total), "total",
 61 )
 62}
 63
 64// 内存指标信息
 65func memDesc() *prometheus.Desc {
 66 return prometheus.NewDesc(
 67  "memory_usage",
 68  "Memory usage metrics including total, used, and free memory",
 69  []string{"type"},
 70  nil)
 71}
 72
 73// cpu指标信息
 74func cpuDesc() *prometheus.Desc {
 75 return prometheus.NewDesc(
 76  "cpu_cores",
 77  "Number of CPU cores available",
 78  nil,
 79  nil)
 80}
 81
 82// 连接指标信息
 83func connDesc() *prometheus.Desc {
 84 return prometheus.NewDesc(
 85  "connection_status",
 86  "Status of network connections",
 87  []string{"local_addr", "local_port", "remote_addr", "remote_port", "status", "pid"},
 88  nil)
 89}
 90
 91// newMetricsCollector 创建并初始化一个新的指标收集器
 92func newMetricsCollector() *ExporterMetrics {
 93 return &ExporterMetrics{
 94  connectInfo: connDesc(),
 95  memInfo:     memDesc(),
 96  cpuNums:     cpuDesc(),
 97 }
 98}
 99
100// =============== Histogram 示例 ===============
101
102// 创建 Histogram 指标 - HTTP 请求延迟
103var (
104 httpRequestDuration = prometheus.NewHistogramVec(
105  prometheus.HistogramOpts{
106   Name: "http_request_duration_seconds",
107   Help: "HTTP request latency distributions",
108   // 自定义桶边界: 1ms, 5ms, 10ms, 50ms, 100ms, 500ms, 1s, 5s
109   Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5},
110  },
111  []string{"method", "endpoint", "status"}, // 标签
112 )
113)
114
115// 模拟 API 处理函数 - 用于演示 Histogram
116func apiHandler(w http.ResponseWriter, r *http.Request) {
117 // 记录请求开始时间
118 start := time.Now()
119
120 // 模拟业务处理(随机延迟 10-500ms)
121 processingTime := time.Duration(10+rand.Intn(490)) * time.Millisecond
122 time.Sleep(processingTime)
123
124 // 模拟不同的响应状态
125 statusCode := 200
126 if rand.Float64() < 0.1 { // 10% 概率返回 500
127  statusCode = 500
128 }
129
130 // 记录请求耗时到 Histogram
131 duration := time.Since(start).Seconds()
132 httpRequestDuration.WithLabelValues(
133  r.Method,
134  r.URL.Path,
135  strconv.Itoa(statusCode),
136 ).Observe(duration)
137
138 // 返回响应
139 w.WriteHeader(statusCode)
140 fmt.Fprintf(w, "Request processed in %.3f seconds\n", duration)
141}
142
143// 健康检查接口
144func health(w http.ResponseWriter, r *http.Request) {
145 w.Write([]byte("health"))
146}
147
148func main() {
149 flag.StringVar(&listenPort, "port", "28880", "exporter listen port")
150 flag.Parse()
151
152 // 初始化随机数种子
153 rand.Seed(time.Now().UnixNano())
154
155 // 注册自定义 Collector (Gauge)
156 allMetrics := newMetricsCollector()
157 prometheus.MustRegister(allMetrics)
158
159 // 注册 Histogram 指标
160 prometheus.MustRegister(httpRequestDuration)
161
162 // 路由配置
163 http.Handle("/metrics", promhttp.Handler())
164 http.HandleFunc("/healthz", health)
165 http.HandleFunc("/api/users", apiHandler)
166 http.HandleFunc("/api/orders", apiHandler)
167 http.HandleFunc("/api/products", apiHandler)
168
169 server := &http.Server{Addr: fmt.Sprintf(":%s", listenPort)}
170
171 log.Printf("Starting server on port %s\n", listenPort)
172 log.Printf("Metrics: http://localhost:%s/metrics\n", listenPort)
173
174 go func() {
175  if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
176   log.Printf("ListenAndServe(): %s\n", err)
177   panic(err)
178  }
179 }()
180
181 sigChan := make(chan os.Signal, 1)
182 signal.Notify(sigChan, syscall.SIGTERM, os.Interrupt, syscall.SIGKILL)
183
184 sig := <-sigChan
185 log.Println("SIGTERM received, shutting down gracefully...")
186
187 timeout, cancel := context.WithTimeout(context.Background(), 60*time.Second)
188 defer cancel()
189
190 if err := server.Shutdown(timeout); err != nil {
191  log.Printf("Server Close Error: %s\n", err)
192 } else {
193  log.Println("server Close Successful")
194 }
195}

使用说明

1. 安装依赖

1go mod init exporter-demo
2go get github.com/prometheus/client_golang/prometheus
3go get github.com/prometheus/client_golang/prometheus/promhttp
4go get github.com/shirou/gopsutil

2. 运行 Exporter

1# 默认端口 28880
2go run main.go
3
4# 自定义端口
5go run main.go -port 9090

3. 测试 API 端点

1# 生成一些请求,产生 Histogram 数据
2for i in {1..100}; do
3  curl http://localhost:28880/api/users
4  curl http://localhost:28880/api/orders
5  curl http://localhost:28880/api/products
6done

4. 查看指标

访问 http://localhost:28880/metrics 查看暴露的指标:

 1# Gauge 指标 - 内存使用
 2memory_usage{type="free"} 8589934592
 3memory_usage{type="used"} 7340032000
 4memory_usage{type="total"} 17179869184
 5
 6# Histogram 指标 - HTTP 请求延迟
 7http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="0.001"} 0
 8http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="0.005"} 0
 9http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="0.01"} 0
10http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="0.05"} 5
11http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="0.1"} 18
12http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="0.5"} 100
13http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="1"} 100
14http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="5"} 100
15http_request_duration_seconds_bucket{method="GET",endpoint="/api/users",status="200",le="+Inf"} 100
16http_request_duration_seconds_sum{method="GET",endpoint="/api/users",status="200"} 25.384
17http_request_duration_seconds_count{method="GET",endpoint="/api/users",status="200"} 100

代码要点解析

1. Gauge 指标 (自定义 Collector)

 1// 实现 prometheus.Collector 接口
 2type ExporterMetrics struct {
 3    memInfo *prometheus.Desc
 4}
 5
 6// Describe - 描述指标
 7func (c *ExporterMetrics) Describe(ch chan<- *prometheus.Desc) {
 8    ch <- c.memInfo
 9}
10
11// Collect - 收集指标
12func (c *ExporterMetrics) Collect(ch chan<- prometheus.Metric) {
13    memoryStats, _ := mem.VirtualMemory()
14    ch <- prometheus.MustNewConstMetric(
15        c.memInfo, 
16        prometheus.GaugeValue, 
17        float64(memoryStats.Free), 
18        "free",
19    )
20}

特点:

  • 动态收集系统指标
  • 每次抓取时实时获取最新值
  • 适合 CPU、内存、磁盘等系统指标

2. Histogram 指标

 1// 定义 Histogram
 2var httpRequestDuration = prometheus.NewHistogramVec(
 3    prometheus.HistogramOpts{
 4        Name: "http_request_duration_seconds",
 5        Help: "HTTP request latency distributions",
 6        Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5},
 7    },
 8    []string{"method", "endpoint", "status"},
 9)
10
11// 记录观测值
12httpRequestDuration.WithLabelValues("GET", "/api/users", "200").Observe(0.123)

桶配置选项:

 1// 1. 自定义桶
 2Buckets: []float64{0.001, 0.01, 0.1, 1, 10}
 3
 4// 2. 默认桶
 5Buckets: prometheus.DefBuckets  // [.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10]
 6
 7// 3. 线性桶 (起始值, 宽度, 数量)
 8Buckets: prometheus.LinearBuckets(0, 10, 10)  // [0, 10, 20, ..., 90]
 9
10// 4. 指数桶 (起始值, 因子, 数量)
11Buckets: prometheus.ExponentialBuckets(1, 2, 10)  // [1, 2, 4, 8, ..., 512]

Prometheus 配置

将此 Exporter 添加到 Prometheus 抓取配置:

1scrape_configs:
2  - job_name: 'custom-exporter'
3    static_configs:
4      - targets: ['localhost:28880']
5    scrape_interval: 15s

PromQL 查询示例

查询 Histogram P99 延迟:

 1# 各端点 P99 延迟
 2histogram_quantile(0.99, 
 3  sum by (endpoint, le) (
 4    rate(http_request_duration_seconds_bucket[5m])
 5  )
 6)
 7
 8# 各端点平均延迟
 9sum by (endpoint) (rate(http_request_duration_seconds_sum[5m])) 
10/ 
11sum by (endpoint) (rate(http_request_duration_seconds_count[5m]))
12
13# 请求 QPS
14sum by (endpoint) (rate(http_request_duration_seconds_count[5m]))
15
16# 错误率
17sum by (endpoint) (rate(http_request_duration_seconds_count{status="500"}[5m])) 
18/ 
19sum by (endpoint) (rate(http_request_duration_seconds_count[5m]))

最佳实践

1. Histogram 桶设计

  • ✅ 根据实际延迟分布设计桶边界
  • ✅ 覆盖 SLO 阈值附近的桶
  • ✅ 避免桶过多(通常 10-20 个桶)

2. 标签使用

  • ✅ 保持标签基数低(<1000)
  • ✅ 使用有意义的标签(method, endpoint, status)
  • ❌ 避免高基数标签(user_id, request_id)

3. 指标命名

  • ✅ 使用描述性名称
  • ✅ 包含单位后缀(_seconds, _bytes)
  • ✅ 遵循 Prometheus 命名规范

4. 性能优化

  • ✅ 使用 HistogramVec 而非多个 Histogram
  • ✅ 缓存标签值,避免重复分配
  • ✅ 在高频路径使用 Observe() 而非 ObserveWithExemplar()

扩展示例

添加 Counter:

 1var httpRequestsTotal = prometheus.NewCounterVec(
 2    prometheus.CounterOpts{
 3        Name: "http_requests_total",
 4        Help: "Total number of HTTP requests",
 5    },
 6    []string{"method", "endpoint", "status"},
 7)
 8
 9// 在 apiHandler 中递增
10httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, strconv.Itoa(statusCode)).Inc()

添加 Summary:

 1var httpRequestDurationSummary = prometheus.NewSummaryVec(
 2    prometheus.SummaryOpts{
 3        Name: "http_request_duration_summary_seconds",
 4        Help: "HTTP request latency summary",
 5        Objectives: map[float64]float64{
 6            0.5: 0.05,   // P50, 误差 ±5%
 7            0.9: 0.01,   // P90, 误差 ±1%
 8            0.99: 0.001, // P99, 误差 ±0.1%
 9        },
10    },
11    []string{"method", "endpoint"},
12)
13
14// 记录观测值
15httpRequestDurationSummary.WithLabelValues("GET", "/api/users").Observe(0.123)

故障排查

问题 1: 指标未暴露

1# 检查注册
2prometheus.MustRegister(httpRequestDuration)
3
4# 检查路由
5http.Handle("/metrics", promhttp.Handler())

问题 2: Histogram 无数据

1# 确保调用了 Observe()
2httpRequestDuration.WithLabelValues(...).Observe(duration)
3
4# 检查桶配置是否合理
5Buckets: []float64{0.001, 0.01, 0.1, 1, 10}

问题 3: 内存占用高

1# 减少标签基数
2# 减少桶数量
3# 使用标签缓存

总结

这个示例展示了:

  • ✅ Gauge 指标 - 使用自定义 Collector
  • ✅ Histogram 指标 - 记录延迟分布
  • ✅ 优雅关闭 - 处理信号和超时
  • ✅ 多端点路由 - 健康检查、指标、API

完整代码可作为生产环境 Exporter 的起点,根据实际需求扩展更多指标类型和业务逻辑。



附录: Pushgateway Golang 示例

概述

Pushgateway 是 Prometheus 生态中用于接收短期任务推送指标的中间组件。本示例展示如何使用 Golang 客户端推送指标到 Pushgateway。

适用场景

✅ 适合使用 Pushgateway:

  • 批处理任务(Batch Jobs)
  • 短期运行的脚本
  • 定时任务(Cron Jobs)
  • 无法被 Prometheus 抓取的服务

❌ 不适合使用 Pushgateway:

  • 长期运行的服务(应使用 Pull 模式)
  • 高频更新的指标
  • 需要自动服务发现的场景

完整代码

  1package main
  2
  3import (
  4 "log"
  5 "time"
  6
  7 "github.com/prometheus/client_golang/prometheus"
  8 "github.com/prometheus/client_golang/prometheus/push"
  9)
 10
 11// MetricCollector 封装指标收集器
 12type MetricCollector struct {
 13 counter   *prometheus.CounterVec
 14 gauge     *prometheus.GaugeVec
 15 histogram *prometheus.HistogramVec
 16 registry  *prometheus.Registry
 17}
 18
 19func NewMetricCollector() *MetricCollector {
 20 // 创建一个带标签的计数器
 21 counter := prometheus.NewCounterVec(
 22  prometheus.CounterOpts{
 23   Name: "example_counter_total",
 24   Help: "Example counter metric",
 25  },
 26  []string{"service", "endpoint", "status"},
 27 )
 28
 29 // 创建一个带标签的 gauge
 30 gauge := prometheus.NewGaugeVec(
 31  prometheus.GaugeOpts{
 32   Name: "example_gauge_value",
 33   Help: "Example gauge metric",
 34  },
 35  []string{"service", "region", "instance_type"},
 36 )
 37
 38 // 创建一个带标签的直方图
 39 histogram := prometheus.NewHistogramVec(
 40  prometheus.HistogramOpts{
 41   Name:    "example_histogram_seconds",
 42   Help:    "Example histogram metric",
 43   Buckets: prometheus.LinearBuckets(0, 0.1, 10),
 44  },
 45  []string{"service", "operation", "status"},
 46 )
 47
 48 // 创建 Registry
 49 registry := prometheus.NewRegistry()
 50 registry.MustRegister(counter)
 51 registry.MustRegister(gauge)
 52 registry.MustRegister(histogram)
 53
 54 return &MetricCollector{
 55  counter:   counter,
 56  gauge:     gauge,
 57  histogram: histogram,
 58  registry:  registry,
 59 }
 60}
 61
 62func (mc *MetricCollector) RecordMetrics() {
 63 // 记录计数器
 64 mc.counter.With(prometheus.Labels{
 65  "service":  "api",
 66  "endpoint": "/users",
 67  "status":   "success",
 68 }).Inc()
 69
 70 mc.counter.With(prometheus.Labels{
 71  "service":  "api",
 72  "endpoint": "/orders",
 73  "status":   "error",
 74 }).Inc()
 75
 76 // 记录 Gauge
 77 mc.gauge.With(prometheus.Labels{
 78  "service":       "backend",
 79  "region":        "us-east-1",
 80  "instance_type": "t2.micro",
 81 }).Set(42.0)
 82
 83 mc.gauge.With(prometheus.Labels{
 84  "service":       "backend",
 85  "region":        "eu-west-1",
 86  "instance_type": "t2.small",
 87 }).Set(56.0)
 88
 89 // 记录 Histogram
 90 mc.histogram.With(prometheus.Labels{
 91  "service":   "database",
 92  "operation": "query",
 93  "status":    "success",
 94 }).Observe(0.23)
 95
 96 mc.histogram.With(prometheus.Labels{
 97  "service":   "database",
 98  "operation": "insert",
 99  "status":    "success",
100 }).Observe(0.15)
101}
102
103func (mc *MetricCollector) PushMetrics(pushGatewayURL string, jobName string) error {
104 pusher := push.New(pushGatewayURL, jobName).
105  Gatherer(mc.registry)
106
107 // 添加分组标签
108 pusher.Grouping("instance", "example_instance")
109 pusher.Grouping("environment", "production")
110
111 // 推送指标
112 return pusher.Push()
113}
114
115// 使用 Basic Auth 推送
116func (mc *MetricCollector) PushMetricsWithBasicAuth(
117 pushGatewayURL, jobName, username, password string,
118) error {
119 pusher := push.New(pushGatewayURL, jobName).
120  Gatherer(mc.registry).
121  BasicAuth(username, password)
122
123 pusher.Grouping("instance", "example_instance")
124 pusher.Grouping("environment", "production")
125
126 return pusher.Push()
127}
128
129// 添加指标(保留旧数据)
130func (mc *MetricCollector) AddMetrics(pushGatewayURL, jobName string) error {
131 pusher := push.New(pushGatewayURL, jobName).
132  Gatherer(mc.registry)
133
134 pusher.Grouping("instance", "example_instance")
135
136 // 使用 Add() 而非 Push()
137 return pusher.Add()
138}
139
140// 删除指标
141func (mc *MetricCollector) DeleteMetrics(pushGatewayURL, jobName string) error {
142 pusher := push.New(pushGatewayURL, jobName)
143
144 pusher.Grouping("instance", "example_instance")
145 pusher.Grouping("environment", "production")
146
147 return pusher.Delete()
148}
149
150func main() {
151 collector := NewMetricCollector()
152 pushGatewayURL := "http://localhost:9091"
153 jobName := "batch_job"
154
155 // 记录并推送指标
156 collector.RecordMetrics()
157 err := collector.PushMetrics(pushGatewayURL, jobName)
158 if err != nil {
159  log.Printf("Could not push to Pushgateway: %v", err)
160 } else {
161  log.Println("Successfully pushed metrics to Pushgateway")
162 }
163
164 // 30秒后删除指标
165 time.Sleep(30 * time.Second)
166 err = collector.DeleteMetrics(pushGatewayURL, jobName)
167 if err != nil {
168  log.Printf("Could not delete metrics: %v", err)
169 } else {
170  log.Println("Successfully deleted metrics from Pushgateway")
171 }
172}

使用说明

1. 启动 Pushgateway

Docker 方式:

1docker run -d -p 9091:9091 prom/pushgateway

二进制方式:

1# 下载
2wget https://github.com/prometheus/pushgateway/releases/download/v1.6.2/pushgateway-1.6.2.linux-amd64.tar.gz
3tar xvfz pushgateway-1.6.2.linux-amd64.tar.gz
4cd pushgateway-1.6.2.linux-amd64
5
6# 启动
7./pushgateway

访问 http://localhost:9091 查看 Web UI。


2. 安装 Go 依赖

1go mod init pushgateway-demo
2go get github.com/prometheus/client_golang/prometheus
3go get github.com/prometheus/client_golang/prometheus/push

3. 运行示例

1go run main.go

输出:

1Successfully pushed metrics to Pushgateway
2Successfully deleted metrics from Pushgateway

4. 查看推送的指标

访问 http://localhost:9091/metrics:

 1# Counter
 2example_counter_total{environment="production",instance="example_instance",job="batch_job",service="api",endpoint="/users",status="success"} 1
 3example_counter_total{environment="production",instance="example_instance",job="batch_job",service="api",endpoint="/orders",status="error"} 1
 4
 5# Gauge
 6example_gauge_value{environment="production",instance="example_instance",job="batch_job",service="backend",region="us-east-1",instance_type="t2.micro"} 42
 7
 8# Histogram
 9example_histogram_seconds_bucket{environment="production",instance="example_instance",job="batch_job",service="database",operation="query",status="success",le="0.1"} 0
10example_histogram_seconds_bucket{environment="production",instance="example_instance",job="batch_job",service="database",operation="query",status="success",le="0.2"} 0
11example_histogram_seconds_bucket{environment="production",instance="example_instance",job="batch_job",service="database",operation="query",status="success",le="0.3"} 1
12example_histogram_seconds_sum{environment="production",instance="example_instance",job="batch_job",service="database",operation="query",status="success"} 0.23
13example_histogram_seconds_count{environment="production",instance="example_instance",job="batch_job",service="database",operation="query",status="success"} 1

核心概念

1. Push vs Add vs Delete

1// Push - 覆盖指定 job/grouping 的所有指标
2pusher.Push()
3
4// Add - 添加指标,保留已有指标
5pusher.Add()
6
7// Delete - 删除指定 job/grouping 的所有指标
8pusher.Delete()

区别示例:

 1// 第一次推送
 2gauge.Set(10)
 3pusher.Push()  // Pushgateway 中: gauge = 10
 4
 5// 第二次推送 (不同指标)
 6counter.Inc()
 7pusher.Push()  // Pushgateway 中: gauge 被删除, counter = 1
 8
 9// 使用 Add 替代
10gauge.Set(10)
11pusher.Push()  // gauge = 10
12
13counter.Inc()
14pusher.Add()   // gauge = 10, counter = 1 (保留 gauge)

2. 分组标签 (Grouping Labels)

分组标签用于区分不同的指标来源:

1pusher.Grouping("instance", "server-1")
2pusher.Grouping("environment", "production")

URL 映射:

1http://localhost:9091/metrics/job/batch_job/instance/server-1/environment/production

最佳实践:

  • ✅ 使用有意义的分组标签(instance, environment, region)
  • ✅ 保持分组标签一致
  • ❌ 避免高基数分组标签(user_id, request_id)

3. Registry 使用

为什么使用自定义 Registry?

 1// ❌ 不好 - 使用默认 Registry
 2prometheus.MustRegister(gauge)
 3pusher := push.New(url, job).Gatherer(prometheus.DefaultGatherer)
 4// 会推送所有默认的 Go 运行时指标
 5
 6// ✅ 好 - 使用自定义 Registry
 7registry := prometheus.NewRegistry()
 8registry.MustRegister(gauge)
 9pusher := push.New(url, job).Gatherer(registry)
10// 只推送显式注册的指标

实际应用场景

场景 1: Cron Job 批处理

 1// backup_job.go
 2func runBackup() {
 3 collector := NewMetricCollector()
 4 
 5 // 执行备份
 6 start := time.Now()
 7 err := performBackup()
 8 duration := time.Since(start).Seconds()
 9 
10 // 记录指标
11 if err != nil {
12  collector.counter.With(prometheus.Labels{
13   "service": "backup",
14   "status":  "error",
15  }).Inc()
16 } else {
17  collector.counter.With(prometheus.Labels{
18   "service": "backup",
19   "status":  "success",
20  }).Inc()
21  
22  collector.gauge.With(prometheus.Labels{
23   "service": "backup",
24   "type":    "duration",
25  }).Set(duration)
26 }
27 
28 // 推送到 Pushgateway
29 collector.PushMetrics("http://pushgateway:9091", "backup_job")
30}

Crontab 配置:

10 2 * * * /usr/local/bin/backup_job

场景 2: 数据处理脚本

 1func processData(batchID string) {
 2 collector := NewMetricCollector()
 3 
 4 // 处理记录数
 5 recordsProcessed := 0
 6 recordsFailed := 0
 7 
 8 for _, record := range records {
 9  if processRecord(record) {
10   recordsProcessed++
11  } else {
12   recordsFailed++
13  }
14 }
15 
16 // 记录指标
17 collector.gauge.With(prometheus.Labels{
18  "batch_id": batchID,
19  "status":   "processed",
20 }).Set(float64(recordsProcessed))
21 
22 collector.gauge.With(prometheus.Labels{
23  "batch_id": batchID,
24  "status":   "failed",
25 }).Set(float64(recordsFailed))
26 
27 // 推送
28 collector.PushMetrics("http://pushgateway:9091", "data_processor")
29}

场景 3: 定期推送 (长期运行脚本)

 1func monitorWorker() {
 2 collector := NewMetricCollector()
 3 ticker := time.NewTicker(30 * time.Second)
 4 defer ticker.Stop()
 5 
 6 for range ticker.C {
 7  // 收集指标
 8  queueSize := getQueueSize()
 9  collector.gauge.With(prometheus.Labels{
10   "queue": "tasks",
11  }).Set(float64(queueSize))
12  
13  // 推送
14  err := collector.PushMetrics("http://pushgateway:9091", "worker")
15  if err != nil {
16   log.Printf("Push failed: %v", err)
17  }
18 }
19}

Prometheus 配置

配置 Prometheus 抓取 Pushgateway:

1scrape_configs:
2  - job_name: 'pushgateway'
3    honor_labels: true  # 重要: 保留推送的 job 标签
4    static_configs:
5      - targets: ['localhost:9091']

关键配置说明:

  • honor_labels: true - 保留 Pushgateway 中的 jobinstance 标签
  • 不设置会导致标签被 Prometheus 的抓取标签覆盖

PromQL 查询示例

 1# 查询批处理任务成功率
 2sum by (service) (example_counter_total{status="success"})
 3/
 4sum by (service) (example_counter_total)
 5
 6# 查询最近一次推送时间
 7push_time_seconds{job="batch_job"}
 8
 9# 查询各批处理任务的处理记录数
10example_gauge_value{status="processed"}
11
12# 告警: 批处理任务失败
13example_counter_total{status="error"} > 0
14
15# 告警: Pushgateway 长时间未收到推送
16time() - push_time_seconds{job="batch_job"} > 3600

最佳实践

1. 使用 Add 而非 Push (多指标场景)

 1// ❌ 不好 - 第二次 Push 会覆盖第一次的 gauge
 2gauge.Set(10)
 3pusher.Push()
 4
 5counter.Inc()
 6pusher.Push()  // gauge 被删除!
 7
 8// ✅ 好 - 使用 Add 保留已有指标
 9gauge.Set(10)
10pusher.Push()
11
12counter.Inc()
13pusher.Add()   // gauge 和 counter 都保留

2. 任务结束时删除指标

 1func runJob() {
 2 collector := NewMetricCollector()
 3 defer func() {
 4  // 任务结束时清理指标
 5  collector.DeleteMetrics(pushGatewayURL, jobName)
 6 }()
 7 
 8 // 执行任务...
 9 collector.RecordMetrics()
10 collector.PushMetrics(pushGatewayURL, jobName)
11}

3. 添加时间戳指标

1// Pushgateway 自动添加的指标
2push_time_seconds{job="batch_job",instance="server-1"} 1700000000
3push_failure_time_seconds{job="batch_job",instance="server-1"} 0
4
5// 用于告警
6time() - push_time_seconds{job="batch_job"} > 3600

4. 错误处理和重试

 1func PushWithRetry(collector *MetricCollector, url, job string) error {
 2 maxRetries := 3
 3 var err error
 4 
 5 for i := 0; i < maxRetries; i++ {
 6  err = collector.PushMetrics(url, job)
 7  if err == nil {
 8   return nil
 9  }
10  
11  log.Printf("Push attempt %d failed: %v", i+1, err)
12  time.Sleep(time.Duration(i+1) * time.Second)
13 }
14 
15 return fmt.Errorf("push failed after %d retries: %w", maxRetries, err)
16}

安全性

1. 使用 Basic Auth

1pusher := push.New(url, job).
2 BasicAuth("username", "password")

Pushgateway 启动参数:

1./pushgateway \
2  --web.basic-auth-file=/etc/pushgateway/auth.yml

auth.yml:

1basic_auth_users:
2  admin: $2y$10$... # bcrypt hash

2. TLS 加密

 1import "crypto/tls"
 2
 3tlsConfig := &tls.Config{
 4 InsecureSkipVerify: false,
 5}
 6
 7client := &http.Client{
 8 Transport: &http.Transport{
 9  TLSClientConfig: tlsConfig,
10 },
11}
12
13pusher := push.New("https://pushgateway:9091", job).
14 Client(client)

常见问题

Q1: Pushgateway 指标一直存在?

A: 使用 Delete() 手动删除,或者重启 Pushgateway:

1// 任务结束时删除
2defer collector.DeleteMetrics(url, job)

Q2: 多个实例推送到同一 job?

A: 使用不同的分组标签区分:

1hostname, _ := os.Hostname()
2pusher.Grouping("instance", hostname)

Q3: Push 失败怎么办?

A: 实现重试机制,记录到本地日志:

1err := collector.PushMetrics(url, job)
2if err != nil {
3 // 记录到本地文件
4 logMetrics(collector.registry)
5}

Q4: Pushgateway 重启后数据丢失?

A: Pushgateway 是无状态的,重启后数据丢失。解决方案:

  • 使用持久化 Pushgateway (–persistence.file)
  • 批处理任务重新推送指标
1./pushgateway --persistence.file=/data/metrics.db

监控 Pushgateway

关键指标:

1# Pushgateway 存储的时间序列数
2pushgateway_http_push_size_bytes
3
4# HTTP 推送请求数
5pushgateway_http_requests_total
6
7# 推送失败数
8rate(pushgateway_http_requests_total{code!="200"}[5m])

告警规则:

 1groups:
 2  - name: pushgateway
 3    rules:
 4      # Pushgateway 宕机
 5      - alert: PushgatewayDown
 6        expr: up{job="pushgateway"} == 0
 7        for: 5m
 8      
 9      # 批处理任务长时间未推送
10      - alert: BatchJobStale
11        expr: time() - push_time_seconds{job="batch_job"} > 86400
12        annotations:
13          summary: "批处理任务超过 24 小时未运行"

总结

Pushgateway 适用于:

  • ✅ 批处理任务、Cron Jobs
  • ✅ 短期运行的脚本
  • ✅ 无法被 Prometheus 抓取的服务

关键要点:

  1. 使用自定义 Registry 避免推送不必要的指标
  2. 使用 Add() 保留已有指标,使用 Push() 覆盖
  3. 任务结束时使用 Delete() 清理指标
  4. 配置 Prometheus 时设置 honor_labels: true
  5. 添加错误处理和重试机制

不要用于:

  • ❌ 长期运行的服务(使用 Pull 模式)
  • ❌ 高频更新的指标
  • ❌ 需要服务发现的场景

完整代码可作为生产环境批处理任务监控的起点! 🎯



附录: Textfile Collector 示例

概述

Textfile Collector 是 Node Exporter 的一个功能,允许通过读取 .prom 格式的文本文件来暴露自定义指标。这对于以下场景非常有用:

  • 批处理任务生成指标
  • 脚本定期更新指标
  • 无法直接集成 Prometheus 客户端的程序

本示例展示如何使用 expfmt 包将指标写入 textfile 格式。


适用场景

✅ 适合使用 Textfile Collector:

  • Cron 定时任务生成指标
  • Shell/Python 脚本导出指标
  • 第三方工具输出转换为 Prometheus 格式
  • 静态指标(配额、阈值等)

✅ 相比 Pushgateway 的优势:

  • 不需要额外组件(复用 Node Exporter)
  • 自动生命周期管理(文件删除 = 指标消失)
  • 更简单的部署
  • 本地文件系统,无网络依赖

完整代码

  1package main
  2
  3import (
  4 "fmt"
  5 "log"
  6 "os"
  7 "time"
  8
  9 "github.com/prometheus/client_golang/prometheus"
 10 "github.com/prometheus/common/expfmt"
 11)
 12
 13// MetricCollector 指标收集器
 14type MetricCollector struct {
 15 registry *prometheus.Registry
 16 gauge    *prometheus.GaugeVec
 17 counter  *prometheus.CounterVec
 18}
 19
 20func NewMetricCollector() *MetricCollector {
 21 // 创建自定义 Registry
 22 registry := prometheus.NewRegistry()
 23
 24 // 创建 Gauge 指标
 25 gauge := prometheus.NewGaugeVec(
 26  prometheus.GaugeOpts{
 27   Name: "custom_metric_gauge",
 28   Help: "Custom gauge metric for textfile collector",
 29  },
 30  []string{"service", "region"},
 31 )
 32
 33 // 创建 Counter 指标
 34 counter := prometheus.NewCounterVec(
 35  prometheus.CounterOpts{
 36   Name: "custom_metric_counter_total",
 37   Help: "Custom counter metric for textfile collector",
 38  },
 39  []string{"service", "status"},
 40 )
 41
 42 // 注册指标
 43 registry.MustRegister(gauge)
 44 registry.MustRegister(counter)
 45
 46 return &MetricCollector{
 47  registry: registry,
 48  gauge:    gauge,
 49  counter:  counter,
 50 }
 51}
 52
 53// RecordMetrics 记录指标
 54func (mc *MetricCollector) RecordMetrics() {
 55 mc.gauge.With(prometheus.Labels{
 56  "service": "api",
 57  "region":  "us-east-1",
 58 }).Set(42.5)
 59
 60 mc.gauge.With(prometheus.Labels{
 61  "service": "database",
 62  "region":  "eu-west-1",
 63 }).Set(78.9)
 64
 65 mc.counter.With(prometheus.Labels{
 66  "service": "api",
 67  "status":  "success",
 68 }).Inc()
 69
 70 mc.counter.With(prometheus.Labels{
 71  "service": "api",
 72  "status":  "error",
 73 }).Add(3)
 74}
 75
 76// WriteToTextfile 将指标写入 textfile
 77func (mc *MetricCollector) WriteToTextfile(filename string) error {
 78 file, err := os.Create(filename)
 79 if err != nil {
 80  return fmt.Errorf("failed to create file: %w", err)
 81 }
 82 defer file.Close()
 83
 84 // 从 Registry 收集指标
 85 metricFamilies, err := mc.registry.Gather()
 86 if err != nil {
 87  return fmt.Errorf("failed to gather metrics: %w", err)
 88 }
 89
 90 // 使用 expfmt 将指标写入文件
 91 for _, mf := range metricFamilies {
 92  if _, err := expfmt.MetricFamilyToText(file, mf); err != nil {
 93   return fmt.Errorf("failed to write metric family: %w", err)
 94  }
 95 }
 96
 97 return nil
 98}
 99
100// WriteToTextfileAtomic 原子写入(推荐)
101func (mc *MetricCollector) WriteToTextfileAtomic(filename string) error {
102 // 创建临时文件
103 tmpFile := filename + ".tmp." + fmt.Sprintf("%d", time.Now().UnixNano())
104
105 file, err := os.Create(tmpFile)
106 if err != nil {
107  return fmt.Errorf("failed to create temp file: %w", err)
108 }
109
110 // 收集并写入指标
111 metricFamilies, err := mc.registry.Gather()
112 if err != nil {
113  file.Close()
114  os.Remove(tmpFile)
115  return fmt.Errorf("failed to gather metrics: %w", err)
116 }
117
118 for _, mf := range metricFamilies {
119  if _, err := expfmt.MetricFamilyToText(file, mf); err != nil {
120   file.Close()
121   os.Remove(tmpFile)
122   return fmt.Errorf("failed to write metric family: %w", err)
123  }
124 }
125
126 if err := file.Close(); err != nil {
127  os.Remove(tmpFile)
128  return fmt.Errorf("failed to close temp file: %w", err)
129 }
130
131 // 原子重命名
132 if err := os.Rename(tmpFile, filename); err != nil {
133  os.Remove(tmpFile)
134  return fmt.Errorf("failed to rename temp file: %w", err)
135 }
136
137 return nil
138}
139
140func main() {
141 collector := NewMetricCollector()
142 collector.RecordMetrics()
143
144 // 写入 textfile
145 outputFile := "/var/lib/node_exporter/textfile_collector/custom_metrics.prom"
146 if err := collector.WriteToTextfileAtomic(outputFile); err != nil {
147  log.Fatalf("Failed to write textfile: %v", err)
148 }
149
150 log.Printf("Metrics written to: %s", outputFile)
151}

使用说明

1. 安装 Node Exporter

二进制安装:

1wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
2tar xvfz node_exporter-1.7.0.linux-amd64.tar.gz
3cd node_exporter-1.7.0.linux-amd64

启动 Node Exporter (启用 textfile collector):

1# 创建 textfile 目录
2mkdir -p /var/lib/node_exporter/textfile_collector
3
4# 启动 Node Exporter
5./node_exporter --collector.textfile.directory=/var/lib/node_exporter/textfile_collector

2. 安装 Go 依赖

1go mod init textfile-demo
2go get github.com/prometheus/client_golang/prometheus
3go get github.com/prometheus/common/expfmt

3. 运行示例

1# 修改输出路径到 textfile 目录
2go run main.go

4. 生成的 .prom 文件内容

查看生成的文件:

1cat /var/lib/node_exporter/textfile_collector/custom_metrics.prom

输出示例:

1# HELP custom_metric_counter_total Custom counter metric for textfile collector
2# TYPE custom_metric_counter_total counter
3custom_metric_counter_total{service="api",status="error"} 3
4custom_metric_counter_total{service="api",status="success"} 1
5# HELP custom_metric_gauge Custom gauge metric for textfile collector
6# TYPE custom_metric_gauge gauge
7custom_metric_gauge{region="eu-west-1",service="database"} 78.9
8custom_metric_gauge{region="us-east-1",service="api"} 42.5

5. 访问指标

访问 Node Exporter: http://localhost:9100/metrics

搜索 custom_metric:

1custom_metric_counter_total{service="api",status="error"} 3
2custom_metric_counter_total{service="api",status="success"} 1
3custom_metric_gauge{region="eu-west-1",service="database"} 78.9
4custom_metric_gauge{region="us-east-1",service="api"} 42.5

核心 API 详解

1. registry.Gather()

从 Registry 收集所有指标:

 1metricFamilies, err := registry.Gather()
 2if err != nil {
 3    return err
 4}
 5
 6// metricFamilies 是 []*dto.MetricFamily 类型
 7// 每个 MetricFamily 包含:
 8// - Name: 指标名称
 9// - Help: 帮助信息
10// - Type: 指标类型 (COUNTER, GAUGE, HISTOGRAM, SUMMARY)
11// - Metric: 具体的指标值和标签

2. expfmt.MetricFamilyToText()

将 MetricFamily 写入文本格式:

1for _, mf := range metricFamilies {
2    // 写入 Prometheus text format
3    _, err := expfmt.MetricFamilyToText(file, mf)
4    if err != nil {
5        return err
6    }
7}

支持的格式:

1// Text format (默认,推荐)
2expfmt.MetricFamilyToText(writer, metricFamily)
3
4// OpenMetrics format
5encoder := expfmt.NewEncoder(writer, expfmt.FmtOpenMetrics)
6encoder.Encode(metricFamily)

实际应用场景

场景 1: 备份任务指标

 1// backup_monitor.go
 2package main
 3
 4import (
 5 "log"
 6 "os"
 7 "time"
 8
 9 "github.com/prometheus/client_golang/prometheus"
10 "github.com/prometheus/common/expfmt"
11)
12
13func recordBackupMetrics(backupSize int64, duration time.Duration, success bool) error {
14 registry := prometheus.NewRegistry()
15
16 // 备份大小
17 sizeGauge := prometheus.NewGauge(prometheus.GaugeOpts{
18  Name: "backup_size_bytes",
19  Help: "Size of the backup in bytes",
20 })
21 sizeGauge.Set(float64(backupSize))
22 registry.MustRegister(sizeGauge)
23
24 // 备份耗时
25 durationGauge := prometheus.NewGauge(prometheus.GaugeOpts{
26  Name: "backup_duration_seconds",
27  Help: "Duration of the backup in seconds",
28 })
29 durationGauge.Set(duration.Seconds())
30 registry.MustRegister(durationGauge)
31
32 // 最后备份时间
33 timestampGauge := prometheus.NewGauge(prometheus.GaugeOpts{
34  Name: "backup_last_success_timestamp",
35  Help: "Timestamp of the last successful backup",
36 })
37 if success {
38  timestampGauge.Set(float64(time.Now().Unix()))
39 }
40 registry.MustRegister(timestampGauge)
41
42 // 写入 textfile
43 return writeMetrics(registry, "/var/lib/node_exporter/textfile_collector/backup.prom")
44}
45
46func writeMetrics(registry *prometheus.Registry, filename string) error {
47 tmpFile := filename + ".tmp"
48 file, err := os.Create(tmpFile)
49 if err != nil {
50  return err
51 }
52 defer file.Close()
53
54 metricFamilies, err := registry.Gather()
55 if err != nil {
56  return err
57 }
58
59 for _, mf := range metricFamilies {
60  if _, err := expfmt.MetricFamilyToText(file, mf); err != nil {
61   return err
62  }
63 }
64
65 return os.Rename(tmpFile, filename)
66}
67
68func main() {
69 // 执行备份
70 backupSize := int64(1024 * 1024 * 500) // 500MB
71 duration := 120 * time.Second
72 success := true
73
74 if err := recordBackupMetrics(backupSize, duration, success); err != nil {
75  log.Fatalf("Failed to record metrics: %v", err)
76 }
77
78 log.Println("Backup metrics recorded successfully")
79}

Crontab:

10 2 * * * /usr/local/bin/backup.sh && /usr/local/bin/backup_monitor

场景 2: 磁盘配额监控

 1// disk_quota.go
 2package main
 3
 4import (
 5 "fmt"
 6 "os/exec"
 7 "strconv"
 8 "strings"
 9
10 "github.com/prometheus/client_golang/prometheus"
11 "github.com/prometheus/common/expfmt"
12)
13
14type QuotaInfo struct {
15 User      string
16 UsedBytes int64
17 LimitBytes int64
18}
19
20func getQuotaInfo() ([]QuotaInfo, error) {
21 // 执行 quota 命令
22 cmd := exec.Command("quota", "-v")
23 output, err := cmd.Output()
24 if err != nil {
25  return nil, err
26 }
27
28 // 解析输出 (示例)
29 quotas := []QuotaInfo{
30  {User: "alice", UsedBytes: 5368709120, LimitBytes: 10737418240},
31  {User: "bob", UsedBytes: 8589934592, LimitBytes: 10737418240},
32 }
33
34 return quotas, nil
35}
36
37func recordQuotaMetrics() error {
38 registry := prometheus.NewRegistry()
39
40 usedGauge := prometheus.NewGaugeVec(
41  prometheus.GaugeOpts{
42   Name: "disk_quota_used_bytes",
43   Help: "Disk quota used in bytes",
44  },
45  []string{"user"},
46 )
47
48 limitGauge := prometheus.NewGaugeVec(
49  prometheus.GaugeOpts{
50   Name: "disk_quota_limit_bytes",
51   Help: "Disk quota limit in bytes",
52  },
53  []string{"user"},
54 )
55
56 registry.MustRegister(usedGauge)
57 registry.MustRegister(limitGauge)
58
59 quotas, err := getQuotaInfo()
60 if err != nil {
61  return err
62 }
63
64 for _, q := range quotas {
65  usedGauge.WithLabelValues(q.User).Set(float64(q.UsedBytes))
66  limitGauge.WithLabelValues(q.User).Set(float64(q.LimitBytes))
67 }
68
69 return writeMetrics(registry, "/var/lib/node_exporter/textfile_collector/disk_quota.prom")
70}

Crontab:

1*/5 * * * * /usr/local/bin/disk_quota_monitor

场景 3: Shell 脚本生成指标

bash 版本:

 1#!/bin/bash
 2# generate_metrics.sh
 3
 4TEXTFILE_DIR="/var/lib/node_exporter/textfile_collector"
 5METRIC_FILE="$TEXTFILE_DIR/custom.prom"
 6TMP_FILE="$METRIC_FILE.$$"
 7
 8# 生成指标
 9cat > "$TMP_FILE" << EOF
10# HELP custom_database_connections Active database connections
11# TYPE custom_database_connections gauge
12custom_database_connections{database="primary"} $(mysql -e "SHOW STATUS LIKE 'Threads_connected'" | awk 'NR==2 {print $2}')
13custom_database_connections{database="replica"} $(mysql -h replica -e "SHOW STATUS LIKE 'Threads_connected'" | awk 'NR==2 {print $2}')
14
15# HELP custom_log_errors_total Total number of errors in logs
16# TYPE custom_log_errors_total counter
17custom_log_errors_total $(grep -c ERROR /var/log/app.log)
18EOF
19
20# 原子移动
21mv "$TMP_FILE" "$METRIC_FILE"

Python 版本:

 1#!/usr/bin/env python3
 2import time
 3from prometheus_client import CollectorRegistry, Gauge, write_to_textfile
 4
 5registry = CollectorRegistry()
 6
 7# 创建指标
 8connections = Gauge(
 9    'custom_database_connections',
10    'Active database connections',
11    ['database'],
12    registry=registry
13)
14
15# 设置值
16connections.labels(database='primary').set(42)
17connections.labels(database='replica').set(38)
18
19# 写入文件
20write_to_textfile(
21    '/var/lib/node_exporter/textfile_collector/custom.prom',
22    registry
23)

最佳实践

1. 使用原子写入

1// ✅ 好 - 原子写入,避免 Node Exporter 读取部分文件
2tmpFile := filename + ".tmp"
3os.Create(tmpFile)
4// ... 写入内容
5os.Rename(tmpFile, filename)
6
7// ❌ 不好 - 直接写入,可能读取到不完整数据
8os.Create(filename)
9// ... 写入内容

2. 添加文件修改时间指标

1// 添加文件生成时间,用于监控脚本是否正常运行
2timestampGauge := prometheus.NewGauge(prometheus.GaugeOpts{
3 Name: "textfile_scrape_timestamp",
4 Help: "Timestamp of the last textfile generation",
5})
6timestampGauge.Set(float64(time.Now().Unix()))
7registry.MustRegister(timestampGauge)

告警规则:

1- alert: TextfileStale
2  expr: time() - textfile_scrape_timestamp > 3600
3  annotations:
4    summary: "Textfile 超过 1 小时未更新"

3. 错误处理

 1func writeMetricsWithErrorHandling(registry *prometheus.Registry, filename string) error {
 2 tmpFile := filename + ".tmp"
 3
 4 // 创建临时文件
 5 file, err := os.Create(tmpFile)
 6 if err != nil {
 7  return fmt.Errorf("create temp file: %w", err)
 8 }
 9
10 // 收集指标
11 mfs, err := registry.Gather()
12 if err != nil {
13  file.Close()
14  os.Remove(tmpFile)
15  return fmt.Errorf("gather metrics: %w", err)
16 }
17
18 // 写入指标
19 for _, mf := range mfs {
20  if _, err := expfmt.MetricFamilyToText(file, mf); err != nil {
21   file.Close()
22   os.Remove(tmpFile)
23   return fmt.Errorf("write metric: %w", err)
24  }
25 }
26
27 // 关闭文件
28 if err := file.Close(); err != nil {
29  os.Remove(tmpFile)
30  return fmt.Errorf("close file: %w", err)
31 }
32
33 // 原子重命名
34 if err := os.Rename(tmpFile, filename); err != nil {
35  os.Remove(tmpFile)
36  return fmt.Errorf("rename file: %w", err)
37 }
38
39 return nil
40}

4. 文件权限

1# 确保 Node Exporter 用户可读
2chmod 644 /var/lib/node_exporter/textfile_collector/*.prom
3
4# 目录权限
5chmod 755 /var/lib/node_exporter/textfile_collector

Prometheus 配置

1scrape_configs:
2  - job_name: 'node'
3    static_configs:
4      - targets: ['localhost:9100']

PromQL 查询示例

 1# 查询 textfile 指标
 2custom_metric_gauge
 3
 4# 检查 textfile 是否过期
 5time() - textfile_scrape_timestamp > 3600
 6
 7# 备份大小趋势
 8backup_size_bytes
 9
10# 磁盘配额使用率
11disk_quota_used_bytes / disk_quota_limit_bytes > 0.9

监控 Textfile Collector

Node Exporter 暴露的相关指标:

1# textfile collector 读取错误
2node_textfile_scrape_error
3
4# 最后一次成功读取时间
5node_textfile_mtime_seconds
6
7# textfile 文件数量
8count(node_textfile_mtime_seconds)

告警规则:

 1groups:
 2  - name: textfile_collector
 3    rules:
 4      # Textfile 读取错误
 5      - alert: TextfileScrapeError
 6        expr: node_textfile_scrape_error == 1
 7        annotations:
 8          summary: "Textfile collector 读取错误"
 9
10      # Textfile 文件过期
11      - alert: TextfileStale
12        expr: time() - node_textfile_mtime_seconds > 3600
13        annotations:
14          summary: "Textfile 超过 1 小时未更新"

常见问题

Q1: 指标未出现在 Node Exporter?

A: 检查以下项:

 1# 1. 文件是否存在
 2ls -la /var/lib/node_exporter/textfile_collector/
 3
 4# 2. 文件权限
 5chmod 644 /var/lib/node_exporter/textfile_collector/*.prom
 6
 7# 3. 文件格式是否正确
 8cat /var/lib/node_exporter/textfile_collector/*.prom
 9
10# 4. 检查 Node Exporter 日志
11journalctl -u node_exporter -f
12
13# 5. 检查 scrape 错误
14curl http://localhost:9100/metrics | grep node_textfile_scrape_error

Q2: 格式错误导致指标无法读取?

A: 确保格式正确:

1# ✅ 正确格式
2# HELP metric_name Metric description
3# TYPE metric_name gauge
4metric_name{label="value"} 42
5
6# ❌ 错误格式 (缺少 HELP/TYPE)
7metric_name{label="value"} 42

Q3: 如何删除指标?

A: 删除对应的 .prom 文件:

1rm /var/lib/node_exporter/textfile_collector/custom.prom

Node Exporter 会在下次 scrape 时自动移除这些指标。


性能考虑

文件数量限制:

  • ✅ 建议每个脚本/任务一个文件
  • ✅ 总文件数 < 100
  • ❌ 避免过多小文件

文件大小:

  • ✅ 每个文件 < 10MB
  • ❌ 避免大量时间序列

更新频率:

  • ✅ 批处理任务: 按需更新
  • ✅ 定期任务: 1-5 分钟
  • ❌ 避免高频更新 (< 10s)

总结

Textfile Collector 适用于:

  • ✅ Cron/批处理任务生成指标
  • ✅ Shell/Python 脚本导出指标
  • ✅ 静态指标和配置信息
  • ✅ 本地文件系统可访问的场景

关键要点:

  1. 使用 registry.Gather() 收集指标
  2. 使用 expfmt.MetricFamilyToText() 写入文件
  3. 使用临时文件 + 原子重命名
  4. 添加时间戳指标监控脚本运行
  5. 正确的文件权限 (644)

对比 Pushgateway:

特性Textfile CollectorPushgateway
部署复杂度低 (复用 Node Exporter)中 (需要额外组件)
网络依赖无 (本地文件)有 (HTTP 推送)
生命周期自动 (删除文件)手动 (需调用 Delete)
适用场景本地定时任务远程批处理任务

完整代码可作为批处理任务指标导出的最佳实践!🎯



附录: Python & Shell 完整示例

本章节提供 Python 和 Shell 脚本的完整 Prometheus 集成示例,涵盖三种主要模式。


Python HTTP Exporter

概述

使用 prometheus_client 库创建 HTTP 端点暴露指标,适合长期运行的服务。

完整代码

 1#!/usr/bin/env python3
 2import time
 3import psutil
 4from prometheus_client import start_http_server, Gauge, Counter, Histogram, Info, REGISTRY
 5
 6# 定义指标
 7cpu_usage = Gauge('system_cpu_usage_percent', 'CPU usage', ['cpu'], registry=REGISTRY)
 8memory_usage = Gauge('system_memory_usage_bytes', 'Memory usage', ['type'], registry=REGISTRY)
 9request_count = Counter('http_requests_total', 'HTTP requests', ['method', 'endpoint', 'status'], registry=REGISTRY)
10request_duration = Histogram('http_request_duration_seconds', 'Request latency', ['endpoint'], buckets=[0.01, 0.05, 0.1, 0.5, 1, 2, 5], registry=REGISTRY)
11app_info = Info('application', 'Application info', registry=REGISTRY)
12
13def update_system_metrics():
14    cpu_percent = psutil.cpu_percent(interval=1, percpu=True)
15    for i, percent in enumerate(cpu_percent):
16        cpu_usage.labels(cpu=f'cpu{i}').set(percent)
17    
18    mem = psutil.virtual_memory()
19    memory_usage.labels(type='total').set(mem.total)
20    memory_usage.labels(type='used').set(mem.used)
21
22def main():
23    app_info.info({'version': '1.0.0', 'env': 'production'})
24    port = 8000
25    start_http_server(port)
26    print(f"Metrics: http://localhost:{port}/metrics")
27    
28    try:
29        while True:
30            update_system_metrics()
31            time.sleep(5)
32    except KeyboardInterrupt:
33        print("Shutting down...")
34
35if __name__ == '__main__':
36    main()

安装和运行

1# 安装依赖
2pip install prometheus-client psutil
3
4# 运行
5python3 python_http_exporter.py
6
7# 查看指标
8curl http://localhost:8000/metrics

Python Pushgateway

完整代码

 1#!/usr/bin/env python3
 2import time
 3import random
 4import socket
 5from prometheus_client import CollectorRegistry, Gauge, Counter
 6from prometheus_client import push_to_gateway, delete_from_gateway
 7
 8registry = CollectorRegistry()
 9
10batch_size = Gauge('batch_processing_size', 'Batch size', ['batch_id'], registry=registry)
11batch_processed = Counter('batch_items_processed_total', 'Items processed', ['batch_id', 'status'], registry=registry)
12
13def process_batch(batch_id, item_count):
14    print(f"Processing batch: {batch_id}")
15    batch_size.labels(batch_id=batch_id).set(item_count)
16    
17    for i in range(item_count):
18        time.sleep(random.uniform(0.001, 0.01))
19        if random.random() < 0.9:
20            batch_processed.labels(batch_id=batch_id, status='success').inc()
21        else:
22            batch_processed.labels(batch_id=batch_id, status='failed').inc()
23
24def main():
25    pushgateway_url = 'localhost:9091'
26    job_name = 'batch_job'
27    grouping_key = {'instance': socket.gethostname()}
28    
29    batch_id = f"batch_{int(time.time())}"
30    process_batch(batch_id, 100)
31    
32    push_to_gateway(pushgateway_url, job=job_name, registry=registry, grouping_key=grouping_key)
33    print(f"Metrics pushed to {pushgateway_url}")
34    
35    time.sleep(30)
36    delete_from_gateway(pushgateway_url, job=job_name, grouping_key=grouping_key)
37
38if __name__ == '__main__':
39    main()

运行

1# 启动 Pushgateway
2docker run -d -p 9091:9091 prom/pushgateway
3
4# 运行脚本
5python3 python_pushgateway.py

Python Textfile Collector

完整代码

 1#!/usr/bin/env python3
 2import os
 3import time
 4import psutil
 5from prometheus_client import CollectorRegistry, Gauge
 6from prometheus_client import write_to_textfile
 7
 8TEXTFILE_DIR = '/var/lib/node_exporter/textfile_collector'
 9METRIC_FILE = os.path.join(TEXTFILE_DIR, 'custom_metrics.prom')
10
11if not os.path.exists(TEXTFILE_DIR):
12    TEXTFILE_DIR = '/tmp/textfile_collector'
13    METRIC_FILE = os.path.join(TEXTFILE_DIR, 'custom_metrics.prom')
14    os.makedirs(TEXTFILE_DIR, exist_ok=True)
15
16registry = CollectorRegistry()
17
18disk_usage = Gauge('custom_disk_usage_percent', 'Disk usage', ['mountpoint'], registry=registry)
19scrape_timestamp = Gauge('custom_textfile_scrape_timestamp', 'Last scrape time', registry=registry)
20
21def collect_metrics():
22    for partition in psutil.disk_partitions():
23        try:
24            usage = psutil.disk_usage(partition.mountpoint)
25            disk_usage.labels(mountpoint=partition.mountpoint).set(usage.percent)
26        except PermissionError:
27            continue
28    
29    scrape_timestamp.set(time.time())
30
31def main():
32    print("Collecting metrics...")
33    collect_metrics()
34    
35    tmp_file = f"{METRIC_FILE}.tmp"
36    write_to_textfile(tmp_file, registry)
37    os.rename(tmp_file, METRIC_FILE)
38    
39    print(f"Metrics written to: {METRIC_FILE}")
40
41if __name__ == '__main__':
42    main()

Crontab 配置

1*/5 * * * * /usr/bin/python3 /path/to/python_textfile_collector.py

Shell Textfile Collector

完整代码

 1#!/bin/bash
 2set -e
 3
 4TEXTFILE_DIR="/var/lib/node_exporter/textfile_collector"
 5METRIC_FILE="$TEXTFILE_DIR/shell_metrics.prom"
 6
 7if [ ! -d "$TEXTFILE_DIR" ]; then
 8    TEXTFILE_DIR="/tmp/textfile_collector"
 9    METRIC_FILE="$TEXTFILE_DIR/shell_metrics.prom"
10    mkdir -p "$TEXTFILE_DIR"
11fi
12
13TMP_FILE="$METRIC_FILE.$$"
14
15: > "$TMP_FILE"
16
17# 系统负载
18LOAD_1=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | tr -d ' ')
19cat >> "$TMP_FILE" << EOF
20# HELP shell_system_load System load average
21# TYPE shell_system_load gauge
22shell_system_load{period="1m"} $LOAD_1
23
24EOF
25
26# 磁盘使用
27cat >> "$TMP_FILE" << 'EOF'
28# HELP shell_disk_usage_percent Disk usage percentage
29# TYPE shell_disk_usage_percent gauge
30EOF
31
32df -h | awk 'NR>1 && $1 ~ /^\// {
33    gsub(/%/, "", $5)
34    printf "shell_disk_usage_percent{mountpoint=\"%s\"} %s\n", $6, $5
35}' >> "$TMP_FILE"
36
37echo "" >> "$TMP_FILE"
38
39# 内存使用
40MEMORY_TOTAL=$(free -b | awk '/^Mem:/ {print $2}')
41cat >> "$TMP_FILE" << EOF
42# HELP shell_memory_bytes Memory usage
43# TYPE shell_memory_bytes gauge
44shell_memory_bytes{type="total"} $MEMORY_TOTAL
45
46EOF
47
48# 时间戳
49cat >> "$TMP_FILE" << EOF
50# HELP shell_textfile_scrape_timestamp Timestamp
51# TYPE shell_textfile_scrape_timestamp gauge
52shell_textfile_scrape_timestamp $(date +%s)
53EOF
54
55mv "$TMP_FILE" "$METRIC_FILE"
56chmod 644 "$METRIC_FILE"
57
58echo "Metrics written to: $METRIC_FILE"

使用方法

1chmod +x shell_textfile_collector.sh
2./shell_textfile_collector.sh
3
4# Crontab
5*/5 * * * * /path/to/shell_textfile_collector.sh

对比总结

语言选择

特性PythonGoShell
性能
部署需要 Python 环境单一二进制无依赖
开发效率
适用场景数据处理、脚本生产服务系统监控

模式选择

模式优势劣势场景
HTTP Exporter实时更新需要暴露端口长期服务
Pushgateway适合批处理需要额外组件批处理任务
Textfile简单、无依赖定期更新定时任务

最佳实践

HTTP Exporter

1# 使用自定义 Registry
2registry = CollectorRegistry()
3
4# 合理标签基数
5gauge = Gauge('metric', 'desc', ['service'])  # ✅ 低基数
6gauge = Gauge('metric', 'desc', ['user_id'])  # ❌ 高基数

Pushgateway

1# 任务结束时删除指标
2try:
3    push_to_gateway(url, job='batch', registry=registry)
4finally:
5    delete_from_gateway(url, job='batch')

Textfile Collector

1# 原子写入
2tmp_file = f"{metric_file}.tmp"
3write_to_textfile(tmp_file, registry)
4os.rename(tmp_file, metric_file)

Prometheus 配置

 1scrape_configs:
 2  - job_name: 'python_exporter'
 3    static_configs:
 4      - targets: ['localhost:8000']
 5  
 6  - job_name: 'pushgateway'
 7    honor_labels: true
 8    static_configs:
 9      - targets: ['localhost:9091']
10  
11  - job_name: 'node'
12    static_configs:
13      - targets: ['localhost:9100']

总结

本章节提供了完整的示例代码:

  • 3 种语言: Go、Python、Shell
  • 3 种模式: HTTP、Pushgateway、Textfile
  • 4 种指标: Counter、Gauge、Histogram、Info
  • 最佳实践: 原子写入、错误处理、标签使用

所有代码均可直接用于生产环境!🎯


最新文章

本页导航

文档导航