快捷菜单
常用功能一站直达
更多功能请点顶栏「快捷菜单」
基于Prometheus官网文档,结合Prometheus 3.5 & Alertmanager 0.27整理而成。
Prometheus 是一个开源的系统监控和告警工具包,最初由 SoundCloud 构建。自 2012 年诞生以来,许多公司和组织都采用了 Prometheus,该项目拥有非常活跃的开发者和用户社区。2016 年,Prometheus 作为第二个托管项目(继 Kubernetes 之后)加入了云原生计算基金会(CNCF)。
核心特性:
多维数据模型
灵活的查询语言 PromQL
不依赖分布式存储
基于 HTTP 的 Pull 模型
支持推送时间序列
服务发现
多种图形和仪表板支持
指标是数值测量的通俗术语。时间序列是指随时间变化的记录。
应用场景示例:
Prometheus 生态系统由多个组件组成,其中许多是可选的:
| 组件 | 说明 |
|---|---|
| Prometheus Server | 抓取和存储时间序列数据的主服务器 |
| Client Libraries | 用于为应用程序代码添加监控埋点的客户端库 |
| Push Gateway | 支持短生命周期任务的推送网关 |
| Exporters | 用于 HAProxy、StatsD、Graphite 等服务的专用导出器 |
| Alertmanager | 处理告警的告警管理器 |
| Support Tools | 各种支持工具 |
大多数 Prometheus 组件使用 Go 语言编写,易于构建和部署为静态二进制文件。
工作流程:
✅ 适合使用 Prometheus 的场景:
❌ 不适合使用 Prometheus 的场景:
本节将指导你完成 Prometheus 的安装、配置和监控第一个资源的过程。
1tar xvfz prometheus-*.tar.gz
2cd prometheus-*prometheus 的单一二进制文件(Windows 上为 prometheus.exe)1./prometheus --helpPrometheus 使用 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']配置文件三大块:
| 配置块 | 说明 |
|---|---|
| global | Prometheus 服务器的全局配置 |
| rule_files | 指定 Prometheus 服务器要加载的规则文件位置 |
| scrape_configs | 控制 Prometheus 监控哪些资源 |
配置详解:
global 块
scrape_interval: 控制 Prometheus 抓取目标的频率(默认 15 秒)evaluation_interval: 控制 Prometheus 评估规则的频率rule_files 块
scrape_configs 块
prometheus 的任务,抓取 Prometheus 服务器自身暴露的时间序列数据http://localhost:9090/metrics使用配置文件启动 Prometheus:
1./prometheus --config.file=prometheus.yml启动后,可以访问以下地址:
http://localhost:9090http://localhost:9090/metricshttp://localhost:9090/graph查询示例:
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 的功能,推荐探索其他导出器的文档,例如:
范围(Scope)
数据模型(Data Model)
| 特性 | Graphite | Prometheus |
|---|---|---|
| 指标命名 | 点分隔的组件(隐式编码维度) | 显式的键值对标签 |
| 示例 | stats.api-server.tracks.post.500 -> 93 | api_server_http_requests_total{method="POST",handler="/tracks",status="500",instance="sample1"} -> 34 |
| 维度支持 | 隐式 | 显式,易于过滤、分组和匹配 |
存储(Storage)
总结
范围(Scope)
数据模型/存储
| 特性 | InfluxDB | Prometheus |
|---|---|---|
| 标签 | Tags(第一级)+ Fields(第二级) | Labels |
| 时间戳精度 | 纳秒级 | 毫秒级 |
| 数据类型 | float64, int64, bool, string | float64(有限的字符串支持) |
| 存储方式 | LSM 树变体 + WAL,按时间分片 | 每个时间序列一个仅追加文件 |
架构(Architecture)
优劣对比
InfluxDB 更优:
Prometheus 更优:
范围(Scope)
数据模型(Data Model)
存储(Storage)
总结
范围(Scope)
数据模型(Data Model)
存储(Storage)
架构(Architecture)
总结
范围(Scope)
数据模型(Data Model)
存储(Storage)
架构(Architecture)
优劣对比
Sensu 更优:
Prometheus 更优:
通过以上内容,我们了解了:
接下来将深入学习 Prometheus 的核心概念、服务器配置、查询语言和最佳实践。
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 支持。建议使用推荐的字符集以获得最佳兼容性。
样本构成实际的时间序列数据。每个样本包括:
| 组成部分 | 说明 |
|---|---|
| 值 | float64 或原生直方图值 |
| 时间戳 | 毫秒精度的时间戳 |
给定指标名称和一组标签,时间序列通常使用以下表示法标识:
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 使用的表示法相同。
Prometheus 客户端库提供四种核心指标类型。这些类型目前仅在客户端库中区分(以支持特定类型的使用 API)和在传输协议中区分。Prometheus 服务器尚未使用类型信息,而是将所有数据展平为无类型时间序列。
Counter 是一个累积指标,表示单个单调递增的计数器,其值只能增加或在重启时重置为零。
适用场景:
❌ 不要使用 Counter:
示例:
1# HTTP 请求总数
2http_requests_total{method="POST", endpoint="/api/users"}Gauge 是表示单个数值的指标,该值可以任意上下波动。
适用场景:
示例:
1# 当前内存使用量(字节)
2memory_usage_bytes{instance="localhost:9090"}
3
4# 当前并发请求数
5http_concurrent_requests{endpoint="/api"}Histogram 对观测值(通常是请求持续时间或响应大小等)进行采样,并将其计入可配置的桶中。它还提供所有观测值的总和。
暴露的时间序列:
对于基础指标名称为 <basename> 的直方图,会在抓取时暴露多个时间序列:
| 时间序列 | 说明 |
|---|---|
<basename>_bucket{le="<上限>"}" | 观测桶的累积计数器 |
<basename>_sum | 所有观测值的总和 |
<basename>_count | 已观测到的事件计数(等同于 <basename>_bucket{le="+Inf"}) |
使用场景:
histogram_quantile() 函数计算直方图的分位数示例:
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 与 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 对比:
| 特性 | Histogram | Summary |
|---|---|---|
| 分位数计算 | 服务器端(PromQL) | 客户端(应用端) |
| 聚合能力 | 可聚合多个实例 | 无法聚合 |
| 精度 | 桶粒度限制 | 精确 |
| 性能开销 | 低 | 较高 |
| 适用场景 | 需要聚合、预先不知道分位数 | 已知所需分位数、单实例观测 |
在 Prometheus 术语中:
示例:一个有 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架构关系图:
当 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 的核心概念:
这些概念是理解和使用 Prometheus 的基础,为后续学习服务器配置、查询语言和最佳实践奠定了坚实基础。
本指南是一个 “Hello World” 风格的教程,展示如何安装、配置和使用一个简单的 Prometheus 实例。
1tar xvfz prometheus-*.tar.gz
2cd 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 | 静态配置的目标列表 |
1# 启动 Prometheus
2# 默认情况下,Prometheus 将数据库存储在 ./data (标志 --storage.tsdb.path)
3./prometheus --config.file=prometheus.yml启动后访问:
http://localhost:9090http://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/metricshttp://localhost:8081/metricshttp://localhost:8082/metrics将所有三个端点分组到一个名为 node 的任务中。我们将前两个端点标记为生产目标,第三个表示金丝雀实例。
在 prometheus.yml 的 scrape_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-CPrometheus 提供多种安装方式,适合不同的使用场景。
优点: 简单快速,适合快速测试和开发环境
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优点: 容器化部署,易于管理和扩展
所有 Prometheus 服务都提供 Docker 镜像,可从 Quay.io 或 Docker Hub 获取。
基本运行:
1# 使用示例配置启动 Prometheus
2docker run -p 9090:9090 prom/prometheus这将使用示例配置启动 Prometheus 并在端口 9090 上暴露。
重要提示:
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/prometheus3. 保存 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.yml 和 Dockerfile:
1FROM prom/prometheus
2ADD prometheus.yml /etc/prometheus/构建并运行:
1docker build -t my-prometheus .
2docker run -p 9090:9090 my-prometheus高级选项:
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 |
通过本章节,我们学习了:
Getting Started:
Installation:
这些内容为后续深入学习 Prometheus 的配置、查询和告警功能打下了坚实基础。
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💡 如果新配置格式不正确,更改不会被应用。重新加载也会重新加载规则文件。
| 占位符 | 说明 | 示例 |
|---|---|---|
<boolean> | 布尔值 | true 或 false |
<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> | 协议方案 | http 或 https |
<secret> | 机密字符串(如密码) | password123 |
<string> | 常规字符串 | 任意字符串 |
<size> | 字节大小(需要单位) | 512MB, 1GB |
全局配置在所有其他配置上下文中有效,并作为其他配置节的默认值。
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 |
1runtime:
2 # 配置 Go 垃圾回收器 GOGC 参数
3 # 降低此数字会增加 CPU 使用率
4 gogc: 75 # 默认 75抓取配置指定一组目标及其抓取参数。一般情况下,一个抓取配置指定一个任务。
基本结构:
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 配置允许配置 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: httpsHTTP 认证方式对比:
| 认证方式 | 适用场景 | 示例 |
|---|---|---|
| Basic Auth | 简单的用户名/密码认证 | 内部服务、开发环境 |
| Bearer Token | API Token 认证 | Kubernetes, 云服务 |
| OAuth 2.0 | 复杂的授权流程 | 需要刷新 token 的场景 |
| TLS Client Cert | mTLS 双向认证 | 高安全要求的服务 |
Prometheus 支持 25+ 种服务发现机制,可以自动发现监控目标。
支持的服务发现类型:
常用服务发现配置示例:
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: service3. 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: 30s4. 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_name5. 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: 30stargets.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]重新标记是一个强大的工具,可以在抓取之前动态重写目标的标签集。
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 | 计算哈希值用于分片 |
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 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 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 配置的核心内容:
这些配置选项为 Prometheus 提供了极大的灵活性,可以适应各种监控场景和基础设施环境。
记录规则允许你预计算频繁使用或计算成本高的表达式,并将结果保存为新的时间序列。查询预计算的结果通常比每次执行原始表达式快得多,特别适用于需要重复查询相同表达式的仪表板。
Prometheus 支持两种类型的规则:
规则文件格式: 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.yml1groups:
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) 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
job, instance, cluster)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)告警规则允许你基于 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 }})"状态说明:
| 状态 | 说明 |
|---|---|
| Inactive | 告警表达式结果为假,告警未激活 |
| Pending | 表达式为真,但未达到 for 持续时间 |
| Firing | 表达式为真且持续时间超过 for 阈值 |
| Keep Firing | 表达式已变为假,但在 keep_firing_for 期间内继续触发 |
告警的 labels 和 annotations 支持使用 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: emergency4. 提供详细的上下文信息
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"}Prometheus 负责告警检测,Alertmanager 负责告警通知、分组、抑制和静默。
在 prometheus.yml 中配置:
1alerting:
2 alertmanagers:
3 - static_configs:
4 - targets:
5 - 'alertmanager1:9093'
6 - 'alertmanager2:9093'
7 timeout: 10s告警流程:
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 格式化数字动态查询参数:
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 }}B | 1MiB |
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.yml2. 在告警注解中输出调试信息:
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 (记录规则):
level:metric:operations)Alerting Rules (告警规则):
for 和 keep_firing_for 控制告警行为Template Examples (模板示例):
这些功能组合使用,能够构建强大的监控和告警系统,满足复杂的生产环境需求。
Prometheus 提供了一种功能强大的查询语言 PromQL (Prometheus Query Language),允许用户实时选择和聚合时间序列数据。
1. Instant Query (即时查询)
2. Range Query (范围查询)
PromQL 表达式可以评估为以下四种类型之一:
| 数据类型 | 说明 | 示例用途 |
|---|---|---|
| Instant Vector``(即时向量) | 包含单个时间戳的一组时间序列,每个序列一个样本 | 当前 CPU 使用率 |
| Range Vector``(范围向量) | 包含一段时间内数据点的一组时间序列 | 过去5分钟的请求数据 |
| Scalar``(标量) | 简单的浮点数值 | 阈值、常数 |
| String``(字符串) | 简单的字符串值(当前未使用) | - |
查询类型限制:
本部分详细介绍 PromQL 查询语言的基础知识、操作符、函数和实用示例。由于内容较多,以下是简明版本,完整文档请参考官方文档。
核心概念:
数据类型:
选择器语法:
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标签匹配运算符:
= : 等于!= : 不等于=~ : 正则匹配!~ : 正则不匹配算术操作符: + - * / % ^
比较操作符: == != > < >= <=
逻辑/集合操作符:
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, counttopk, bottomk, quantilestddev, stdvar, groupcount_values1# 按标签聚合
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)速率函数:
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() # 时间组件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 * 1004. 计算延迟分位数
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) / 864008. 统计计数
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* 10010. 告警相关查询
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.81. 性能优化
2. 准确性
rate() 或 increase()delta() 或直接查询rate() 再聚合3. 可读性
4. 告警查询
rate() 而非 irate() (更稳定)for 持续时间避免抖动absent() 检测缺失指标Prometheus 提供了完整的 HTTP API 用于查询数据和管理服务器。当前稳定版本API位于 /api/v1 端点。
所有 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 - 查询超时或中止即时查询 (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'查询时间序列 (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'查询抓取目标
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'查询配置
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# 返回时间序列数据库的基数统计创建快照
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# 清理已删除的数据,释放磁盘空间格式化查询
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格式)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'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')"
7fiPython 示例:
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/v1/query | GET/POST | 即时查询 |
/api/v1/query_range | GET/POST | 范围查询 |
/api/v1/series | GET/POST | 查询时间序列 |
/api/v1/labels | GET/POST | 查询标签名称 |
/api/v1/label/<name>/values | GET | 查询标签值 |
/api/v1/targets | GET | 查询抓取目标 |
/api/v1/rules | GET | 查询规则 |
/api/v1/alerts | GET | 查询告警 |
/api/v1/status/config | GET | 查询配置 |
/api/v1/status/flags | GET | 查询标志 |
/api/v1/status/runtimeinfo | GET | 查询运行时信息 |
/api/v1/status/buildinfo | GET | 查询构建信息 |
/api/v1/status/tsdb | GET | 查询 TSDB 统计 |
/api/v1/admin/tsdb/snapshot | POST | 创建快照 |
/api/v1/admin/tsdb/delete_series | POST | 删除序列 |
/api/v1/admin/tsdb/clean_tombstones | POST | 清理墓碑 |
Prometheus 包含本地磁盘时间序列数据库,同时可选地与远程存储系统集成。
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 压缩:
1--storage.tsdb.wal-compression
2# 可将 WAL 大小减半,CPU 开销很小
3# v2.11.0 引入,v2.20.0 默认启用| 参数 | 说明 | 默认值 |
|---|---|---|
--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--storage.tsdb.retention.time=30d大小保留:
1--storage.tsdb.retention.size=50GB建议:
文件系统要求:
备份建议:
数据恢复:
如果本地存储损坏:
Prometheus 通过以下方式与远程存储系统集成:
编码方式:
版本:
配置位置: prometheus.yml 的 remote_write 和 remote_read 部分
启用方式:
1prometheus --web.enable-remote-write-receiver端点: /api/v1/write
⚠️ 注意: 这不是高效的样本采集方式,仅用于特定低流量场景,不适合替代抓取机制
用途: 从其他监控系统或时间序列数据库迁移数据
使用方法:
1# 从 OpenMetrics 格式创建 blocks
2promtool tsdb create-blocks-from openmetrics <input_file> [<output_dir>]
3
4# 移动生成的 blocks 到 Prometheus 数据目录
5mv <output_dir>/* /path/to/prometheus/data/注意事项:
--storage.tsdb.allow-overlapping-blocks (v2.38 及以下)调整 Block 大小:
1# 使用更大的 block 持续时间(适合回填大量历史数据)
2promtool tsdb create-blocks-from openmetrics input.txt \
3 --max-block-duration=7d⚠️ 注意: 大 block 会影响基于时间的保留策略效果
用途: 为新创建的记录规则生成历史数据
使用方法:
1promtool tsdb create-blocks-from rules \
2 --start 1617079873 \
3 --end 1617097873 \
4 --url http://prometheus:9090 \
5 rules.yaml rules2.yaml限制:
Federation 允许一个 Prometheus 服务器从另一个 Prometheus 服务器抓取选定的时间序列。
1. 分层联邦 (Hierarchical Federation)
用于扩展到拥有数十个数据中心和数百万节点的环境。
架构特点:
2. 跨服务联邦 (Cross-Service Federation)
一个服务的 Prometheus 从另一个服务的 Prometheus 抓取数据,实现跨服务查询和告警。
示例场景:
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 仍能正确采集
Prometheus 提供了一组管理 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 重放完成等)重新加载配置
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 prometheus1. 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: 32. 自动配置更新脚本
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
24fi3. 监控脚本
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/-/readyStorage (存储):
Federation (联邦):
/federate 端点和 match[] 参数Management API (管理API):
/-/healthy - 健康检查(存活探针)/-/ready - 就绪检查(就绪探针)/-/reload - 重新加载配置/-/quit - 优雅关闭--web.enable-lifecyclePrometheus 监控服务器启动命令。
1prometheus [flags]配置文件:
1# 指定配置文件
2--config.file=prometheus.yml # 默认: prometheus.yml
3
4# 配置自动重载间隔
5--config.auto-reload-interval=30s # 默认: 30sWeb 服务器:
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-lockfileAgent 模式存储:
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=1mAlertmanager:
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, json1# 以 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=jsonAgent 模式:
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-histogramsPrometheus 监控系统的工具箱,用于验证、测试、查询和调试。
1promtool [flags] <command> [args...]| 命令 | 说明 |
|---|---|
check | 检查资源有效性 |
query | 对 Prometheus 服务器执行查询 |
test | 单元测试 |
tsdb | TSDB 相关命令 |
debug | 获取调试信息 |
push | 推送数据到 Prometheus |
promql | PromQL 格式化和编辑(实验性) |
检查配置文件:
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即时查询:
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.*"}'测试规则:
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"列出 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获取性能分析数据:
1promtool debug pprof http://localhost:9090获取指标:
1promtool debug metrics http://localhost:9090获取所有调试信息:
1promtool debug all http://localhost:9090 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需要 --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"}自动化配置验证:
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 "---"
15done1. 配置验证
promtool check config--lint 检查常见问题2. 规则测试
promtool test rules--debug 排查测试问题3. TSDB 维护
tsdb analyze 检查基数问题tsdb list 监控 block 大小tsdb dump 导出数据4. 查询优化
query analyze 分析查询模式promtool query 验证--format 参数获取不同格式的输出5. 安全性
--http.config.file 配置 TLS 客户端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/21. 正则表达式匹配换行符
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-functionsPrometheus 3.0 对 Content-Type 头更严格:
v2.x 行为:
Content-Type → 默认为文本格式v3.0 行为:
Content-Type → 抓取失败1scrape_configs:
2 - job_name: 'legacy_app'
3 fallback_scrape_protocol: PrometheusText0.0.41. TSDB 格式和降级
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: legacy3. 日志格式变更
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. le 和 quantile 标签归一化
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 # ✅使用 --enable-feature 启用实验性或破坏性变更的特性。
1prometheus --enable-feature=feature1,feature21. exemplar-storage - 示例存储
1--enable-feature=exemplar-storage2. native-histograms - 原生直方图
1--enable-feature=native-histograms配置示例:
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.94. memory-snapshot-on-shutdown - 关闭时内存快照
1--enable-feature=memory-snapshot-on-shutdown5. 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=30s7. otlp-deltatocumulative - OTLP Delta 转换
1--enable-feature=otlp-deltatocumulative8. 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 原生 Deltametadata-wal-records - 元数据 WAL 记录use-uncached-io - 非缓存 I/O (Linux)⚠️ 重要: Prometheus HTTP 端点不应暴露到公网,包括:
/metrics端点- API 端点 (
/api/v1/*)/pprof调试端点
Prometheus:
目标:
honor_labels: true 会移除此保护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:
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 验证CSRF 防护:
XSS 防护:
Access-Control-Allow-OriginPromQL 注入:
1# ❌ 危险 - 用户输入未转义
2up{job="<user_input>"}
3
4# 如果 user_input = "} or some_metric{zzz="
5# 结果: up{job=""} or some_metric{zzz=""}Grafana 注意事项:
秘密字段:
外部秘密:
AWS_SECRET_KEY)可能被泄露缓解措施:
用户责任:
公开漏洞:
未公开漏洞:
prometheus-team@googlegroups.com扫描工具用户注意:
govulncheck)验证Prometheus 是开源的系统监控和告警工具包,最初在 SoundCloud 构建。2016 年加入 CNCF,成为继 Kubernetes 之后的第二个托管项目。
核心特点:
指标: 数值测量
时间序列: 随时间变化的记录
示例:
作用: 理解应用行为,诊断问题,指导扩容
核心组件:
语言: 大多数组件用 Go 编写,易于部署
Prometheus 从已埋点的作业抓取指标:
存储样本并运行规则:
Grafana 或其他 API 消费者可视化数据。
适合:
可靠性设计:
不适合:
Alertmanager 处理客户端(如 Prometheus)发送的告警,负责:
目的: 将相似告警归类为单个通知
场景:
数百个服务实例 → 网络分区 → 一半实例无法访问数据库
无分组: 发送数百条告警
有分组: 发送一条告警,列出所有受影响实例
配置:
1route:
2 group_by: ['cluster', 'alertname']
3 group_wait: 10s
4 group_interval: 10s
5 repeat_interval: 1h
6 receiver: 'team-ops'目的: 某些告警触发时,抑制其他相关告警
场景:
整个集群不可达 → 数百个实例告警
抑制规则: 集群不可达告警触发时,抑制该集群的所有其他告警
配置:
1inhibit_rules:
2 - source_match:
3 severity: 'critical'
4 alertname: 'ClusterDown'
5 target_match:
6 severity: 'warning'
7 equal: ['cluster']目的: 在给定时间内静默告警
配置方式: 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:9094Prometheus 配置:
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'支持的接收器:
Webhook 示例:
1receivers:
2 - name: 'webhook'
3 webhook_configs:
4 - url: 'http://example.com/webhook'
5 send_resolved: true1. 合理分组
2. 使用抑制
3. 静默管理
4. 高可用部署
5. 测试通知
amtool 测试配置Alertmanager 通过命令行标志和配置文件进行配置。
启动命令:
1alertmanager --config.file=alertmanager.yml重载配置:
1# 发送 SIGHUP 信号
2kill -HUP <pid>
3
4# 或使用 HTTP 端点
5curl -X POST http://localhost:9093/-/reload 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路由定义告警如何分组和发送到接收器。
核心参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
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']定义静默或激活路由的时间范围。
基本示例:
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)当某个告警触发时,抑制其他相关告警。
基本示例:
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 - 必须相等的标签列表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 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" . }}'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: true1receivers:
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 }}' 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: 1hTLS 配置:
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: falseBasic 认证:
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' 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']常用变量:
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 数量验证配置:
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 query1. 合理的分组策略
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 - Slackpagerduty_configs - PagerDutywebhook_configs - Webhookopsgenie_configs - OpsGenietelegram_configs - Telegramwechat_configs - 微信pushover_configs - Pushoverdiscord_configs - Discordmsteams_configs - Microsoft Teamsvictorops_configs - VictorOpssns_configs - AWS SNSwebex_configs - Webex以下是使用 Go 模板系统的 Alertmanager 通知配置示例。
添加 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 }}'效果: 根据告警的 app 和 alertname 标签,自动生成对应的 Wiki 文档链接。
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 提及 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 }}效果: 遍历所有接收到的告警,每个告警的摘要和描述各占一行。
步骤 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" . }}'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常用函数:
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" }}/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 }}Alertmanager 提供管理 API 用于自动化和集成。
端点:
1GET /-/healthy
2HEAD /-/healthy用途: 检查 Alertmanager 是否运行
返回: 始终返回 200
示例:
1# 检查健康状态
2curl http://localhost:9093/-/healthy
3
4# 仅检查状态码
5curl -I http://localhost:9093/-/healthyKubernetes 存活探针:
1livenessProbe:
2 httpGet:
3 path: /-/healthy
4 port: 9093
5 initialDelaySeconds: 30
6 periodSeconds: 10端点:
1GET /-/ready
2HEAD /-/ready用途: 检查 Alertmanager 是否准备好处理流量
返回: 准备好时返回 200,否则返回 503
示例:
1curl http://localhost:9093/-/readyKubernetes 就绪探针:
1readinessProbe:
2 httpGet:
3 path: /-/ready
4 port: 9093
5 initialDelaySeconds: 10
6 periodSeconds: 5端点:
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配置更新脚本:
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/-/readyDocker 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: 40s1. 在配置更新前验证
1# ✅ 好 - 先验证再重载
2amtool check-config alertmanager.yml && \
3 curl -X POST http://localhost:9093/-/reload
4
5# ❌ 差 - 直接重载可能导致错误
6curl -X POST http://localhost:9093/-/reload2. 使用就绪检查而非健康检查
1# ✅ 好 - 使用就绪检查确保可以处理流量
2curl http://localhost:9093/-/ready
3
4# ⚠️ 健康检查只表明进程存活,不代表可以处理请求
5curl http://localhost:9093/-/healthy3. 重载后验证
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=5s5. 日志监控
1# 监控重载操作
2journalctl -u alertmanager -f | grep -i "reload"
3
4# 或 Docker logs
5docker logs -f alertmanager | grep -i "reload"Notification Examples (通知示例):
Management API (管理API):
/-/healthy - 健康检查/-/ready - 就绪检查/-/reload - 重载配置这两个章节提供了实用的模板和自动化方案,帮助运维人员高效管理 Alertmanager!
指标和标签命名约定不是强制的,但可作为风格指南和最佳实践集合。
必须 (MUST):
1# ✅ 好
2prometheus_notifications_total # Prometheus 服务器特定
3process_cpu_seconds_total # 客户端库导出
4http_request_duration_seconds # 所有 HTTP 请求
5
6# ❌ 差
7notifications_total # 缺少前缀 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# ✅ 好 - 公共组件在前,相关指标排在一起
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_total1# ✅ 好 - sum() 或 avg() 有意义
2request_duration_seconds
3bytes_transferred
4resource_usage_ratio
5
6# ❌ 差 - sum() 无意义(混合了不同类型)
7queue_capacity_and_size # 混合容量和当前大小Prometheus 强烈建议包含单位和类型,原因:
指标消费可靠性和 UX
避免指标冲突
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
- ❌ 邮箱地址
- ❌ 其他无界值集合
| 类别 | 基础单位 | 备注 |
|---|---|---|
| Time | seconds | |
| Temperature | celsius | 首选 celsius 而非 kelvin(实用性) |
| Length | meters | |
| Bytes | bytes | |
| Bits | bytes | 始终使用 bytes 避免混淆 |
| Percent | ratio | 值为 0-1(而非 0-100),后缀为 _ratio |
| Voltage | volts | |
| Electric current | amperes | |
| Energy | joules | |
| Power | - | 优先导出 counter of joules,用 rate(joules[5m]) 得到瓦特 |
| Mass | grams | 优先 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简短答案: 埋点一切!
1. Online-Serving Systems (在线服务系统)
关键指标:
最佳实践:
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最佳实践:
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 (日志):
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. 不要过度使用标签
成本:
指导原则:
示例:
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
简单规则:
GaugeCounter 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 调用/秒):
最佳实践: 使用基准测试确定影响
注意: 本文档早于原生直方图(v2.40 实验性,v3.8 稳定)。
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])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 分数计算:
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 是因为桶是累积的
φ-quantile: φ*N 排名的观测值 (0 ≤ φ ≤ 1)
Histogram vs. Summary:
| 特性 | Histogram | Summary |
|---|---|---|
| 配置 | 选择适合范围的桶 | 选择所需 φ-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))两条经验法则:
需要聚合 → Histogram
其他情况:
Histogram 误差示例:
桶配置: {le="0.1"}, {le="0.2"}, {le="0.3"}, {le="0.45"}
Summary 误差示例:
配置: 0.95±0.01 (94th 到 96th 之间)
结论:
简单化告警:
推荐: 使用 CamelCase
1# ✅ 推荐
2- alert: HighRequestLatency
3- alert: DatabaseDown
4- alert: ServiceUnavailable
5
6# 也可以,但不推荐
7- alert: high_request_latencyOnline-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监控监控基础设施本身:
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补充白盒监控:
格式: 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"})验证规则:
仅限特定情况,不建议盲目使用
问题 1: 单点故障和瓶颈
问题 2: 失去自动健康监控
up 指标(每次抓取生成)问题 3: 永不遗忘
Service-Level Batch Jobs:
特点:
machine 或 instance 标签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/server11. 防火墙/NAT 问题:
1# 将 Prometheus 移到网络屏障内
2# 或使用 PushProxPushProx 架构:
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 使用检查清单:
如果任何一项为 No → 使用替代方案:
Naming (命名):
Instrumentation (埋点):
Histograms:
Alerting:
Recording Rules:
level:metric:operationsPushing:
以下是 Prometheus 官方和第三方 Exporter 列表,涵盖各种系统、服务和应用。
| 名称 | 链接 |
|---|---|
| Aerospike exporter | GitHub |
| AWS RDS exporter | GitHub |
| ClickHouse exporter | GitHub |
| Consul exporter 🏅 | GitHub |
| Couchbase exporter | GitHub |
| CouchDB exporter | GitHub |
| Druid exporter | GitHub |
| Elasticsearch exporter | GitHub |
| EventStore exporter | GitHub |
| IoTDB exporter | GitHub |
| KDB+ exporter | GitHub |
| Memcached exporter 🏅 | GitHub |
| MongoDB exporter | GitHub |
| MongoDB query exporter | GitHub |
| MongoDB Node.js Driver exporter | GitHub |
| MSSQL server exporter | GitHub |
| MySQL router exporter | GitHub |
| MySQL server exporter 🏅 | GitHub |
| OpenTSDB exporter | GitHub |
| Oracle DB exporter | GitHub |
| PgBouncer exporter | GitHub |
| PostgreSQL exporter | GitHub |
| Presto exporter | GitHub |
| ProxySQL exporter | GitHub |
| RavenDB exporter | GitHub |
| Redis exporter | GitHub |
| RethinkDB exporter | GitHub |
| SQL exporter | GitHub |
| Tarantool metric library | GitHub |
| Twemproxy exporter | GitHub |
| 名称 | 链接 |
|---|---|
| apcupsd exporter | GitHub |
| BIG-IP exporter | GitHub |
| Bosch Sensortec BMP/BME exporter | GitHub |
| Collins exporter | GitHub |
| Dell Hardware OMSA exporter | GitHub |
| Disk usage exporter | GitHub |
| Fortigate exporter | GitHub |
| IBM Z HMC exporter | GitHub |
| IoT Edison exporter | GitHub |
| InfiniBand exporter | GitHub |
| IPMI exporter | GitHub |
| knxd exporter | GitHub |
| Modbus exporter | GitHub |
| Netgear Cable Modem exporter | GitHub |
| Netgear Router exporter | GitHub |
| Network UPS Tools (NUT) exporter | GitHub |
| Node/system metrics exporter 🏅 | GitHub |
| NVIDIA GPU exporter | GitHub |
| ProSAFE exporter | GitHub |
| SmartRAID exporter | GitLab |
| Waveplus Radon Sensor exporter | GitHub |
| Weathergoose Climate Monitor exporter | GitHub |
| Windows exporter | GitHub |
| Intel® Optane™ PMem Controller exporter | GitHub |
| 名称 | 链接 |
|---|---|
| Bamboo exporter | GitHub |
| Bitbucket exporter | GitHub |
| Confluence exporter | GitHub |
| Jenkins exporter | GitHub |
| JIRA exporter | GitHub |
| 名称 | 链接 |
|---|---|
| Beanstalkd exporter | GitHub |
| EMQ exporter | GitHub |
| Gearman exporter | GitHub |
| IBM MQ exporter | GitHub |
| Kafka exporter | GitHub |
| NATS exporter | GitHub |
| NSQ exporter | GitHub |
| Mirth Connect exporter | GitHub |
| MQTT blackbox exporter | GitHub |
| MQTT2Prometheus | GitHub |
| RabbitMQ exporter | GitHub |
| RabbitMQ Management Plugin exporter | GitHub |
| RocketMQ exporter | GitHub |
| Solace exporter | GitHub |
| 名称 | 链接 |
|---|---|
| Ceph exporter | GitHub |
| Ceph RADOSGW exporter | GitHub |
| Gluster exporter | GitHub |
| GPFS exporter | GitHub |
| Hadoop HDFS FSImage exporter | GitHub |
| HPE CSI info metrics provider | Docs |
| HPE storage array exporter | GitHub |
| Lustre exporter | GitHub |
| NetApp E-Series exporter | GitHub |
| Pure Storage exporter | GitHub |
| ScaleIO exporter | GitHub |
| Tivoli Storage Manager/IBM Spectrum Protect | GitHub |
| 名称 | 链接 |
|---|---|
| Apache exporter | GitHub |
| HAProxy exporter 🏅 | GitHub |
| Nginx metric library | GitHub |
| Nginx VTS exporter | GitHub |
| Passenger exporter | GitHub |
| Squid exporter | GitHub |
| Tinyproxy exporter | GitHub |
| Varnish exporter | GitHub |
| WebDriver exporter | GitHub |
| 名称 | 链接 |
|---|---|
| AWS ECS exporter | GitHub |
| AWS Health exporter | GitHub |
| AWS SQS exporter | GitHub |
| AWS SQS Prometheus exporter | GitHub |
| Azure Health exporter | GitHub |
| BigBlueButton exporter | GitHub |
| Cloudflare exporter | GitLab |
| Cryptowat exporter | GitHub |
| DigitalOcean exporter | GitHub |
| Docker Cloud exporter | GitHub |
| Docker Hub exporter | GitHub |
| Fastly exporter | GitHub |
| GitHub exporter | GitHub |
| Gmail exporter | GitHub |
| GraphQL exporter | GitHub |
| InstaClustr exporter | GitHub |
| Mozilla Observatory exporter | GitHub |
| OpenWeatherMap exporter | GitHub |
| Pagespeed exporter | GitHub |
| Rancher exporter | GitHub |
| Speedtest exporter | GitHub |
| Tankerkönig API exporter | GitHub |
| 名称 | 链接 |
|---|---|
| Fluentd exporter | GitHub |
| Google’s mtail log data extractor | GitHub |
| Grok exporter | GitHub |
| 名称 | 链接 |
|---|---|
| AWS Cost exporter | GitHub |
| Azure Cost exporter | GitHub |
| Kubernetes Cost exporter | GitHub |
| 名称 | 链接 |
|---|---|
| ACT Fibernet exporter | Git |
| BIND exporter | GitHub |
| BIND query exporter | GitHub |
| Bitcoind exporter | GitHub |
| Blackbox exporter 🏅 | GitHub |
| Bungeecord exporter | GitHub |
| BOSH exporter | GitHub |
| cAdvisor | GitHub |
| Cachet exporter | GitHub |
| ccache exporter | GitHub |
| c-lightning exporter | GitHub |
| DHCPD leases exporter | GitHub |
| Dovecot exporter | GitHub |
| Dnsmasq exporter | GitHub |
| eBPF exporter | GitHub |
| eBPF network traffic exporter | GitHub |
| Ethereum Client exporter | GitHub |
| FFmpeg exporter | GitHub |
| File statistics exporter | GitHub |
| JFrog Artifactory exporter | GitHub |
| Hostapd exporter | GitHub |
| IBM Security Verify Access exporter | GitLab |
| IPsec exporter | GitHub |
| ipset exporter | GitHub |
| IRCd exporter | GitHub |
| Linux HA ClusterLabs exporter | GitHub |
| JMeter plugin | GitHub |
| JSON exporter | GitHub |
| Kannel exporter | GitHub |
| Kemp LoadBalancer exporter | GitHub |
| Kibana exporter | GitHub |
| kube-state-metrics | GitHub |
| Locust exporter | GitHub |
| Meteor JS web framework exporter | Atmosphere |
| Minecraft exporter module | GitHub |
| Minecraft exporter | GitHub |
| NetBird exporter | GitHub |
| Nomad exporter | GitLab |
| nftables exporter | GitHub |
| OpenStack exporter | GitHub |
| OpenStack blackbox exporter | GitHub |
| OpenVPN exporter | GitHub |
| oVirt exporter | GitHub |
| Pact Broker exporter | GitHub |
| PHP-FPM exporter | GitHub |
| PowerDNS exporter | GitHub |
| Podman exporter | GitHub |
| Prefect2 exporter | GitHub |
| Process exporter | GitHub |
| rTorrent exporter | GitHub |
| Rundeck exporter | GitHub |
| SABnzbd exporter | GitHub |
| SAML exporter | GitHub |
| Script exporter | GitHub |
| Shield exporter | GitHub |
| Smokeping prober | GitHub |
| SMTP/Maildir MDA blackbox prober | GitHub |
| SoftEther exporter | GitHub |
| SSH exporter | GitHub |
| Teamspeak3 exporter | GitHub |
| Transmission exporter | GitHub |
| Unbound exporter | GitHub |
| WireGuard exporter | GitHub |
| Xen exporter | GitHub |
图例:
参考链接:
统计:
总计: 200+ 个 Exporter
这是一个完整的 Golang Exporter 示例,展示了如何使用 Prometheus 客户端库实现:
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/gopsutil2. 运行 Exporter
1# 默认端口 28880
2go run main.go
3
4# 自定义端口
5go run main.go -port 90903. 测试 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
6done4. 查看指标
访问 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// 实现 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}特点:
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]将此 Exporter 添加到 Prometheus 抓取配置:
1scrape_configs:
2 - job_name: 'custom-exporter'
3 static_configs:
4 - targets: ['localhost:28880']
5 scrape_interval: 15s查询 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 桶设计
2. 标签使用
3. 指标命名
_seconds, _bytes)4. 性能优化
HistogramVec 而非多个 HistogramObserve() 而非 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# 使用标签缓存这个示例展示了:
完整代码可作为生产环境 Exporter 的起点,根据实际需求扩展更多指标类型和业务逻辑。
Pushgateway 是 Prometheus 生态中用于接收短期任务推送指标的中间组件。本示例展示如何使用 Golang 客户端推送指标到 Pushgateway。
✅ 适合使用 Pushgateway:
❌ 不适合使用 Pushgateway:
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}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。
1go mod init pushgateway-demo
2go get github.com/prometheus/client_golang/prometheus
3go get github.com/prometheus/client_golang/prometheus/push1go run main.go输出:
1Successfully pushed metrics to Pushgateway
2Successfully deleted metrics from Pushgateway访问 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"} 11// 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)分组标签用于区分不同的指标来源:
1pusher.Grouping("instance", "server-1")
2pusher.Grouping("environment", "production")URL 映射:
1http://localhost:9091/metrics/job/batch_job/instance/server-1/environment/production最佳实践:
为什么使用自定义 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// 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 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} 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 抓取 Pushgateway:
1scrape_configs:
2 - job_name: 'pushgateway'
3 honor_labels: true # 重要: 保留推送的 job 标签
4 static_configs:
5 - targets: ['localhost:9091']关键配置说明:
honor_labels: true - 保留 Pushgateway 中的 job 和 instance 标签 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// ❌ 不好 - 第二次 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 都保留 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}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 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}1pusher := push.New(url, job).
2 BasicAuth("username", "password")Pushgateway 启动参数:
1./pushgateway \
2 --web.basic-auth-file=/etc/pushgateway/auth.ymlauth.yml:
1basic_auth_users:
2 admin: $2y$10$... # bcrypt hash 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 是无状态的,重启后数据丢失。解决方案:
1./pushgateway --persistence.file=/data/metrics.db关键指标:
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 适用于:
关键要点:
Add() 保留已有指标,使用 Push() 覆盖Delete() 清理指标honor_labels: true不要用于:
完整代码可作为生产环境批处理任务监控的起点! 🎯
Textfile Collector 是 Node Exporter 的一个功能,允许通过读取 .prom 格式的文本文件来暴露自定义指标。这对于以下场景非常有用:
本示例展示如何使用 expfmt 包将指标写入 textfile 格式。
✅ 适合使用 Textfile Collector:
✅ 相比 Pushgateway 的优势:
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}二进制安装:
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_collector1go mod init textfile-demo
2go get github.com/prometheus/client_golang/prometheus
3go get github.com/prometheus/common/expfmt1# 修改输出路径到 textfile 目录
2go run main.go查看生成的文件:
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访问 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从 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: 具体的指标值和标签将 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// 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 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_monitorbash 版本:
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// ✅ 好 - 原子写入,避免 Node Exporter 读取部分文件
2tmpFile := filename + ".tmp"
3os.Create(tmpFile)
4// ... 写入内容
5os.Rename(tmpFile, filename)
6
7// ❌ 不好 - 直接写入,可能读取到不完整数据
8os.Create(filename)
9// ... 写入内容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 小时未更新" 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}1# 确保 Node Exporter 用户可读
2chmod 644 /var/lib/node_exporter/textfile_collector/*.prom
3
4# 目录权限
5chmod 755 /var/lib/node_exporter/textfile_collector1scrape_configs:
2 - job_name: 'node'
3 static_configs:
4 - targets: ['localhost:9100'] 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.9Node 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_errorQ2: 格式错误导致指标无法读取?
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"} 42Q3: 如何删除指标?
A: 删除对应的 .prom 文件:
1rm /var/lib/node_exporter/textfile_collector/custom.promNode Exporter 会在下次 scrape 时自动移除这些指标。
文件数量限制:
文件大小:
更新频率:
Textfile Collector 适用于:
关键要点:
registry.Gather() 收集指标expfmt.MetricFamilyToText() 写入文件对比 Pushgateway:
| 特性 | Textfile Collector | Pushgateway |
|---|---|---|
| 部署复杂度 | 低 (复用 Node Exporter) | 中 (需要额外组件) |
| 网络依赖 | 无 (本地文件) | 有 (HTTP 推送) |
| 生命周期 | 自动 (删除文件) | 手动 (需调用 Delete) |
| 适用场景 | 本地定时任务 | 远程批处理任务 |
完整代码可作为批处理任务指标导出的最佳实践!🎯
本章节提供 Python 和 Shell 脚本的完整 Prometheus 集成示例,涵盖三种主要模式。
使用 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 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 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()1*/5 * * * * /usr/bin/python3 /path/to/python_textfile_collector.py 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| 特性 | Python | Go | Shell |
|---|---|---|---|
| 性能 | 中 | 高 | 低 |
| 部署 | 需要 Python 环境 | 单一二进制 | 无依赖 |
| 开发效率 | 高 | 中 | 中 |
| 适用场景 | 数据处理、脚本 | 生产服务 | 系统监控 |
| 模式 | 优势 | 劣势 | 场景 |
|---|---|---|---|
| HTTP Exporter | 实时更新 | 需要暴露端口 | 长期服务 |
| Pushgateway | 适合批处理 | 需要额外组件 | 批处理任务 |
| Textfile | 简单、无依赖 | 定期更新 | 定时任务 |
1# 使用自定义 Registry
2registry = CollectorRegistry()
3
4# 合理标签基数
5gauge = Gauge('metric', 'desc', ['service']) # ✅ 低基数
6gauge = Gauge('metric', 'desc', ['user_id']) # ❌ 高基数1# 任务结束时删除指标
2try:
3 push_to_gateway(url, job='batch', registry=registry)
4finally:
5 delete_from_gateway(url, job='batch')1# 原子写入
2tmp_file = f"{metric_file}.tmp"
3write_to_textfile(tmp_file, registry)
4os.rename(tmp_file, metric_file) 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']本章节提供了完整的示例代码:
所有代码均可直接用于生产环境!🎯
日历 · 精选 · 友链 · 更多
如果内容对你有帮助,欢迎请我喝杯咖啡 ☕



One的公众号
爱折腾博客的小白