Files
wiki/监控/Prometheus/Prometheus-学习手册-04-Alertmanager与生态篇.md
T
2026-10-08 15:05:32 +08:00

402 KiB
Raw Blame History

Prometheus 知识学习手册 · 第 4 篇:Alertmanager 与生态

基于 prometheus.io 官方文档 整理 文档版本:Prometheus v3.15.0 + Alertmanager latest(2026-10)| 抓取日期:2026-10-08 数据来源:prometheus/prometheus@v3.15.0、prometheus/alertmanager、prometheus/docs 官方仓库源文件(与官网 latest 内容一致)

全册结构(共 4 篇)

  • 第 1 篇:入门与架构
  • 第 2 篇:PromQL 与查询
  • 第 3 篇:配置与运维
  • 第 4 篇:Alertmanager 与生态(告警、插桩、可视化、规范) ← 本篇

本篇共 10 章:第 1-4 章为 Alertmanager 告警体系,第 5-10 章为插桩、生态与规范。

目录

上半部分(本篇第 1-4 章:Alertmanager 专题)

下半部分(本篇第 5-10 章:插桩、生态与规范,由续稿提供)


第 1 章 Alertmanager 架构与核心概念

Prometheus 的告警能力并非由单一组件完成,而是「规则引擎 + 通知管理」两段式协作的结果。本章先建立整体认知:告警从何而来、经谁处理、如何送达,以及 Alertmanager 提供的分组、抑制、静默、去重、高可用等核心机制。

1.1 告警体系的组成

Prometheus 的告警分为两部分:

  1. Prometheus server 中的 alerting rules:Prometheus 依据时序数据(time series)周期性评估告警规则,条件成立时把告警推送给 Alertmanager。
  2. Alertmanager:独立进程,负责接收这些告警,并对其进行管理——静默、抑制、聚合,最后通过多种方式发送通知。

Alertmanager 发送通知的方式,官方原文列举了三类:email(邮件)、on-call notification systems(值班/待命通知系统)、chat platforms(聊天平台)。实际落地上,这三类由「receiver integration」承载,官方目前提供了 18 种原生集成(详见第 2.10 节)。

搭建一套完整的告警与通知链路,官方给出三个主要步骤:

  1. Setup and configure the Alertmanager——部署并配置 Alertmanager。
  2. Configure Prometheus 与 Alertmanager 通信——在 Prometheus 配置中通过 alertmanager_config 指定 Alertmanager 地址。
  3. 在 Prometheus 中创建 alerting rules——通过 alerting_rules 定义告警规则。

整体数据流如下:

┌──────────────────────────┐         ┌──────────────────────────┐        ┌────────────────────┐
│  Prometheus Server       │  push   │  Alertmanager            │ notify │  Notification      │
│  alerting rules 周期评估 │ ──────► │  dedup / group / route   │ ─────► │  email / PagerDuty │
│  (expr / for / labels)   │  POST   │  silence / inhibit / HA  │        │  Slack / Webhook…  │
└──────────────────────────┘ api/v2  └──────────────────────────┘        └────────────────────┘
         时序数据                    (任一实例独立接收,状态互通)              18 类 receiver

引自:Alerting overview

💡 补充:alerting rules、alertmanager_config、alerting_rules 三个键均定义在 Prometheus 的配置(prometheus.yml)中,不属于 Alertmanager 的配置文件。第 2 章讨论的 alertmanager.yml 只负责「收到告警之后怎么处理」,两个配置文件不要混淆。

1.2 Alertmanager 的核心能力

Alertmanager 处理由客户端应用(例如 Prometheus server)发送来的告警,其职责可归纳为五点:

能力 英文 一句话说明
去重 deduplicating 把标签完全相同的重复告警合并为同一条实例
分组 grouping 把性质相近的告警(如同一集群的批量故障)归并为单条通知
路由 routing 依据标签把告警分发到「正确的」receiver 集成
静默 silencing 在指定时间窗内,让匹配 matcher 的告警不发通知
抑制 inhibition 当某条(更宏观的)告警已触发时,压制其它(派生的)告警通知
  • 去重(deduplication):labels 集合完全相同的告警被视为同一告警的重复上报,Alertmanager 只会保留一份。
  • 分组(grouping):成百上千条告警可能源自同一个根因,分组后只发一条紧凑通知。
  • 路由(routing):receiver integration 可以是 email、PagerDuty、OpsGenie 等,具体由配置文件中的 routing tree 决定。
  • 静默(silencing):基于 matchers 配置,入站告警若匹配某条 active silence 的全部 matcher,则不发送任何通知。
  • 抑制(inhibition):在特定其它告警已触发时,抑制某些告警的通知。

此外,Alertmanager 还负责 高可用:在 HA 部署中,每个 Alertmanager 实例独立接收告警,但 silences 与 notification state 会在 peers 之间复制(详见第 4 章)。

引自:Alertmanager

1.3 告警的生命周期

一条告警从产生到消亡,会横跨 Prometheus 与 Alertmanager 两端。下表按阶段梳理。

阶段 发生位置 说明
规则评估 Prometheus 按 evaluation_interval 周期性执行 expr,条件为真则告警进入 pending
等待 for Prometheus 持续为真达 for 时长后,告警转为 firing;期间若恢复则不触发
推送 Prometheus → Alertmanager firing 告警以 api/v2/alerts POST 给 Alertmanager,并周期性重发
接收与去重 Alertmanager 依据 labels 去重,更新 startsAt/endsAt
分组 Alertmanager 按 route 的 group_by 归入聚合组,等待 group_wait
抑制/静默过滤 Alertmanager 过滤掉被 inhibit 或匹配 silence 的告警
路由与通知 Alertmanager 沿 routing tree 匹配 receiver,在 group_interval/repeat_interval 约束下发送
解决 Prometheus → Alertmanager Prometheus 发出带 endsAt 的 resolved 告警;Alertmanager 视 send_resolved 决定是否发 resolved 通知

关于 Alertmanager 侧的告警时间语义(来自 Alerts API 规范):

  • startsAt:告警触发时间。若省略,Alertmanager 将其设为当前时间。
  • endsAt:告警应被解决的时间。若省略,Alertmanager 将其设为当前时间 + resolve_timeout。
  • generatorURL:唯一 URL,链接到告警来源(例如 Prometheus 中触发该告警的 rule)。
  • 客户端应定期重新发送 firing alerts 直到告警解决;若省略 endsAt,Alertmanager 会把已存在告警的 endsAt 更新为当前时间 + resolve_timeout。firing 告警在其 endsAt 过去后即被解决。
  • 为确保 resolved notification 能发出,客户端在告警解决后最长 5 分钟内仍应持续重发 resolved alerts;由于 Alertmanager 是无状态的,这保证了即使 Alertmanager 崩溃或重启,resolved notification 依然会发出。

引自:Alerts API

💡 补充:这条「客户端持续重发」的约定是理解 Alertmanager「无状态」设计的关键——Alertmanager 自身不持久化 active alerts(只持久化 silences 与 notification log),告警的「记忆」由 Prometheus 的重复推送来维持。因此 Prometheus 与 Alertmanager 之间的网络短暂抖动通常不会丢告警。

1.4 核心概念详解

1.4.1 分组(Grouping)

是什么:把性质相似的告警归类为一条通知。

为什么:在大量系统同时故障时,成百上千条告警可能同时涌入。例如网络分区导致半数服务实例无法连接数据库,如果每条实例各发一条告警,会产生数百条通知,淹没值班人员。

原理/怎么用:可配置按 cluster 与 alertname 分组,此时同一集群下同名的告警会被合并,只发一条紧凑通知。分组的告警、分组通知的时机、通知的 receivers,均由配置文件中的 routing tree 控制(route 的 group_by、group_wait、group_interval、repeat_interval)。

注意事项:group_by 使用特殊值 '...'(即 group_by: ['...'])作为唯一标签名时,会完全禁用聚合,把所有 alert 原样透传——即每个 alert 独立成组。这在与需要逐条告警的下游集成时有用。

1.4.2 抑制(Inhibition)

是什么:在特定其它告警已触发时,抑制某些告警的通知。

为什么:系统/服务之间存在依赖关系。当上游故障时,下游会连带告警,若全部发出会造成噪声。

原理/怎么用:典型示例是:一条「整个集群不可达」的告警触发时,可静音该集群的所有其它告警。Inhibition 通过配置文件中的 inhibit_rules 配置:当存在匹配 source matchers 的告警时,静音匹配 target matchers 的告警,且二者在 equal 列出的标签上取值相同。

注意事项:语义上「缺失标签」与「空值标签」相同,所以若 equal 中所有标签名在 source 与 target 中均缺失,抑制规则也会生效。要防止告警抑制自身,建议把 target/source matchers 设计成「任何告警都不会同时出现在两侧」。

1.4.3 静默(Silences)

是什么:在给定时间段内单纯地静音告警。

为什么:计划内维护、已知故障窗口等场景,需要临时屏蔽通知而不改动规则。

原理/怎么用:silence 基于 matchers 配置,语法与 routing tree 相同。入站告警会检查是否匹配某条 active silence 的全部 equality 或 regular expression matcher;若匹配,则该告警不发送任何通知。Silences 在 Alertmanager 的 web interface 中配置(或经 v2 API)。

注意事项:与抑制的区别在于——抑制是「告警之间的条件关系」,静默是「时间维度上的主动屏蔽」。静默是 HA 集群中一等复制状态,创建后会自动传播到所有 peer。

1.4.4 去重(Deduplication)

是什么:识别「同一条告警」的多次上报。

为什么:Prometheus 会周期性重发 firing 告警(见 1.3),同时多个 Prometheus 实例、多个 Alertmanager 实例也可能上报同一告警。

原理:告警的身份由 labels 集合决定——labels 被用于「去重同一告警的相同实例」。此外,HA 集群中还有一层去重:通过 gossip 复制的 notification log,各实例共享「谁在什么时候发过哪条通知」的历史,从而避免同一通知被多个实例重复发出。

注意事项:labels 是去重的唯一依据,因此给告警加上会频繁变化的 label(如时间戳、随机 ID)会导致去重失效、通知风暴。

1.4.5 高可用(HA gossip)

是什么:多个 Alertmanager 实例组成集群,彼此通过 gossip 协议同步状态。

为什么:单点 Alertmanager 宕机会导致整个告警链路中断。

原理/怎么用:集群中每个实例独立接收告警,通过 gossip mesh 复制 silences 与 notification log,各自独立处理并发送通知;发送前用 peer position + notification log 去重(详见第 4 章)。通过 --cluster.* flags 组网。

注意事项:不要对 Prometheus 与 Alertmanager 之间的流量做负载均衡,而应让 Prometheus 指向所有 Alertmanager 实例的地址列表。

引自:Alertmanager

1.5 Alertmanager 的限制与注意事项

1.5.1 客户端行为约束

Alertmanager 对其客户端行为有特殊要求(详见 Alerts API,即第 4.8 节)。这一要求仅在不用 Prometheus 发送告警的高级场景下才相关——因为 Prometheus 已经替你处理了这些细节。

1.5.2 建议由 Prometheus 发送告警

Prometheus 自行负责把告警发送给 Alertmanager。官方建议在 Prometheus 中基于 time series 数据配置 alerting rules,而非直接往 Alerts API 发告警。原因:Prometheus 处理了许多特殊情况,以确保即使 Alertmanager 崩溃或重启,告警也能送达。

1.5.3 Alert limits(每 alertname 告警数量限制)

Alertmanager 支持限制每个 alertname 的 active alerts 数量,通过 --alerts.per-alertname-limit flag 配置。

  • 达到上限后,新告警被丢弃;已存在告警的 heartbeats 仍会处理(避免已有告警被误判为 resolved)。
  • 已知告警(按 fingerprint)会自动过期以腾出空间。
  • 该特性适用于「同一告警实例数异常激增」的场景,可防止可靠性问题、避免 receiver 被淹没。

相关指标:

  • alertmanager_alerts_limited_total:因 per-alertname limit 被丢弃的告警总数。
  • 启用 alert-names-in-metrics feature flag 后,会给该 metric 增加 alertname label(便于定位是哪个告警名触顶)。

1.5.4 集群相关提醒

  • YAML 中没有 cluster 配置,集群必须通过 CLI flags 组建。
  • HA 下 alerts 本身不持久化——依赖 Prometheus 定期重发 firing alerts;只有 silences 与 notification log 会落盘。
  • 默认集群通信未加密,跨 WAN 生产部署应启用 mutual TLS。

引自:Alertmanager | High Availability

第 2 章 Alertmanager 配置全解

本章逐字段拆解 alertmanager.yml。这是 Alertmanager 最核心、体量最大的一份配置,涵盖全局默认值、路由树、抑制规则、时间区间、标签匹配器,以及 18 类 receiver 集成的完整字段。

2.1 配置概览、重载与命令行限制

Alertmanager 通过两类输入配置:

  • 命令行 flag:不可变的系统参数(监听地址、集群参数、数据目录、feature flag 等)。
  • 配置文件(YAML):定义 inhibition rules、notification routing、notification receivers。

所有 flag 可用 alertmanager -h 查看。官方还提供一个可视化编辑器(routing-tree-editor)辅助构建 routing tree。

配置重载:运行时可重载。若新配置格式不正确,则变更不生效,并记录错误日志。触发方式有两种:

  1. 向 Alertmanager 进程发送 SIGHUP。
  2. 向 /-/reload 端点发送 HTTP POST。

通过 --config.file 指定配置文件:

./alertmanager --config.file=alertmanager.yml

Limits(命令行限制)

Flag 说明 默认
--silences.max-silences 限制 silence 的最大数量(含已过期的) 禁用
--silences.max-silence-size-bytes 限制单个 silence 的最大字节数(单位 bytes) 禁用

引自:Configuration

2.2 通用占位符定义

配置文档中用一组占位符标记参数类型:

占位符 含义 说明
<duration> 时长 匹配正则 `((([0-9]+)y)?(([0-9]+)w)?(([0-9]+)d)?(([0-9]+)h)?(([0-9]+)m)?(([0-9]+)s)?(([0-9]+)ms)?
<labelname> 标签名 匹配 [a-zA-Z_][a-zA-Z0-9_]* 的字符串
<labelvalue> 标签值 unicode 字符串
<filepath> 文件路径 当前工作目录下的有效路径
<boolean> 布尔值 true 或 false
<string> 字符串 普通字符串
<secret> 秘密 作为秘密的普通字符串,如密码
<tmpl_string> 模板字符串 使用前会做模板展开的字符串
<tmpl_secret> 模板秘密 使用前做模板展开的秘密字符串
<int> 整数 整数值
<regex> 正则 任意合法 RE2 正则(两端自动锚定;要取消锚定用 .*<regex>.*)

文件为 YAML 格式。方括号 [] 表示参数可选;非 list 参数取指定默认值。

引自:Configuration

2.3 global 全局设置

global 指定对所有其他配置上下文有效的参数,同时作为其他配置段的默认值。

global:
  [ smtp_from: <tmpl_string> ]                 # 默认 SMTP From 头
  [ smtp_smarthost: <string> ]                 # 发送邮件的 SMTP smarthost,含端口;常为 25,或 587(over TLS/STARTTLS),例 smtp.example.org:587
  [ smtp_hello: <string> | default = "localhost" ]
  [ smtp_auth_username: <string> ]             # SMTP Auth 用 CRAM-MD5/LOGIN/PLAIN;为空则不做认证;PLAIN 仅在 TLS 下支持
  [ smtp_auth_password: <secret> ]             # LOGIN 与 PLAIN
  [ smtp_auth_password_file: <string> ]
  [ smtp_auth_identity: <string> ]             # PLAIN
  [ smtp_auth_secret: <secret> ]               # CRAM-MD5
  [ smtp_auth_secret_file: <string> ]
  [ smtp_require_tls: <bool> | default = true ] # Go 不支持未加密连接到远端 SMTP 端点
  [ smtp_tls_config: <tls_config> ]
  [ smtp_force_implicit_tls: <bool> ]          # 不按端口强制隐式 TLS
  [ jira_api_url: <string> ]
  [ slack_api_url: <secret> ]
  [ slack_api_url_file: <filepath> ]
  [ slack_app_token: <secret> ]
  [ slack_app_token_file: <filepath> ]
  [ slack_app_url: <string> ]
  [ victorops_api_key: <secret> ]              # 与 victorops_api_key_file 互斥
  [ victorops_api_key_file: <filepath> ]
  [ victorops_api_url: <string> | default = "https://alert.victorops.com/integrations/generic/20131114/alert/" ]
  [ pagerduty_url: <string> | default = "https://events.pagerduty.com/v2/enqueue" ]
  [ opsgenie_api_key: <secret> ]
  [ opsgenie_api_key_file: <filepath> ]
  [ opsgenie_api_url: <string> | default = "https://api.opsgenie.com/" ]
  [ rocketchat_api_url: <string> | default = "https://open.rocket.chat/" ]
  [ rocketchat_token: <secret> ]               # 与 rocketchat_token_file 互斥
  [ rocketchat_token_file: <filepath> ]
  [ rocketchat_token_id: <secret> ]            # 与 rocketchat_token_id_file 互斥
  [ rocketchat_token_id_file: <filepath> ]
  [ wechat_api_url: <string> | default = "https://qyapi.weixin.qq.com/cgi-bin/" ]
  [ wechat_api_secret: <secret> ]
  [ wechat_api_secret_file: <string> ]
  [ wechat_api_corp_id: <string> ]
  [ telegram_api_url: <string> | default = "https://api.telegram.org" ]
  [ telegram_bot_token: <secret> ]             # 与 telegram_bot_token_file 互斥
  [ telegram_bot_token_file: <string> ]
  [ webex_api_url: <string> | default = "https://webexapis.com/v1/messages" ]
  [ mattermost_webhook_url: <secret> ]
  [ mattermost_webhook_url_file: <string> ]
  [ http_config: <http_config> ]               # 默认 HTTP 客户端配置
  [ resolve_timeout: <duration> | default = 5m ] # 若 alert 不含 EndsAt 则用此默认值;超时后若未更新即可判定为 resolved;对来自 Prometheus 的 alert 无影响(它们总含 EndsAt)

关键字段解读:

字段 作用 注意
smtp_smarthost SMTP 服务器地址,含端口 25,或 587(STARTTLS)
smtp_require_tls 是否要求 TLS 默认 true;Go 不支持未加密连接远端 SMTP 端点
smtp_force_implicit_tls 强制隐式 TLS 不按端口判断,直接 TLS
resolve_timeout 告警解析超时 若 alert 无 EndsAt 则以其为默认值;超时后若未更新即判为 resolved。对来自 Prometheus 的告警无影响,因为它们总带 EndsAt
slack_api_url / slack_app_token Slack 两种认证方式 分别有对应的 _file 变体
http_config 全局默认 HTTP 客户端配置 各 receiver 的 http_config 默认取此值

引自:Configuration

2.4 顶级配置段(File layout)

除 global 外,配置文件的顶级段如下:

顶级键 说明
global 全局默认值
templates [ - <filepath> ... ]——读取自定义通知模板定义的文件;最后一段可用通配符,如 templates/*.tmpl
route <route>——routing tree 的根节点
receivers - <receiver> ...——通知接收者列表
inhibit_rules [ - <inhibit_rule> ... ]——抑制规则列表
mute_time_intervals [ - <time_interval> ... ]——DEPRECATED: 使用 time_intervals
time_intervals [ - <time_interval> ... ]——用于 mute/activate route
event_recorder (可选)捕获重要 Alertmanager 事件并投递到 file/webhook/Kafka/stdout;受 event-recorder feature flag 控制,需 --enable-feature=event-recorder
tracing (可选)分布式追踪配置

引自:Configuration

2.5 route 路由树

2.5.1 继承规则

定义一个 routing tree 节点及其子节点。多数可选参数若未设置则从父节点继承:

参数 继承行为
receiver、group_by、group_wait、group_interval、repeat_interval 未设置则从父节点继承
labels 从父到子合并(非替换)
mute_time_intervals、active_time_intervals 完全不继承(省略即无,无论父节点如何)

2.5.2 匹配与遍历逻辑

  • 每个 alert 从顶级 route进入(顶级 route 必须匹配所有 alert,即不配置任何 matcher),再遍历子节点。
  • 若 continue 为 false,在第一个匹配子节点后停止。
  • 若匹配节点 continue 为 true,则继续匹配后续兄弟节点。
  • 若 alert 不匹配任何子节点(无匹配或无子节点),则按当前节点的配置参数处理。

2.5.3 字段全解

[ receiver: <string> ]
[ group_by: '[' <labelname>, ... ']' ]        # 聚合标签;用特殊值 '...' 作为唯一标签名(group_by: ['...'])可完全禁用聚合,原样透传所有 alert
[ continue: <boolean> | default = false ]      # 是否继续匹配后续兄弟节点
# DEPRECATED: 使用 matchers
match:
  [ <labelname>: <labelvalue>, ... ]
# DEPRECATED: 使用 matchers
match_re:
  [ <labelname>: <regex>, ... ]
matchers:                                      # 必须满足才匹配该节点
  [ - <matcher> ... ]
labels:                                        # 附加到 route 的标签,通知模板经 routeLabels 函数可用(也见于 /api/v2/alerts/groups 的 routeLabels 字段)
  [ <labelname>: <tmpl_string>, ... ]
[ group_wait: <duration> | default = 30s ]     # 新组首条通知前等待;可由子路由继承
[ group_interval: <duration> | default = 5m ]  # group_wait 后对同组后续通知间隔;同时设定每次发送的通知流水线 context 超时(发送耗时超过该值会被取消);可由子路由继承
[ repeat_interval: <duration> | default = 4h ] # 重复上次通知间隔;应为 group_interval 的倍数,否则向上取整到下一倍数;若比 --data.retention 更长,则在数据保留期结束时重复;可由子路由继承
mute_time_intervals:                           # 需匹配 time_intervals 中的名字;根节点不可有;不继承
  [ - <string> ...]
active_time_intervals:                         # 空值表示始终 active;根节点不可有;不继承
  [ - <string> ...]
routes:                                        # 零或多个子路由
  [ - <route> ... ]

三个 group 相关参数的语义细化:

  • group_wait:新组首条通知前的等待时间。
    • 太短 → 首条通知可能不含完整预期 alert 集合;且抑制 alert 若未及时到达,则本应被抑制的 alert 未被抑制。
    • 太长 → firing 通知不及时。
    • 若 alert 在 group_wait 结束前 resolved,则不发送通知(减少 flapping 噪声)。错过初始 group_wait 的 alert 通知将在下一 group_interval 发送。
  • group_interval:group_wait 后,对同组后续通知的间隔。
  • repeat_interval:重复上次通知的间隔。
    • 应为 group_interval 的倍数,否则向上取整到下一倍数。
    • 若自上次 group_interval 以来有新 alert fired 或 firing alert resolved,则不重复。

labels 细节:从父到子合并,子路由可重定义覆盖单个标签;与 group_by 不同,route labels 不影响分组。标签值本身可为模板,按每个 alert group 的通知数据渲染(可引用组标签、其他 route labels 等)。route labels 按组渲染,与单个通知无关(也经 API 暴露)。仅对特定通知有意义的字段不可用,尤其 .NotificationReason 在 route label 模板中恒为 "unknown"。

2.5.4 Route 示例

route:
  receiver: 'default-receiver'
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  group_by: [cluster, alertname]
  routes:
    - receiver: 'database-pager'
      group_wait: 10s
      matchers:
        - service=~"mysql|cassandra"
    - receiver: 'frontend-pager'
      group_by: [product, environment]
      matchers:
        - team="frontend"
    - receiver: 'dev-pager'
      matchers:
        - service="inhouse-service"
      mute_time_intervals:
        - offhours
        - holidays
      continue: true
    - receiver: 'on-call-pager'
      matchers:
        - service="inhouse-service"
      active_time_intervals:
        - offhours
        - holidays

上例要点:

  • 根路由 group_by: [cluster, alertname],默认 30s 聚合窗口,5m 组内间隔,4h 重复间隔。
  • database-pager:仅缩短 group_wait 到 10s,其余继承根路由。
  • frontend-pager:按 product、environment 重新分组。
  • dev-pager 与 on-call-pager 共享同一 matcher(service="inhouse-service"),但前者在 offhours/holidays 时段被静音(mute_time_intervals),且设 continue: true 让匹配继续;后者只在 offhours/holidays 时段激活(active_time_intervals)。二者组合实现「工作时间与值班时间路由到不同 pager」。

引自:Configuration

2.6 time_intervals 时间区间

name: <string>
time_intervals:
  [ - <time_interval_spec> ... ]

<time_interval_spec>

- times: [ - <time_range> ...]
  weekdays: [ - <weekday_range> ...]
  days_of_month: [ - <days_of_month_range> ...]
  months: [ - <month_range> ...]
  years: [ - <year_range> ...]
  location: <string>
  • 所有字段均为 list。每个非空 list 内至少一个元素满足即匹配该字段;字段未指定则任意值均匹配;一个时刻要匹配完整 time interval 需所有字段都匹配。
  • 部分字段支持 range 与负索引。未指定时区则按 UTC。
子字段 语法 说明
times start_time: HH:MM / end_time: HH:MM 包含起始、不包含结束(便于表示整点起止,如 17:00 到 24:00 前一刻)
weekdays 周日起、周六止,按名称(如 Sunday) 支持 <start_day>:<end_day> 含两端;可跨周边界(如 friday:monday 覆盖周五到周一)
days_of_month 从 1 开始,支持负值 -1 表示月末(1 月的 -1 即 1 月 31 日);越界会被 clamp(2 月指定 1:31 截到 28 或 29);含两端
months 月份名(大小写不敏感,如 January)或数字(1 月=1) 支持 range,含两端;例 ['1:3', 'may:august', 'december']
years 数字年份 支持 range,含两端;例 ['2020:2022', '2030']
location IANA 时区(如 Australia/Sydney) 也可用 Local(Alertmanager 所在机器本地时间)或 UTC;未提供则按 UTC

注意事项:Windows 上仅支持 Local 或 UTC,除非用 ZONEINFO 环境变量提供自定义时区库。

引自:Configuration

2.7 inhibit_rules 抑制规则

Inhibition 允许基于另一组 alert 的存在来静音某组 alert,从而建立系统/服务依赖,使中断期间只发出最相关的互连 alert。

<inhibit_rule>

一条抑制规则:当存在匹配某组 matchers 的 alert(source)时,静音匹配另一组 matchers 的 alert(target)。target 与 source 的 alert 在 equal 列表中的标签名必须有相同标签值。

name: <string>                                 # 可选规则名;允许重名但会影响 per-rule 指标
# DEPRECATED: 使用 target_matchers
target_match:
  [ <labelname>: <labelvalue>, ... ]
# DEPRECATED: 使用 target_matchers
target_match_re:
  [ <labelname>: <regex>, ... ]
target_matchers:                               # 被静音 target alert 必须满足
  [ - <matcher> ... ]
# DEPRECATED: 使用 source_matchers
source_match:
  [ <labelname>: <labelvalue>, ... ]
# DEPRECATED: 使用 source_matchers
source_match_re:
  [ <labelname>: <regex>, ... ]
source_matchers:                               # 存在一个或多个 alert 满足才生效
  [ - <matcher> ... ]
[ equal: '[' <labelname>, ... ']' ]            # source 与 target 必须有相同值的标签

语义与陷阱:

  • 缺失标签与空值标签相同。因此若 equal 中所有标签名在 source 与 target 中均缺失,抑制规则会生效。
  • 防止 alert 抑制自身:同时匹配规则 target 与 source 两侧的 alert,不能被同样匹配两侧(含自身)的 alert 抑制。建议选择 target/source matchers 使 alert 永不两侧都匹配。

引自:Configuration

2.8 Label matchers 标签匹配器

Label matchers 用于将 alert 匹配到 route、silence 与 inhibition rule。

2.8.1 UTF-8 过渡背景(重要)

Prometheus 正在为 labels 与 metrics 增加 UTF-8 支持。为支持 UTF-8,Alertmanager 0.27 及以后版本引入了新的 matcher parser,含若干不向后兼容变更。多数 matcher 前向兼容,部分不兼容。Alertmanager 处于同时支持 UTF-8 与 classic matchers 的过渡期:

  • 新安装建议在创建配置文件前启用 UTF-8 strict mode;
  • 既有安装建议先在默认的 fallback mode 运行。
  • Alertmanager 将在接下来两个版本把 UTF-8 strict mode 设为默认。
  • 也可用 amtool 校验配置是否兼容 UTF-8 strict mode(无需运行 server)。

2.8.2 三种运行模式

模式 启用方式 行为
Fallback mode(默认) 无 先按 UTF-8 matchers 解析,若不兼容再按 classic 解析;不兼容时按 classic 解析并记 warning(含改进建议)。罕见的 "disagreement"(两 parser 均 valid 但结果不同)时用 classic parser 并记 warning
UTF-8 strict mode alertmanager --config.file=config.yml --enable-feature="utf8-strict-mode" 禁用 classic matchers。新安装应启用;既有安装在所有不兼容 warning 解决后启用。未解决完之前不会启动
Classic mode alertmanager --config.file=config.yml --enable-feature="classic-mode" 等价于 Alertmanager 0.26.0 及更早版本。怀疑 fallback/UTF-8 strict 有问题时可用

2.8.3 校验(amtool check-config)

amtool check-config config.yml
amtool check-config config.yml --enable-feature="utf8-strict-mode"
  • 第一条:与 server 类似,配置不兼容或含 disagreement 时输出 warning;无 warning 即兼容 UTF-8 strict mode。
  • 第二条:附加一级校验;配置无效则命令失败,有效则成功。
  • 输出末尾会列出 Found:global config / route / N inhibit rules / N receivers / N templates。

2.8.4 <matcher>(共享)

UTF-8 matchers

由三个 token 组成:

  1. 未加引号的 literal 或双引号字符串(标签名);
  2. =、!=、=~、!~ 之一——= 相等,!= 不等,=~ 匹配正则,!~ 不匹配正则;
  3. 未加引号的 literal 或双引号字符串(正则或标签值)。
  • Unquoted literals 可含除保留字符外的所有 UTF-8 字符。保留字符包括空白及 { } ! = ~ , \ " ' \``。例:foo、[a-zA-Z]+、Προμηθεύς(希腊语 Prometheus)合法;foo! 非法(!` 为保留字符)。
  • Double-quoted strings 可含任意 UTF-8 字符,无保留字符,但字面双引号与反斜杠须用单个反斜杠转义(如匹配正则 \d+ 须写 "\\d+"),规则同 Go 字符串字面量;支持 UTF-8 code points(如 "foo!"、"bar,baz"、"\"baz qux\""、"\xf0\x9f\x99\x82")。

YAML quoting vs. matcher token quoting(易踩坑)

matchers: 列表中每个条目是单个 YAML 字符串,Alertmanager 在 YAML 处理后解析它。YAML quoting 作用于整个 matcher 字符串,不保护其中单个 token。三种 YAML 风格:

风格 示例 说明
plain style env !~ preprod 无外层引号;matcher 不含 YAML 特殊字符 `{ } [ ] , #
single-quoted style `'env =~ "prod staging"'`
double-quoted style "env !~ \"uat\"" 支持 YAML 转义序列,内部字面双引号须写 \"

关键提醒:不带外层引号的 env =~ "prod" 是合法 plain-style YAML,内部双引号被 Alertmanager parser 剥离且不提供 YAML 特殊字符保护——值可能含特殊字符时应始终在 YAML 层引用整个 matcher。

Classic matchers

语法灵感来自 PromQL/OpenMetrics,三个 token:

  1. 合法 Prometheus 标签名;
  2. = / != / =~ / !~ 之一;
  3. UTF-8 字符串(可用双引号包裹)。每 token 前后可有任意空白,第 3 个 token 可为空字符串。

其内适用 OpenMetrics 转义规则:\" 双引号、\n 换行、\\ 字面反斜杠;未转义的 " 不得出现在第 3 token 内部(仅可作首尾字符);允许字面换行符,以及未后跟 \、n、" 的单个 \(此时作字面反斜杠)。

Matchers 组合

  • 组合时所有 matcher 都须匹配。如 {alertname="Watchdog", severity=~"warning|critical"} 匹配 alertname=Watchdog, severity=critical,但不匹配 severity=none。
  • 可用 YAML list 组合,或用 PromQL 风格逗号分隔表达式。
  • 允许单个尾随逗号;{ 与 } 可选但必须同时存在或同时省略(不可只有半边);不可重复括号;双引号外的空白无影响。

引自:Configuration

2.9 receiver 与共享配置块

2.9.1 <receiver>

Receiver 是一个或多个通知集成的命名配置。

注:解除「新 receiver 禁令」时约定,除既有要求外,新通知集成须有具备 push 权限的 committed maintainer。

name: <string>                                 # 唯一名称
labels:                                        # 附加到该 receiver 用于查询与过滤
  [ <labelname>: <labelvalue>, ... ]
discord_configs: [ - <discord_config>, ... ]
email_configs: [ - <email_config>, ... ]
mattermost_configs: [ - <mattermost_config>, ... ]
msteams_configs: [ - <msteams_config>, ... ]
msteamsv2_configs: [ - <msteamsv2_config>, ... ]
jira_configs: [ - <jira_config>, ... ]
opsgenie_configs: [ - <opsgenie_config>, ... ]
pagerduty_configs: [ - <pagerduty_config>, ... ]
incidentio_configs: [ - <incidentio_config>, ... ]
pushover_configs: [ - <pushover_config>, ... ]
rocketchat_configs: [ - <rocketchat_config>, ... ]
slack_configs: [ - <slack_config>, ... ]
sns_configs: [ - <sns_config>, ... ]
telegram_configs: [ - <telegram_config>, ... ]
victorops_configs: [ - <victorops_config>, ... ]
webex_configs: [ - <webex_config>, ... ]
webhook_configs: [ - <webhook_config>, ... ]
wechat_configs: [ - <wechat_config>, ... ]

一个 receiver 可同时挂载多种集成;多个 *_configs 条目会同时触发。

2.9.2 <http_config>(共享)

http_config 配置 receiver 与 HTTP 类 API 服务通信所用的 HTTP 客户端。

# basic_auth 与 authorization 互斥
basic_auth:                                    # 用配置的用户名/密码设置 Authorization 头;password 与 password_file 互斥
  [ username: <string> ]
  [ password: <secret> ]
  [ password_file: <string> ]
authorization:                                 # 可选 Authorization 头配置
  [ type: <string> | default: Bearer ]
  [ credentials: <secret> ]                    # 与 credentials_file 互斥
  [ credentials_file: <filename> ]
oauth2:                                        # 可选 OAuth 2.0;不能与 basic_auth 或 authorization 同时用
  [ <oauth2> ]
[ enable_http2: <bool> | default: true ]
[ proxy_url: <string> ]                        # 可选代理 URL
[ no_proxy: <string> ]                         # 逗号分隔,可含 IP、CIDR、域名;IP 与域名可含端口
[ proxy_from_environment: <boolean> | default: false ]  # 使用 HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, NO_PROXY, no_proxy
[ proxy_connect_header: [ <string>: [<secret>, ...] ] ] # CONNECT 请求发给代理的 header
[ follow_redirects: <bool> | default = true ]  # 是否跟随 HTTP 3xx 重定向
tls_config:
  [ <tls_config> ]
http_headers:                                  # 每个请求携带的自定义 HTTP header;Prometheus 自身设置的 header 不可覆盖
  [ <http_header> ]

<http_header>(共享)

<string>:
    [ values: [<string>, ...] ]                # Header 值
    [ secrets: [<secret>, ...] ]               # Header 值,配置页隐藏
    [ files: [<string>, ...] ]                 # 从文件读取 header 值

<oauth2>(共享)

OAuth 2.0,使用 client credentials grant type。Alertmanager 用给定 client access/secret key 从指定 endpoint 获取 access token。

client_id: <string>
[ client_secret: <secret> ]
[ client_secret_file: <filename> ]             # 与 client_secret 互斥
scopes:
  [ - <string> ... ]
token_url: <string>                            # 获取 token 的 URL
endpoint_params:
  [ <string>: <string> ... ]                   # 附加到 token URL 的可选参数
tls_config:
  [ <tls_config> ]
[ proxy_url: <string> ]
[ no_proxy: <string> ]
[ proxy_from_environment: <boolean> | default: false ]
[ proxy_connect_header: [ <string>: [<secret>, ...] ] ]

<tls_config>(共享)

[ ca_file: <filepath> ]                        # 校验服务器证书的 CA 证书
[ cert_file: <filepath> ]                      # 客户端证书认证
[ key_file: <filepath> ]
[ server_name: <string> ]                      # ServerName 扩展
[ insecure_skip_verify: <boolean> | default = false ]
[ min_version: <string> ]                      # 接受值 TLS10/TLS11/TLS12/TLS13;未设则用 Go 默认最小版本 TLS 1.2
[ max_version: <string> ]                      # 接受值同上;未设则用 Go 默认最大版本 TLS 1.3

引自:Configuration

2.10 全部 receiver 集成配置(逐类逐字段)

本节逐类给出字段与默认值。说明:官方 <receiver> 配置块中列出的原生 receiver 集成共 18 类(discord、email、mattermost、msteams、msteamsv2、jira、opsgenie、pagerduty、incidentio、pushover、rocketchat、slack、sns、telegram、victorops、webex、webhook、wechat);另有 tracing(第 2.11 节)与 event_recorder(第 2.12 节,其内置 kafka_output 等输出机制)属于相邻功能。下文 18 类一个不少。

2.10.1 <discord_config>

Discord webhook API。

字段 类型/默认 说明
send_resolved ` default = true`
webhook_url / webhook_url_file <secret> / <filepath> 互斥
title 默认 {{ template "discord.default.title" . }} 消息标题模板
message 默认 discord.default.message 消息正文模板
content 默认 discord.default.content 限 2000 字符
username 默认 '' 覆盖 webhook 用户名
avatar_url 默认 '' 头像 URL
http_config 默认 global.http_config HTTP 客户端配置

2.10.2 <email_config>

字段 默认 说明
send_resolved false 是否发送 resolved
to — <tmpl_string>,收件地址,允许逗号分隔的 rfc5322 合规列表
from global.smtp_from 发件人
smarthost global.smtp_smarthost SMTP 服务器
hello global.smtp_hello HELO 主机名
auth_username / auth_password / auth_password_file / auth_secret / auth_secret_file / auth_identity 分别默认对应 global.smtp_* auth_password 与 auth_password_file 互斥;auth_secret 与 auth_secret_file 互斥
require_tls global.smtp_require_tls 是否要求 TLS
force_implicit_tls nil true 强制隐式 TLS(任意端口直接 TLS);nil 按端口自动检测(465=implicit,其他=explicit)
tls_config global.smtp_tls_config TLS 配置
html 默认 {{ template "email.default.html" . }} HTML 正文
text — 纯文本正文
headers { <string>: <tmpl_string>, ... } 覆盖此前设置的 header
threading enabled 默认 false;thread_by_date 默认 daily thread_by_date 接受 daily、none(none 表示按 alert group key 分组不带日期)

Email TLS 示例:

# 示例1:强制隐式 TLS
receivers:
  - name: 'email-implicit'
    email_configs:
      - to: 'oncall@example.org'
        smarthost: 'smtp.example.com:8465'
        force_implicit_tls: true

# 示例2:向后兼容——465 自动隐式、587 自动显式
receivers:
  - name: 'email-compat'
    email_configs:
      - to: 'oncall@example.org'
        smarthost: 'smtp.example.com:465'

2.10.3 <mattermost_config>

Mattermost webhook API。

字段 默认 说明
send_resolved true 是否发送 resolved
webhook_url / webhook_url_file — 互斥
channel '' 用频道名而非显示名,如 town-square
username '' 覆盖用户名
icon_url '' 图标 URL
icon_emoji '' 图标 emoji
fallback / color / title / title_link / text / pretext / author_name / author_link / author_icon / fields(<field_config> 列表)/ thumb_url / footer / footer_icon / image_url — attachments 兼容 Slack 字段(顶级字段)
attachments — Deprecated: 使用顶级字段,未来将移除
props <prop_config> RHS 面板 Markdown 卡片
priority <priority_config> 优先级配置
http_config — HTTP 客户端

<attachment_config>(Mattermost)

fallback / color / pretext / text / author_name / author_link / author_icon / title / title_link(均默认 '')、fields(同 Slack fields)、thumb_url / footer / footer_icon / image_url(均默认 '')。

<prop_config>(Mattermost)

[ card: <string> | default = '' ] —— Markdown 文本,仅在 RHS 面板显示。

<priority_config>(Mattermost)

字段 默认 说明
priority '' 可选 urgent、important、standard
requested_ack false 需 priority 为 Important/Urgent,仅企业版
persistent_notifications false 仅 Urgent,每 5 分钟持续通知直到确认,仅企业版

2.10.4 <msteams_config>

Microsoft Teams Incoming Webhooks。

DEPRECATION NOTICE:Microsoft 正在弃用 Microsoft 365 connectors,建议迁移到 Workflows + msteamsv2。

字段 默认 说明
send_resolved true 是否发送 resolved
webhook_url / webhook_url_file — 互斥
title msteams.default.title 标题
summary msteams.default.summary 摘要
text msteams.default.text 正文
http_config — HTTP 客户端

2.10.5 <msteamsv2_config>

使用新消息格式(adaptive cards),符合 flows 要求。

字段 默认 说明
send_resolved true 是否发送 resolved
webhook_url / webhook_url_file — 互斥
title msteamsv2.default.title 标题
text msteamsv2.default.text 正文
http_config — HTTP 客户端

2.10.6 <jira_config>

经 JIRA Rest API v2 或 v3。仅针对 Jira Cloud 测试;Jira Data Center(on-premise)可能可用但不保证。V2 支持 Wiki Markup,V3 支持 ADF;默认 jira.default.description 模板仅适用于 V2。

字段 默认 说明
send_resolved true 是否发送 resolved
api_url global.jira_api_url 须含完整 API 路径,例 https://company.atlassian.net/rest/api/2/
api_type auto 可为 auto/cloud/datacenter
project — <tmpl_string>
summary.template jira.default.summary 摘要模板
summary.enable_update true false 则更新 issue 时不改 summary
description.template jira.default.description 描述模板
description.enable_update true 是否更新描述
labels [ - <tmpl_string> ... ] 支持模板,如 '{{ .CommonLabels.severity }}'
priority jira.default.priority 优先级
issue_type / resolve_transition / reopen_transition / wont_fix_resolution — 工作流字段
reopen_duration — 按分钟向下取整,用 resolutiondate 判断龄期
fields [ <string>: <jira_field> ... ] 自定义字段
http_config — PAT 经 basic_auth——Jira Cloud 用邮箱作 username、PAT 作 password;或 authorization 字段——Jira Data Center 用 credentials: <PAT>;也支持 OAuth2 client_credentials;Jira 自身 OAuth2 需交互式浏览器授权,不可用于自动通知

<jira_field>

示例:

fields:
  components: { name: "Monitoring" }                # Component
  customfield_10001: "Random text"                  # TextField
  customfield_10002: {"value": "red"}               # SelectList
  customfield_10003: [{"value": "red"}, {"value": "blue"}, {"value": "green"}]  # MultiSelect

2.10.7 <opsgenie_config>

字段 默认 说明
send_resolved true 是否发送 resolved
api_key global.opsgenie_api_key 与 api_key_file 冲突
api_key_file global.opsgenie_api_key_file —
api_url global.opsgenie_api_url —
message opsgenie.default.message 限 130 字符
description opsgenie.default.description 描述
source opsgenie.default.source 来源
details { <string>: <tmpl_string>, ... } 默认含所有 common labels
responders [ - <responder> ... ] 响应者列表
tags — 逗号分隔
note — 备注
priority — P1..P5
update_alerts false 默认从不更新,新消息仅现于 activity log
entity — 实体
actions — 逗号分隔
http_config — HTTP 客户端

<responder>(OpsGenie)

id / name / username(三者恰定义其一);type: <tmpl_string>(team/teams/user/escalation/schedule 之一;teams 用 name 字段,可逗号分隔;空列表则不配置 responder)。

2.10.8 <pagerduty_config>

字段 默认 说明
send_resolved true 是否发送 resolved
routing_key <tmpl_secret> Events API v2 集成密钥,与 routing_key_file 互斥
routing_key_file — —
service_key <tmpl_secret> Prometheus 集成密钥,与 service_key_file 互斥
service_key_file — —
— — routing_key 与 service_key 互斥
url global.pagerduty_url API URL
client pagerduty.default.client 客户端名
client_url pagerduty.default.clientURL 客户端 URL
description pagerduty.default.description 描述
severity 'error' 严重级别
source 默认 client 来源
details 默认含 firing/resolved JSON、num_firing/num_resolved Events API v2 支持嵌套 k/v
images [ <image_config> ... ] 图片
links [ <link_config> ... ] 链接
component / group / class — 分类字段
http_config — HTTP 客户端
timeout 0s 0s 表示无超时;高于 group_interval 时无效

<image_config>(PagerDuty)

href: <tmpl_string>、src: <tmpl_string>、alt: <tmpl_string>。

<link_config>(PagerDuty)

href: <tmpl_string>、text: <tmpl_string>。

2.10.9 <pushover_config>

字段 默认 说明
send_resolved true 是否发送 resolved
user_key / user_key_file — 互斥
token / token_file — 互斥
title pushover.default.title 标题
message pushover.default.message 正文
url pushover.default.url 附带链接
device — 目标设备
sound — 提示音
priority {{ if eq .Status "firing" }}2{{ else }}0{{ end }} 优先级
retry 1m 至少 30 秒
expire 1h 过期
ttl — 生存时间
html false 与 monospace 互斥
monospace false 与 html 互斥
http_config — HTTP 客户端

2.10.10 <rocketchat_config>

字段 默认 说明
send_resolved false 是否发送 resolved
api_url global.rocketchat_api_url API URL
channel — <tmpl_string>
token / token_file — 互斥
token_id / token_id_file — 互斥
color {{ if eq .Status "firing" }}red{{ else }}green{{ end }} 颜色
emoji rocketchat.default.emoji emoji
icon_url rocketchat.default.iconurl 图标
text rocketchat.default.text 正文
title rocketchat.default.title 标题
title_link rocketchat.default.titlelink 标题链接
fields [ <rocketchat_field_config> ... ] 字段
image_url / thumb_url — 图片
link_names false 是否链接 @用户名
short_fields false 字段是否短显示
actions [ <rocketchat_action_config> ... ] 动作按钮

<rocketchat_field_config>

title、value、short(默认 rocketchat_config.short_fields)。

<rocketchat_action_config>

type(忽略,仅支持 button)、text、url、msg。

2.10.11 <slack_config>

可经 Incoming webhooks 或 Bot tokens。用 incoming webhook 时 api_url 须为 webhook URL(或 api_url_file);用 Bot token 时 api_url 须为 https://slack.com/api/chat.postMessage,bot token 设为 http_config 的 authorization credentials,channel 含频道名(# 可选)或 Channel ID。

字段 默认 说明
send_resolved false 是否发送 resolved
api_url / api_url_file 默认 global.* 与 app_token/app_token_file 二者择一
app_token / app_token_file 默认 global.* 与 api_url 互斥
app_url global.slack_app_url 用 app_token 认证时必需
channel — <tmpl_string>
icon_emoji slack.default.iconemoji 图标 emoji
icon_url slack.default.iconurl 图标 URL
link_names false 是否链接 @用户名
message_text — 作为顶层 text 字段发送,便于兼容 Slack Workflow Webhooks
username slack.default.username 用户名
actions [ <action_config> ... ] 动作按钮
callback_id — 回调 ID
color — 颜色
fallback — 回退文本
fields [ <field_config> ... ] 字段
footer — 页脚
mrkdwn_in 默认 ["fallback", "pretext", "text"] 启用 markdown 的字段
pretext — 前置文本
short_fields false 字段短显示
text — 正文
title / title_link — 标题与链接
image_url / thumb_url — 图片
http_config — HTTP 客户端
timeout 0s 0s 无超时;高于 group_interval 时无效
update_message false 更新既有消息而非新建;webhook URL 不支持
post_updates_to_thread false 后续通知作为 thread 回复;与 update_message 结合时原地更新并回复;webhook URL 不支持 thread

<action_config>(Slack)

text、type(必填);url 或 name+value 二者之一;confirm(<action_confirm_field_config>)、style(默认 '')。

<action_confirm_field_config>(Slack)

text(必填)、dismiss_text、ok_text、title(默认 '')。

<field_config>(Slack)

title、value(必填)、short(默认 slack_config.short_fields)。

2.10.12 <sns_config>

字段 默认 说明
send_resolved true 是否发送 resolved
api_url <tmpl_string> 未指定则用 SNS SDK 的
sigv4 [ <sigv4_config> ] AWS 签名配置
topic_arn — 不指定则须给 phone_number 或 target_arn;FIFO topic 建议 message group interval > 5 分钟
subject sns.default.subject 主题
phone_number — E.164 格式
target_arn — 目标 ARN
message sns.default.message 消息正文
attributes [ <string>: <string> ... ] 消息属性
http_config — HTTP 客户端
use_aws_http_client false 强制用 AWS SDK 的 BuildableClient 而非默认 tracing-wrapped client;AWS_CA_BUNDLE 环境变量设置时自动启用;启用后 SNS 请求禁用 tracing,仅 tls_config 与代理字段生效,其他 http_config 项被静默忽略

<sigv4_config>(SNS)

region、access_key+secret_key(须同时提供或同时留空,留空则用 AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)、profile、role_arn、external_id(仅与 role_arn 同用)。

2.10.13 <telegram_config>

字段 默认 说明
send_resolved true 是否发送 resolved
api_url global.telegram_api_url API URL
bot_token / bot_token_file — 互斥
chat_id(<int>)/ chat_id_file — 互斥
message_thread_id <int> 话题 ID
message telegram.default.message 正文
disable_notifications false 静默发送
parse_mode "HTML" 支持 MarkdownV2/Markdown/HTML/空串;超字符上限会被截断,HTML 模式下替换为 fallback 消息
http_config — HTTP 客户端
timeout 0s 超时

2.10.14 <victorops_config>

Splunk On-Call(原 VictorOps),经 Splunk On-Call REST endpoint 集成。名称与 victorops_* 全局字段保留以向后兼容;metrics/logs/traces/校验错误也继续用 VictorOps 标识。

字段 默认 说明
send_resolved true 是否发送 resolved
api_key global.victorops_api_key 与 api_key_file 互斥
api_key_file — —
api_url global.victorops_api_url API URL
routing_key <tmpl_string> 映射 alert 到 team
message_type 'CRITICAL' 可 CRITICAL/WARNING/INFO
entity_display_name victorops.default.entity_display_name 实体显示名
state_message victorops.default.state_message 状态消息
monitoring_tool victorops.default.monitoring_tool 监控工具名
custom_fields { <string>: <tmpl_string>, ... } 保留名不可作 key:routing_key、message_type、state_message、entity_display_name、monitoring_tool、entity_id、entity_state
http_config — HTTP 客户端

2.10.15 <webhook_config>

字段 默认 说明
send_resolved true 是否发送 resolved
url / url_file <secret> / <filepath> 互斥
http_config — HTTP 客户端
max_alerts 0 超出截断;0 表示全部包含
timeout 0s 超时
payload { <string>: <tmpl_string>, ... } 高级选项,自定义 Go 模板 payload,须为合法 JSON,可用 toJson;Alertmanager 不做校验,官方不提供支持

Webhook JSON 载荷格式(version "4"):

{
  "version": "4",
  "groupKey": "<string>",
  "truncatedAlerts": 0,
  "status": "firing",
  "receiver": "<string>",
  "groupLabels": { "<labelname>": "<labelvalue>" },
  "commonLabels": { "<labelname>": "<labelvalue>" },
  "commonAnnotations": { "<labelname>": "<labelvalue>" },
  "externalURL": "<string>",
  "notification_reason": "<string>",
  "alerts": [
    {
      "status": "firing",
      "labels": { "<labelname>": "<labelvalue>" },
      "annotations": { "<labelname>": "<labelvalue>" },
      "startsAt": "<rfc3339>",
      "endsAt": "<rfc3339>",
      "generatorURL": "<string>",
      "fingerprint": "<string>"
    }
  ]
}

💡 补充:version 字段用于载荷演进的兼容判断;truncatedAlerts 表示因 max_alerts 被截断的告警数量;status 取值 resolved 或 firing。自定义 payload 时建议保留 version,便于下游按版本解析。

2.10.16 <incidentio_config>

经 incident.io Alert Sources API。可经 http_config 的 authorization,或用 alert_source_token/alert_source_token_file(后者优先于 http_config)。载荷超 512KB 时会自动截断为仅保留第一个 alert。

字段 默认 说明
send_resolved true 是否发送 resolved
http_config — HTTP 客户端
url / url_file — 互斥
alert_source_token / alert_source_token_file — 互斥
max_alerts 0 0 不限;超 512KB 自动丢弃除首个外的所有 alert;截断后仍过大则收到 429 且 alert 不被摄取
timeout 0s 超时
metadata { <string>: <tmpl_string>, ... } 元数据

2.10.17 <wechat_config>

字段 默认 说明
send_resolved false 是否发送 resolved
api_secret / api_secret_file 默认 global.wechat_api_secret* 二者择一
api_url global.wechat_api_url API URL
corp_id global.wechat_api_corp_id 企业 ID
message wechat.default.message 正文
message_type 'text' 支持 text/markdown
agent_id 默认 wechat.default.* 应用 ID
to_user 默认 wechat.default.* 目标用户
to_party 默认 wechat.default.* 目标部门
to_tag 默认 wechat.default.* 目标标签

2.10.18 <webex_config>

字段 默认 说明
send_resolved true 是否发送 resolved
api_url global.webex_api_url API URL
room_id <tmpl_string> 必填
message webex.default.message 正文
http_config — 须用它提供 bot token 作为 HTTP Authorization 头

receiver 字段速查表(按集成列关键字段):

集成 配置键 send_resolved 默认 核心必填/认证
Email email_configs false to + smarthost
Webhook webhook_configs true url
PagerDuty pagerduty_configs true routing_key 或 service_key
Slack slack_configs false api_url/app_token + channel
OpsGenie opsgenie_configs true api_key
Splunk On-Call (VictorOps) victorops_configs true api_key + routing_key
Webhook (WeChat) wechat_configs false api_secret + corp_id + agent_id + to_*
Amazon SNS sns_configs true topic_arn 或 phone_number/target_arn
Telegram telegram_configs true bot_token + chat_id
Discord discord_configs true webhook_url
Microsoft Teams msteams_configs true webhook_url
Microsoft Teams v2 msteamsv2_configs true webhook_url
Webex webex_configs true room_id + bot token
Rocket.Chat rocketchat_configs false token + token_id
Pushover pushover_configs true user_key + token
Mattermost mattermost_configs true webhook_url
Jira jira_configs true project + api_url
incident.io incidentio_configs true url + alert_source_token

引自:Configuration

2.11 tracing_config 分布式追踪

[ client_type: <string> | default = "grpc" ]   # 支持 http / grpc
[ endpoint: <string> | default = "" ]
[ sampling_fraction: <float> | default = 0.0 ]
[ insecure: <boolean> | default = false ]
[ tls_config: <tls_config> ]
[ http_headers: [ <http_header> ] ]
[ compression: <string> | default = "gzip" ]
[ timeout: <duration> | default = 0s ]

引自:Configuration

2.12 event_recorder 事件记录器

捕获重要 Alertmanager 事件(进程启停、alert 生命周期转换、silence 创建、通知投递、mute/inhibit 抑制),扇出到一个或多个目的地。每个事件编码为结构化载荷,含时间戳、产生实例 hostname、cluster position(启用 HA 集群时)及事件特定数据。

  • 受 event-recorder feature flag 控制(--enable-feature=event-recorder);未设 flag 时静默丢弃所有事件。
  • 配置在顶级 event_recorder 键下。

<event_recorder_config>

输出按类型分组,每种目的地一个 list(镜像 receiver 组织集成的方式)。每个记录事件会发往所有 list 中的所有 output。每个 output 需 name,与 type 一起作为 metrics 与 logs 中的标识(如 webhook:primary)。路径/URL/broker/topic 等目的地配置不进入 metric labels;URL/broker/topic 也从 logs 省略,文件路径保留在 file-output 错误日志中。name 须非空、合法 UTF-8 且在每种 output type 内唯一。

file_outputs: [ - <file_output> ... ]
webhook_outputs: [ - <webhook_output> ... ]
kafka_outputs: [ - <kafka_output> ... ]
stdout_outputs: [ - <stdout_output> ... ]

每个 output 使用 proto/eventrecorder/events/v2/events.proto 的 schema。Alert labels、alert annotations、group labels、silence annotations、muted-alert labels 编码为 maps。Protobuf 消费者须用 Go 包 github.com/prometheus/alertmanager/eventrecorder/events/v2 或据该 schema 生成的绑定。

<file_output>

每个事件作为单行 JSON 写入文件;父目录观察到目标路径 rename/remove/create 时会重开文件(兼容 logrotate)。

字段 必填 说明
name 是 output 名称
path 是 目标路径,不存在时创建

<webhook_output>

将每个事件作为 JSON body POST 到 HTTP 端点。经有界 worker pool + 有界重试 + 指数退避投递。重试会重发整个事件或批次,接收端应容忍重复。多 worker 时请求可能乱序,需有序时设 workers: 1。

字段 默认 说明
name 必填 output 名称
url 必填 <secret> 目标 URL
http_config — HTTP 客户端
timeout 10s 超时
workers 4 并发 worker 数
max_retries 3 最大重试次数
retry_backoff 500ms 后续退避为 base * 2^attempt,上限 30s
batch false 以 JSON 数组发送,改变 payload 契约,仅在接收端接受数组时启用
batch_max_events 100 批最大事件数
batch_max_bytes 1048576 软上限;单个超限事件单独发送
batch_flush_interval 100ms 批刷新间隔

示例:Cloudflare Pipelines streams 接受 JSON 数组,可配置为批量 webhook output;event timestamp 字段名为 @timestamp,在 pipeline SQL 引用时需加引号;完整扇出示例见 doc/examples/cloudflare-pipelines。

<kafka_output>

将每个事件 produce 到 Kafka topic。记录以产生实例 hostname 作 message key(同一实例的事件落同一 partition 并保序)。投递异步且有界:本地 buffer 满时丢事件。启动时无法连 broker 记 warn 但不阻止 Alertmanager 启动(底层 client 后台重试)。目标 topic 须已存在(或 broker 配置了 auto-create),Alertmanager 不创建它。

字段 默认 说明
name 必填 output 名称
brokers 必填(至少一项) [ - <string> ... ],host:port
topic 必填 目标 topic
client_id "alertmanager" 客户端 ID
format "json" json(protojson)/ protobuf(二进制 proto)
acks "leader" none / leader / all
compression 省略则不压缩 none / gzip / snappy / lz4 / zstd
buffer_size 1024 满则丢事件
tls_config — 未设则 PLAINTEXT

<stdout_output>

每个事件作为单行 JSON 写 stdout,容器部署推荐(运行时日志驱动自动捕获)。

字段 必填 说明
name 是 output 名称

💡 补充:使用 stdout_outputs 时建议同时传 --log.format=json,否则 Alertmanager 自身日志用 logfmt 而事件记录为 JSON,同一流出现两种格式,可能增加下游解析复杂度。

引自:Configuration

2.13 配置验证与 reload

操作 方式 说明
查看全部 flag alertmanager -h 命令行参数参考
校验配置 amtool check-config config.yml 输出 warning 与 Found 统计;无 warning 即兼容 UTF-8 strict mode
严格校验 amtool check-config config.yml --enable-feature="utf8-strict-mode" 无效则命令失败
触发重载 kill -HUP <pid> 或 POST /-/reload 配置格式错误则变更不生效并记日志
编辑器 routing-tree-editor 可视化构建 routing tree

一个最小可用的 alertmanager.yml 结构:

global:
  resolve_timeout: 5m

route:
  receiver: 'default-receiver'
  group_by: [alertname]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

receivers:
  - name: 'default-receiver'
    webhook_configs:
      - url: 'http://localhost:5001/'

inhibit_rules:
  - source_matchers:
      - severity="critical"
    target_matchers:
      - severity="warning"
    equal: [alertname, cluster, service]

templates:
  - 'templates/*.tmpl'

time_intervals:
  - name: offhours
    time_intervals:
      - weekdays: ['saturday', 'sunday']
      - times:
          - start_time: '00:00'
            end_time: '08:00'

(未启用的 mute_time_intervals 顶层键已废弃,应改用 time_intervals。)

引自:Configuration

第 3 章 通知路由与模板

Prometheus 创建并发送告警到 Alertmanager,Alertmanager 再根据 labels 把通知发送到不同 receivers。receiver 可以是多种集成之一:Slack、PagerDuty、email,或经通用 webhook 接口的自定义集成。

发送给 receiver 的通知由 templates 构造。Alertmanager 自带 default templates(仓库 template/default.tmpl),也可自定义。

重要提醒:Alertmanager 模板不同于 Prometheus 的 templating(后者也包含 alert rule 的 labels/annotations 模板)。Alertmanager 通知模板基于 Go templating;注意部分字段按 text 求值、部分按 HTML 求值,会影响转义。

引自:Notification template reference

3.1 模板基础:Go template 语法要点

Alertmanager 通知模板基于 Go 标准库 text/template,在部分按 HTML 求值的字段中会走 html/template(影响转义)。

常用语法速查:

语法 含义
{{ .Field }} 输出当前上下文的字段(. 为根数据)
{{ .Labels.alertname }} 访问 map 的具体 key(也可用 index .Labels "alertname")
{{ range .Alerts }} ... {{ end }} 遍历列表,. 在循环体内指向当前元素
{{ if eq .Status "firing" }}A{{ else }}B{{ end }} 条件判断;eq/ne/lt/gt 等为比较函数
{{ template "name" . }} 引用已定义(define)的命名模板,. 为传入上下文
{{ define "name" }} ... {{ end }} 定义命名模板(通常放外部模板文件)
{{ .Annotations.summary | toUpper }} 管道,把左侧结果作为右侧函数的最后一个参数
{{/* 注释 */}} 模板注释

要点与注意事项:

  • 两种模板语境:在 Prometheus 的 alert rule 中定义 labels/annotations 时用 Prometheus 模板(如 {{ $labels.instance }});在 Alertmanager receiver 配置中引用通知内容时用 Alertmanager 模板(如 {{ .CommonAnnotations.summary }})。原文强调:annotation/label 字段处应用 Prometheus 模板,receiver 处应用 Alertmanager 模板。
  • 转义差异:部分字段按 text、部分按 HTML 求值。在 HTML 邮件等场景,若不希望自动转义可用 safeHtml / safeUrl。
  • 多行模板:跨多行的复杂模板应放到外部文件并 define,再在配置中用 {{ template "xxx" . }} 引用,避免 YAML 中的可读性问题。
  • 外部模板加载:通过顶层 templates: 提供文件路径(最后一段可用通配符,如 templates/*.tmpl)。

引自:Notification template reference | Notification template examples

3.2 通知模板数据变量

3.2.1 Data 结构

Data 是传给通知模板与 webhook push 的根结构:

字段 类型 说明
Receiver string 通知将发送到的 receiver 名称(slack、email 等)
Status string 至少一个告警 firing 则为 firing,否则为 resolved
Alerts [Alert] 本组所有告警对象列表
GroupLabels KV 这些告警被分组所用的 labels
CommonLabels KV 所有告警共有的 labels
CommonAnnotations KV 所有告警共有的 annotations 集合,用于较长的附加信息
RouteLabels KV 附加到本告警组的 route labels,通常经 routeLabels 函数访问
ExternalURL string 发送该通知的 Alertmanager 回链

Alerts 类型还暴露两个过滤函数:

  • Alerts.Firing:返回本组当前 firing 的告警对象列表;
  • Alerts.Resolved:返回本组 resolved 的告警对象列表。

模板中可写 {{ range .Alerts.Firing }} 只遍历 firing 告警,{{ range .Alerts.Resolved }} 只遍历 resolved 告警。

3.2.2 Alert 结构

字段 类型 说明
Status string resolved 还是 firing
Labels KV 标签集合
Annotations KV 注解集合
StartsAt time.Time 告警触发时间;若省略由 Alertmanager 赋当前时间
EndsAt time.Time 仅当结束时间已知才设置,否则设为「自最后收到告警起可配置的超时」
GeneratorURL string 标识告警致因实体的回链
Fingerprint string 可用于识别告警的指纹

3.2.3 KV

KV 是 type KV map[string]string,表示 labels 与 annotations 的 key/value 字符串对。annotation 常见键含 summary、description。

KV methods:

方法 签名 说明
SortedPairs → Pairs 返回排序后的 key/value 对列表
Remove []string → KV 返回去掉给定 keys 的 key/value map 副本
Names → []string 返回 LabelSet 中 label 名的名字列表
Values → []string 返回 LabelSet 中值的列表

Remove 需要 string slice;模板中移除字面 key 需用 stringSlice 函数:

{{ .Annotations.Remove (stringSlice "summary" "description" "runbook_url") }}

引自:Notification template reference

3.3 模板函数

除下表外,Go 模板默认函数亦可使用。

函数 参数 说明
append (slice []any, args ...any) 返回把参数追加到 slice 后的新 slice
base64decode (text string) base64.URLEncoding.DecodeString,解码 URL-safe base64 字符串
base64encode (text string) base64.URLEncoding.EncodeToString,返回 URL-safe base64 编码,如用于 query parameter
date (string, time.Time) 按指定格式返回时间文本表示(格式见 pkg.go.dev/time)
dict (values ...any) 返回 string→any 的 map,由变参 key-value 构成;参数个数须为偶数、key 须为 string
humanizeDuration (number or string) 返回人类可读的时长字符串
join (sep string, v any) 用 sep 连接 slice/array 元素;元素可为任意类型;若第二个参数非 slice/array 则报错;参数顺序与 strings.Join 相反以支持管道
list (...any) 把传入参数作为 interface slice 返回
match (pattern, string) Regexp.MatchString,用 Regexp 匹配字符串
now 无参 time.Now,返回当前本地时间
reReplaceAll (pattern, replacement, text) Regexp.ReplaceAllString,非锚定的正则替换
routeLabels (name string) 返回本告警组指定名称的 route label 值,未设置则返回 ""
safeHtml (text string) html/template.HTML,标记字符串为无需自动转义的 HTML
safeUrl (text string) html/template.URL,标记字符串为无需自动转义的 URL
since (time.Time) time.Since,返回从给定时间到当前系统时间的时长
stringSlice (...string) 把传入字符串作为 string slice 返回
title (string) strings.Title,将每个单词首字母大写
toJson (any) json.Marshal,返回值的 JSON 编码
toLower (string) strings.ToLower,全部转小写
toUpper (string) strings.ToUpper,全部转大写
trimSpace (string) strings.TrimSpace,去除首尾空白
tz (string, time.Time) 返回指定时区(如 Europe/Paris)的时间
urlUnescape (text string) url.QueryUnescape,解转义含 % 编码的 URL

常用组合示例:

{{ .Status | toUpper }}                                  {{/* FIRING / RESOLVED */}}
{{ .StartsAt | since | humanizeDuration }}               {{/* 已持续多久 */}}
{{ .StartsAt | tz "Asia/Shanghai" | date "2006-01-02 15:04:05" }}
{{ .CommonLabels | toJson }}                             {{/* 便于排障 */}}
{{ if eq .Status "firing" }}{{ .Alerts.Firing | len }}{{ end }}

引自:Notification template reference

3.4 官方模板示例逐个解析

以下示例均使用 Go templating,来自官方 notification_examples 页。

3.4.1 Customizing Slack notifications

自定义 Slack 通知,发送指向组织 wiki 的 URL。

global:
  slack_api_url: '<slack_webhook_url>'
  # 也可放入文件:slack_api_url_file: '/etc/alertmanager/slack_url'

route:
  receiver: 'slack-notifications'
  group_by: [alertname, datacenter, app]

receivers:
- name: 'slack-notifications'
  slack_configs:
  - channel: '#alerts'
    text: 'https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}'

解析:slack_api_url 可直填或经 slack_api_url_file 指向文件(避免明文密钥入库)。text 中通过 .GroupLabels.app / .GroupLabels.alertname 取到分组标签,拼出 wiki 链接。

3.4.2 Accessing annotations in CommonAnnotations

Alert 侧用 Prometheus 模板,Receiver 侧用 Alertmanager 模板。

Prometheus 侧告警规则:

groups:
- name: example
  rules:
  - alert: InstanceDown
    expr: up == 0
    for: 5m
    labels:
      severity: page
    annotations:
      description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes.'
      summary: 'Instance {{ $labels.instance }} down'

Receiver 侧:

receivers:
- name: 'default-receiver'
  slack_configs:
  - channel: '#alerts'
    text: "<!channel> \nsummary: {{ .CommonAnnotations.summary }}\ndescription: {{ .CommonAnnotations.description }}"

解析:.CommonAnnotations.summary 取的是所有告警共有的注解。若各组告警的 summary 不同,则不会出现在 CommonAnnotations 中(需改用 range .Alerts 逐条渲染)。

3.4.3 Ranging over all received Alerts

用 {{ range .Alerts }} 遍历所有收到的告警,分别打印各自 annotation。

receivers:
- name: 'default-receiver'
  slack_configs:
  - channel: '#alerts'
    title: "{{ range .Alerts }}{{ .Annotations.summary }}\n{{ end }}"
    text: "{{ range .Alerts }}{{ .Annotations.description }}\n{{ end }}"

解析:循环体内 . 指向单条 Alert,故用 .Annotations.summary(而非 .CommonAnnotations)。\n 用于每条之间换行。

3.4.4 Defining reusable templates

把命名模板放外部文件,由 Alertmanager 加载,避免跨多行的复杂模板。

外部文件 /alertmanager/template/myorg.tmpl:

{{ define "slack.myorg.text" }}
{{- range .Alerts }}
{{ .Annotations.summary }}
{{ end }}
{{- end }}

配置引用:

templates:
- '/etc/alertmanager/templates/myorg.tmpl'

receivers:
- name: 'slack-notifications'
  slack_configs:
  - channel: '#alerts'
    text: '{{ template "slack.myorg.text" . }}'

解析:define "slack.myorg.text" 定义了一个命名模板,{{ template "slack.myorg.text" . }} 把当前 Data 作为上下文传入。

3.4.5 Defining a reusable HTML email template

邮件通知的 HTML body 可用外部文件的命名模板。

外部文件 /etc/alertmanager/templates/email.tmpl:

{{ define "email.myorg.html" }}
<table>
{{ range .Alerts }}
<tr>
  <td>{{ .Status | toUpper }}</td>
  <td>{{ index .Labels "alertname" }}</td>
  <td>{{ index .Annotations "summary" }}</td>
</tr>
{{ end }}
</table>
{{ end }}

配置:

global:
  smtp_smarthost: 'smtp.example.org:587'
  smtp_from: 'alertmanager@example.org'

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

receivers:
- name: 'email'
  email_configs:
  - to: 'oncall@example.org'
    html: '{{ template "email.myorg.html" . }}'

解析:.Status | toUpper、range .Alerts、index .Labels "alertname"、index .Annotations "summary" 组合渲染 HTML 表格。templates 用通配符 *.tmpl 一次加载目录下所有模板。SMTP 认证与 TLS 选项参见 <email_config>。

3.4.6 Sending notifications to Gotify with a custom webhook payload

Gotify(https://gotify.net)无专用 receiver,但其 push message API 是简单 JSON POST,可直接用通用 webhook_config 及其 payload 字段驱动。

receivers:
- name: 'gotify'
  webhook_configs:
  - url: 'https://gotify.example.com/message'
    http_config:
      authorization:
        credentials_file: /etc/alertmanager/secrets/gotify-secret/token
    payload:
      title: '{{ template "gotify.default.title" . }}'
      priority: '{{ if eq .Status "firing" }}5{{ else }}0{{ end }}'
      message: '{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}'

解析:payload 为高级选项,须为合法 JSON,可用 toJson;Alertmanager 不做校验、官方不提供支持。priority 用 if eq .Status "firing" 在 firing/resolved 间切换。更完整版本(含 Markdown 格式与 firing/resolved 分区)见仓库 examples/webhook/gotify.yml。

引自:Notification template examples

3.5 各渠道通知样式与配置示例

各集成默认模板(template/default.tmpl)为通知提供开箱样式。下表汇总常见渠道的关键渲染字段与可定制点;具体字段见第 2.10 节对应 <*_config>。

渠道 主要渲染字段 样式特点 常用定制
Email html、text、headers、threading 支持 HTML 表格、纯文本、线程化 用外部命名模板渲染 email.myorg.html
Slack text、title、title_link、fields、actions、mrkdwn_in 支持 markdown、附件字段、交互按钮、thread update_message、post_updates_to_thread
PagerDuty description、details、severity、links、images 事件制 on-call routing_key/service_key、timeout
OpsGenie message、description、details、responders、priority 团队/值班路由 update_alerts、actions
Splunk On-Call message_type、entity_display_name、state_message、custom_fields 状态式告警 routing_key 映射 team
Discord title、message、content、username、avatar_url webhook 富文本 content 限 2000 字符
Telegram message、parse_mode 支持 HTML/Markdown parse_mode 超限截断
Rocket.Chat text、title、fields、actions、color 附件式卡片 short_fields
Mattermost 顶级 attachment 字段、props、priority 兼容 Slack 附件 企业版 priority
Microsoft Teams title、summary、text Incoming Webhooks(将弃用) 迁移 msteamsv2
Microsoft Teams v2 title、text adaptive cards / Workflows 新消息格式
Webex room_id、message bot token 认证 必填 room_id
Pushover title、message、url、priority 移动推送 retry/expire/ttl
Webhook payload(自定义 JSON) 通用 JSON v4 载荷 任意下游对接
Amazon SNS subject、message、attributes AWS 消息 sigv4、topic_arn
Jira project、summary、description、labels 工单系统 resolve_transition
incident.io metadata、url 事件管理 512KB 自动截断
WeChat message、message_type、to_user/to_party/to_tag 企业微信 text/markdown

通用可复用片段(自定义模板):

{{ define "myorg.common.header" }}
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}]
{{- range .Alerts }}
  - {{ .Labels.alertname }} ({{ .Labels.severity }}): {{ .Annotations.summary }}
{{- end }}
{{ end }}

引自:Notification template reference | Configuration

3.6 集成清单(Integrations)

Alertmanager 通过配置文件支持多种通知集成。官方 Available Integrations 清单如下(集成名 → 对应配置字段):

# 集成 配置字段
1 Amazon SNS sns_config
2 Discord discord_config
3 Email email_config
4 incident.io incidentio_config
5 Jira jira_config
6 Mattermost mattermost_config
7 Microsoft Teams msteams_config
8 Microsoft Teams v2 msteamsv2_config
9 OpsGenie opsgenie_config
10 PagerDuty pagerduty_config
11 Pushover pushover_config
12 Rocket.Chat rocketchat_config
13 Slack slack_config
14 Telegram telegram_config
15 Splunk On-Call(formerly VictorOps) victorops_config
16 Webex webex_config
17 Webhook webhook_config
18 WeChat(企业微信 / WeChat Work) wechat_config

对接方式(External Configuration / API Reference 摘要):

集成 对接要点
Discord Discord Webhooks("Intro to Webhooks")
Slack Incoming Webhooks 或 Bot Tokens(chat.postMessage)
Microsoft Teams Incoming Webhooks(官方标注 Deprecated)
Microsoft Teams v2 Workflows for Teams / Power Automate Flows
Email SMTP(smtp_* 全局项 + email_config)
PagerDuty Events API v2(routing_key)或 Prometheus 集成(service_key)
OpsGenie Alert API(api_key)
Splunk On-Call Splunk On-Call REST endpoint(原 VictorOps)
Webex webexapis.com/v1/messages(bot token)
Rocket.Chat Rocket.Chat REST API(token + token_id)
Pushover Pushover API(user_key + token)
Telegram Telegram Bot API(bot_token + chat_id)
Jira JIRA REST API v2/v3(PAT 或 OAuth2 client_credentials)
incident.io Alert Sources API(alert_source_token)
Amazon SNS AWS SNS(topic_arn / phone_number / target_arn,SigV4)
WeChat 企业微信 API(corp_id + agent_id + api_secret)

扩展方式:对 Alertmanager 原生不支持的机制,可用 webhook receiver 集成;不完整列表见 Prometheus docs/operating/integrations/#alertmanager-webhook-receiver(例如 Gotify 即属此类)。

引自:Notification Integrations

第 4 章 高可用与告警运维 API

单实例 Alertmanager 是单点。本章讲清三件事:如何用 gossip 组建高可用集群、告警在集群中如何被去重与转发(含脑裂下的行为),以及运维所需的 API、HTTPS 认证与生产部署要点。

4.1 HA 设计目标

Alertmanager 的高可用设计遵循三条核心原则:

原则 英文 含义
单窗视图与管理 Single pane view and management Silences 和 alerts 可从任意集群成员查看与管理,提供统一操作体验
脑裂下「失败开放」 Survive cluster split-brain with "fail open" 网络分区期间,Alertmanager 宁可发送重复通知也不漏掉关键告警
至少一次投递 At-least-once delivery 保证通知至少投递一次,与 fail-open 理念一致

这些目标让系统优先保证运行可靠性与告警投递,而非严格的 exactly-once 语义。

引自:High Availability

4.2 gossip / Memberlist 集群机制

4.2.1 架构总览

集群由多个 Alertmanager 实例组成,通过 gossip protocol 通信。每个实例:

  • 独立从 Prometheus servers 接收 alerts;
  • 参与 peer-to-peer gossip mesh;
  • 将状态(silences 与 notification log)复制到其他集群成员;
  • 独立处理并发送通知。

节点在集群中有 position(位置)概念,例如 AM-1 (pos: 0)、AM-2 (pos: 1)、AM-3 (pos: 2)。

4.2.2 Gossip 协议

使用 Hashicorp's Memberlist 库实现基于 gossip 的通信。

成员管理(Membership Management):

机制 说明
Automatic peer discovery 可配置已知 peer 列表,实例会自动发现其他集群成员
Health checking 定期探针检测失效成员(默认:每 1 秒)
Failure detection 失效成员被标记,并可尝试重新加入

状态复制(State Replication):gossip 层复制三类状态:

  1. Silences —— create/update/delete 操作广播到所有 peers;
  2. Notification log —— 记录已发送过哪些通知以去重;
  3. Membership changes —— join、leave、failure 事件。

状态为最终一致(eventually consistent):给定足够时间与网络连通性,所有成员将收敛到同一状态。

Gossip Settling(收敛等待):实例启动或重新加入集群时,会等待 gossip "settle" 之后再处理通知,避免基于不完整状态发通知。收敛算法等待条件(满足其一):

  • peer 数量连续 3 次检查保持稳定(3 consecutive checks,默认检查间隔 push-pull interval);
  • 或发生超时(可通过 context 配置)。

此期间实例仍会接收并存储 alerts,但推迟通知处理。

引自:High Availability

4.3 HA 模式下的通知流水线

HA 模式下的通知流水线分两个阶段。

Dispatcher Stage(分发阶段):

  1. Find matching route(s)
  2. Find/create aggregation group within route
  3. Throttle by group wait or group interval

Notifier Stage(通知阶段):

  1. Wait for HA gossip to settle(确保状态完整)
  2. Filter inhibited alerts
  3. Filter non-time-active alerts
  4. Filter time-muted alerts
  5. Filter silenced alerts(使用复制的 silences)
  6. Wait according to HA cluster peer index(错峰通知)
  7. Dedupe by repeat interval/HA state(使用 notification log)
  8. Notify & retry intermittent failures
  9. Update notification log(复制到 peers)
Alert ──► [Dispatcher]  匹配 route ─► 建/取聚合组 ─► group_wait/group_interval 节流
                                                                       │
                                                                       ▼
          [Notifier]  等 gossip 收敛 ─► 过滤(抑制/时段/静默) ─► 按 peer position 错峰
                       ─► 用 nflog 去重 ─► 发送+重试 ─► 更新并复制 notification log

1. Gossip Settling Wait:某 group 的首次通知前,实例等待 gossip 收敛,以确保 silences 已完成复制、notification log 含其他实例的近期发送记录、集群成员关系稳定。实现:peer.WaitReady(ctx)。

2. Peer Position-Based Wait(基于 peer 位置的等待):为防止所有成员同时发送通知,每个实例按其排序后 peer 列表中的位置等待:

wait_time = peer_position × peer_timeout

例:3 个实例、peer_timeout 为 15 秒时:

实例 position 等待
am-1 0 0 秒
am-2 1 15 秒
am-3 2 30 秒

错峰的目的:让第一个实例先发送;后续实例能看到 notification log 条目;去重防止重复发送。实现:clusterWait() in cmd/alertmanager/main.go:594。

position 由对所有 peer name 按字母序排序决定:

func (p *Peer) Position() int {
    all := p.mlist.Members()
    sort.Slice(all, func(i, j int) bool {
        return all[i].Name < all[j].Name
    })
    // Find position of self in sorted list
}

引自:High Availability

4.4 去重与告警转发逻辑

3. 通过 Notification Log 去重:DedupStage 查询 notification log 决定是否发送:

// Check notification log for recent sends
entry := nflog.Query(receiver, groupKey)
if entry.exists && !shouldNotify(entry, alerts, repeatInterval) {
    // Skip: already notified recently
    return nil
}

去重判断内容:

判断 结果
Firing alerts changed? 是 → 通知
Resolved alerts changed? 是 且 send_resolved: true → 通知
Repeat interval elapsed? 是 → 通知
Otherwise 跳过通知(被去重)

notification log 通过 gossip 复制,所有集群成员共享同一发送历史。

转发逻辑一句话总结:Prometheus 把同一条告警推给每个 Alertmanager 实例;实例各自按 route 建组、过滤,然后靠「错峰等待 + 共享 nflog」做到只有 position 最小的那个实例真正发通知,其余实例跳过。

引自:High Availability

4.5 脑裂处理(Fail Open)

网络分区时集群可能分裂为多个无法通信的分组;"fail open" 设计确保告警仍被投递。

分区期间的行为:

分区 成员 行为
Partition A 仅 AM-1 AM-1 视自己为 position 0 → 等待 0 × timeout = 0 秒 → 发送通知(无来自 AM-2/AM-3 的去重)
Partition B AM-2、AM-3 AM-2 为 position 0、AM-3 为 position 1;AM-2 等待 0 秒发送;AM-3 看到 AM-2 的 notification log 条目后去重

结果:发送重复通知(Partition A 一条、Partition B 一条)——这是有意为之,Alertmanager 宁可重复也不漏告警。

分区恢复后:

  1. gossip 协议重新发现所有 peers;
  2. notification logs 合并(via CRDT-like merge with timestamp,按时间戳的类 CRDT 合并);
  3. 后续通知在所有实例间正确去重;
  4. 任一分区中创建的 silences 都会复制到所有 peers。

引自:High Availability

4.6 HA 中的 Silence 管理

Silences 是集群中的一等(first-class)复制状态。

创建/更新 silence 时:

  1. Local storage —— 存入本地 state map;
  2. Broadcast —— 序列化(protobuf)后经 gossip 广播;
  3. Merge on receive —— 其他实例接收并合并:
// Merge logic: last-write-wins based on UpdatedAt timestamp
if !exists || incoming.UpdatedAt > existing.UpdatedAt {
    accept_update()
}
  1. Indexing —— 更新 silence matcher cache 以加速 alert 匹配。

Silence 的过期字段:

字段 含义
StartsAt、EndsAt 生效时间范围
ExpiresAt 何时被垃圾回收(EndsAt + retention period)
UpdatedAt 合并时用于冲突解决

每个实例独立:按当前时间评估 silence 状态(pending/active/expired);对超过保留期的 silence 做 GC;GC 仅本地执行(不走 gossip),因为所有实例会收敛到同一决策。

Single Pane of Glass:

  • View silences —— 所有实例的 silence 状态相同(最终一致);
  • Create/update silences —— 任一实例上的修改传播到所有 peers;
  • Delete silences —— 实现为 "expire immediately" + gossip。

引自:High Availability

4.7 --cluster.* 全部命令行 flags

YAML 中没有任何 cluster 配置,必须使用 CLI flags:

# alertmanager.yml
global:
  # ... other config ...

# No cluster config in YAML - use CLI flags

命令行示例:

alertmanager \
  --cluster.listen-address=0.0.0.0:9094 \
  --cluster.peer=am-1.example.com:9094 \
  --cluster.peer=am-2.example.com:9094 \
  --cluster.peer=am-3.example.com:9094 \
  --cluster.advertise-address=192.0.2.1:9094 \
  --cluster.peer-timeout=15s \
  --cluster.gossip-interval=200ms \
  --cluster.pushpull-interval=60s

关键 flags 及默认值:

Flag 说明 默认
--cluster.listen-address 集群通信绑定地址 0.0.0.0:9094
--cluster.peer peer 地址列表(可重复) —
--cluster.advertise-address 向 peers 通告的 IP 地址(非 hostname),格式 <ip>:<port> 省略时自动探测
--cluster.peer-timeout 每个 peer position 的去重等待时间 15s
--cluster.gossip-interval gossip 频率 200ms
--cluster.pushpull-interval 全量状态同步间隔 60s
--cluster.probe-interval peer 健康检查间隔 1s
--cluster.settle-timeout 等待 gossip 收敛的最长时间 context timeout
--cluster.tls-config 启用 mutual TLS 的集群 TLS 配置(见 https 页 Gossip Traffic) —

💡 补充:以上为官方 HA 文档明确列出的 cluster flags。Alertmanager 二进制实际还提供若干更细粒度的集群调优 flag(如重连、TCP 超时等),可用 alertmanager -h 查看完整列表;生产调优前建议先核对本机版本帮助输出。

Prometheus 侧配置:应配置 Prometheus 将告警发往所有 Alertmanager 实例,而非经由负载均衡器:

# prometheus.yml
alerting:
  alertmanagers:
    - static_configs:
        - targets:
            - am-1.example.com:9093
            - am-2.example.com:9093
            - am-3.example.com:9093

好处:

  • Redundancy:某实例宕机其他仍收告警;
  • Independent processing:各实例独立做路由、分组、去重;
  • No single point of failure:LB 会引入单点故障。

引自:High Availability

4.8 告警状态 API(Alerts API)

4.8.1 建议由 Prometheus 发送告警

Prometheus 自行负责把告警发送给 Alertmanager。官方建议在 Prometheus 中基于 time series 数据配置 alerting rules,而非直接往 Alerts API 发告警——因为 Prometheus 处理了许多特殊情况,以确保即使 Alertmanager 崩溃或重启,告警也能送达。

4.8.2 API 版本

  • 发送告警使用 APIv2。APIv2 以 OpenAPI 规范定义,规范文件:https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml。
  • APIv1 在 Alertmanager 0.16.0 中废弃,并在 0.27.0 中移除。

4.8.3 端点与请求

向 APIv2 发送告警需对 api/v2/alerts 发起 POST 请求;必须设置 Content-Type header 为 application/json,并发送包含告警数组的 JSON 数据:

[
  {
    "labels": {
      "alertname": "<required_value>",
      "<name>": "<value>"
    },
    "annotations": {
      "<name>": "<value>"
    },
    "startsAt": "<RFC3339>",
    "endsAt": "<RFC3339>",
    "generatorURL": "<value>"
  }
]

generatorURL 为可选。send-resolved 相关行为见下。

4.8.4 字段语义

字段 说明
labels 用于去重同一告警的相同实例;alertname 为必填
annotations 附加信息,如 summary、description、runbook URL
startsAt 告警触发时间;若省略,Alertmanager 将其设为当前时间
endsAt 告警应被解决的时间;若省略,Alertmanager 将其设为当前时间 + resolve_timeout
generatorURL 唯一 URL,链接到告警来源(例如 Prometheus 中触发该告警的 rule)

所有时间戳均须 RFC3339 格式。

4.8.5 客户端期望(Expectations from clients)

  • 客户端应定期重新发送 firing alerts,直到告警解决。确切间隔取决于多个变量,如 endsAt 时间戳,或(若省略)resolve_timeout 的值;若省略 endsAt,Alertmanager 会把已存在告警的 endsAt 更新为当前时间 + resolve_timeout。firing alerts 在其 endsAt 过去后即被解决。
  • 为确保已解决告警能发出 resolved notifications,客户端还应在告警解决后最长 5 分钟内持续重发 resolved alerts 给 Alertmanager。由于 Alertmanager 是无状态的,这能保证即使 Alertmanager 崩溃或重启,resolved notification 仍会发出。

curl 示例:

curl -X POST http://am-1:9093/api/v2/alerts \
  -H 'Content-Type: application/json' \
  -d '[{"labels":{"alertname":"TestAlert","instance":"host1"},"annotations":{"summary":"test"},"startsAt":"2026-10-08T12:00:00Z"}]'

引自:Alerts API

4.9 管理 API(Management API)

Alertmanager 提供一组 management API,以便自动化与集成。

方法/路径 说明
GET /-/healthy、HEAD /-/healthy 该端点始终返回 200,用于检查 Alertmanager health
GET /-/ready、HEAD /-/ready 当 Alertmanager 已就绪可服务流量(即能响应查询)时返回 200
POST /-/reload 触发 Alertmanager 配置文件重载;另一种方式是向进程发送 SIGHUP

引自:Management API

💡 补充:/-/healthy 与 /-/ready 的差异在于——前者只表明进程存活(活着即 200),后者才表明已加载完配置与状态、可以对外服务。Kubernetes 中通常把前者用于 livenessProbe、后者用于 readinessProbe。

4.10 HTTPS 与认证

Alertmanager 支持 basic authentication 与 TLS。该功能为 experimental(实验性),未来可能变化。TLS 支持用于两类流量:HTTP traffic 与 gossip traffic。

4.10.1 HTTP 流量(--web.config.file)

通过 --web.config.file flag 指定要加载的 web 配置文件。文件为 YAML 格式,在每次 HTTP 请求时读取,因此配置或证书的任何变更都会立即生效。

tls_server_config:
  # Certificate and key files for server to use to authenticate to client.
  cert_file: <filename>
  key_file: <filename>

  # Server policy for client authentication. Maps to ClientAuth Policies.
  # NOTE: If you want to enable client authentication, you need to use
  # RequireAndVerifyClientCert. Other values are insecure.
  [ client_auth_type: <string> | default = "NoClientCert" ]

  # CA certificate for client certificate authentication to the server.
  [ client_ca_file: <filename> ]

  # Verify that the client certificate has a Subject Alternate Name (SAN)
  # which is an exact match to an entry in this list, else terminate the connection.
  [ client_allowed_sans:
    [ - <string> ] ]

  # Minimum TLS version that is acceptable.
  [ min_version: <string> | default = "TLS12" ]

  # Maximum TLS version that is acceptable.
  [ max_version: <string> | default = "TLS13" ]

  # List of supported cipher suites for TLS versions up to TLS 1.2.
  [ cipher_suites:
    [ - <string> ] ]

  # prefer_server_cipher_suites controls whether the server selects the
  # client's most preferred ciphersuite, or the server's most preferred ciphersuite.
  [ prefer_server_cipher_suites: <bool> | default = true ]

  # Elliptic curves that will be used in an ECDHE handshake, in preference order.
  [ curve_preferences:
    [ - <string> ] ]

http_server_config:
  # Enable HTTP/2 support. Note that HTTP/2 is only supported with TLS.
  # This can not be changed on the fly.
  [ http2: <boolean> | default = true ]
  # List of headers that can be added to HTTP responses.
  [ headers:
    [ Content-Security-Policy: <string> ]
    [ X-Frame-Options: <string> ]
    [ X-Content-Type-Options: <string> ]
    [ X-XSS-Protection: <string> ]
    [ Strict-Transport-Security: <string> ] ]

# Usernames and hashed passwords that have full access to the web
# server via basic authentication. If empty, no basic authentication is
# required. Passwords are hashed with bcrypt.
basic_auth_users:
  [ <string>: <secret> ... ]

字段逐条说明:

字段 说明
tls_server_config.cert_file / key_file 服务端用于向客户端认证的证书与私钥文件
client_auth_type 客户端认证的服务端策略,映射到 Go ClientAuth Policies;默认 "NoClientCert"。注意:要启用客户端认证必须用 RequireAndVerifyClientCert,其他取值不安全
client_ca_file 用于客户端证书认证的 CA 证书
client_allowed_sans 校验客户端证书的 SAN 是否与列表中某项精确匹配,不匹配则终止连接;SAN 匹配类型可为 DNS、IP、e-mail 或 URI 地址之一或多个
min_version 可接受的最低 TLS 版本,默认 "TLS12"
max_version 可接受的最高 TLS 版本,默认 "TLS13"
cipher_suites 支持至 TLS 1.2 的密码套件列表;为空则用 Go 默认;仅支持 crypto/tls#CipherSuites 返回的套件
prefer_server_cipher_suites 控制服务端选客户端最偏好还是服务端最偏好的套件;为 true 时按 cipher_suites 顺序采用服务端偏好;默认 true
curve_preferences ECDHE 握手使用的椭圆曲线,按偏好顺序
http_server_config.http2 启用 HTTP/2,仅 TLS 下受支持,且不可在线热改;默认 true
http_server_config.headers 可加入 HTTP 响应的 header:Content-Security-Policy、X-Frame-Options(接受值 deny 与 sameorigin)、X-Content-Type-Options(接受值 nosniff)、X-XSS-Protection、Strict-Transport-Security;留空则不设置
basic_auth_users 拥有通过 basic auth 访问 web server 完整权限的用户名与哈希后密码;为空则不要求 basic auth。Passwords are hashed with bcrypt

💡 补充:原文未给出生成 bcrypt 密码哈希的具体命令。实践上可用 htpasswd -nBC 12 "" | tr -d ':\n' 或 amtool/Python bcrypt 生成,再填入 basic_auth_users。此为实现细节,非官方文档内容。

4.10.2 Gossip 流量(--cluster.tls-config)

通过 --cluster.tls-config flag 指定是否对 gossip 使用 mutual TLS。gossip 的 server 端与 client 端均可配置。

tls_server_config:
  cert_file: <filename>
  key_file: <filename>
  [ client_auth_type: <string> | default = "NoClientCert" ]
  [ client_ca_file: <filename> ]
  [ min_version: <string> | default = "TLS12" ]
  [ max_version: <string> | default = "TLS13" ]
  [ cipher_suites:
    [ - <string> ] ]
  [ prefer_server_cipher_suites: <bool> | default = true ]
  [ curve_preferences:
    [ - <string> ] ]

tls_client_config:
  # Path to the CA certificate with which to validate the server certificate.
  [ ca_file: <filepath> ]
  # Certificate and key files for client cert authentication to the server.
  [ cert_file: <filepath> ]
  [ key_file: <filepath> ]
  # Server name extension to indicate the name of the server.
  [ server_name: <string> ]
  # Disable validation of the server certificate.
  [ insecure_skip_verify: <boolean> | default = false]

gossip TLS 与 HTTP 的差异点:

  • tls_server_config 部分字段相同(cert_file、key_file、client_auth_type 默认 "NoClientCert"、client_ca_file、min_version 默认 "TLS12"、max_version 默认 "TLS13"、cipher_suites、prefer_server_cipher_suites 默认 true、curve_preferences),但不含 client_allowed_sans。
  • 额外提供 tls_client_config:ca_file(校验服务端证书的 CA 路径)、cert_file/key_file(客户端证书认证)、server_name(server name extension)、insecure_skip_verify(禁用服务端证书校验,默认 false)。

引自:HTTPS and authentication

4.11 生产部署要点

4.11.1 集群规模考量

Alertmanager 使用 gossip,无 quorum/投票,因此任意 N 个实例可容忍最多 N-1 个故障——只要有一个存活,通知就会被发送。

维度 说明
实例更多的收益 更强的同时故障(硬件、网络、机房级中断)韧性;维护窗口期间仍可运行
实例更多的代价 分区时重复通知增多;gossip 流量增加
典型部署 2-3 实例(单机房生产常见);4-5 实例(多机房或高关键环境)
与共识系统差异 与 etcd、Raft 不同,奇数/偶数集群规模无差别——没有投票或 quorum

4.11.2 集群健康监控指标

# Cluster size
alertmanager_cluster_members

# Peer health
alertmanager_cluster_peer_info

# Peer position (affects notification timing)
alertmanager_peer_position

# Failed peers
alertmanager_cluster_failed_peers

# State replication
alertmanager_nflog_gossip_messages_propagated_total
alertmanager_silences_gossip_messages_propagated_total
指标 用途
alertmanager_cluster_members 集群规模,应等于部署实例总数
alertmanager_cluster_peer_info{state="alive"} 各 peer 健康状态
alertmanager_peer_position 本实例 position,影响通知时机
alertmanager_cluster_failed_peers 失败 peer 数
alertmanager_nflog_gossip_messages_propagated_total notification log 复制量
alertmanager_silences_gossip_messages_propagated_total silences 复制量
alertmanager_alerts_limited_total 因 per-alertname limit 被丢弃的告警数(可选 alertname label)

4.11.3 安全

默认集群通信未加密。生产部署(尤其跨 WAN)应使用 mutual TLS:

alertmanager \
  --cluster.tls-config=/etc/alertmanager/cluster-tls.yml

详见 4.10.2 节 Gossip Traffic 的 mutual TLS 配置。

4.11.4 持久化

每个实例持久化:

状态 存储 默认路径
Silences 快照文件 data/silences
Notification log 快照文件 data/nflog

重启时:从磁盘加载 silences 与 notification log → 加入集群并与 peers gossip → 合并从 peers 收到的状态(较新时间戳胜出)→ gossip 收敛后开始处理通知。

注意:alerts 本身不持久化——Prometheus 会定期重发 firing alerts。

4.11.5 常见坑(Common Pitfalls)

# 坑 正确做法
1 Load balancing Prometheus → Alertmanager ❌ 不要用 LB;✅ 在 Prometheus 中配置所有实例
2 Not waiting for gossip to settle 可能导致启动时漏 silence 或重复通知;由 --cluster.settle-timeout 控制
3 Network ACLs blocking cluster port 确保所有实例间开放 9094(或自定义 --cluster.listen-address 端口);默认同时使用 TCP 和 UDP(仅在使用 TLS transport 时只用 TCP)
4 Unroutable advertise addresses 若未设 --cluster.advertise-address,Alertmanager 会尝试自动探测;该值必须是 <ip>:<port> 格式的 IP 地址——不支持 hostname。使用 hostname 会记 warning,启动会继续但 advertise address 未设或错误,导致集群通信失败;云/NAT 环境应显式设置可路由 IP
5 Mismatched cluster configurations 所有实例应使用相同的 --cluster.peer-timeout 与 gossip 设置;不一致会导致不必要的重复或漏通知

4.11.6 故障排查(Troubleshooting)

# View cluster members via API
curl http://am-1:9093/api/v2/status

# Check metrics
curl http://am-1:9093/metrics | grep cluster

# 开启 debug 日志
alertmanager --log.level=debug

诊断脑裂:

  1. 检查各实例的 alertmanager_cluster_members —— 应等于集群总规模;
  2. 检查 alertmanager_cluster_peer_info{state="alive"} —— 应显示所有 peer 为 alive;
  3. 检查实例间的网络连通性。

调试重复通知,可能原因:

原因 排查
Network partitions 预期内,fail-open
Gossip not settled 检查 --cluster.settle-timeout
Clock skew 确保所有实例配置 NTP
Notification log not replicating 检查 gossip 指标

关注日志关键字:"Waiting for gossip to settle..."、"gossip settled; proceeding",以及通知流水线中的去重决策。

4.11.7 端到端示例(How It Works)

场景一:3 实例集群、新 alert group

  1. 告警经 Prometheus 到达全部 3 个实例;
  2. Dispatcher 创建聚合组,等待 group_wait(如 30s);
  3. group_wait 后各实例准备通知;
  4. Notifier 阶段(若刚启动则都等待 gossip 收敛):
    • AM-1(position 0):等待 0s,查 notification log(空),发送通知并写入 nflog;
    • AM-2(position 1):等待 15s,查到 AM-1 的条目,跳过;
    • AM-3(position 2):等待 30s,查到 AM-1 的条目,跳过;
  5. 结果:恰好发送一条通知(由 AM-1 发出)。

场景二:AM-1 故障

  1. 告警仅到达 AM-2、AM-3;
  2. Dispatcher 建组等待 group_wait;
  3. AM-1 不在集群中(探针失败);AM-2 变为 position 0 等待 0s 并发送,AM-3 变为 position 1 等待 15s 后看到 AM-2 条目并跳过;
  4. 结果:通知仍然发出(fail-open)。

场景三:通知期间发生网络分区

  1. 告警到达所有实例;
  2. 分区将 AM-1 与 AM-2/AM-3 隔开;
  3. Partition A(AM-1)position 0 等待 0s 发送;
  4. Partition B(AM-2、AM-3)AM-2 为 position 0 等待 0s 发送、AM-3 为 position 1 等待 15s 后去重;
  5. 结果:发送两条通知(每个分区一条)——fail-open 行为。

引自:High Availability


第 1–4 章小结(Alertmanager 部分)

本部分(第 1-4 章)覆盖了 Alertmanager 的完整告警链路:

  • 第 1 章:告警体系组成、五大核心能力(去重/分组/路由/静默/抑制)+ HA、告警生命周期、限制与注意事项。
  • 第 2 章:alertmanager.yml 全字段——global、route 路由树、time_intervals、inhibit_rules、label matchers、18 类 receiver 集成、http_config 共享块、tracing 与 event_recorder、配置验证与 reload。
  • 第 3 章:Go 模板基础、Data/Alert/KV 数据结构、全部模板函数、6 组官方示例、各渠道样式与集成清单。
  • 第 4 章:HA 设计目标、gossip/Memberlist 机制、通知流水线与去重转发、脑裂 fail-open、silence 复制、--cluster.* flags、Alerts API、Management API、HTTPS 与认证、生产部署要点。

本篇后续章节(第 5-10 章):插桩与客户端库、导出器与集成生态、应用集成实战、可视化、规范(OpenMetrics / Remote Write / Native Histograms)、参与社区。

引自:Alerting overview | Alertmanager | Configuration | Notification template reference | Notification Integrations | High Availability | Alerts API | Management API | HTTPS and authentication

第 5 章 插桩与客户端库

第 4 篇的前四章解决了"告警怎么发出去",从本章开始转入数据怎么进来。要做监控,第一步永远是插桩(instrumentation)——在应用代码里把想观测的量记录下来,并以 Prometheus 能识别的格式暴露出来。这一章覆盖五件事:

  1. 客户端库(client library)总览:有哪些语言的库,谁在维护,状态如何;
  2. 暴露格式(exposition format):指标最终长什么样——文本格式与 protobuf 格式的逐条规范;
  3. 内容协商(content negotiation):抓取方(Prometheus)与被抓取方(target)如何谈定用哪种格式;
  4. UTF-8 名称转义方案(escaping schemes):当指标名/label 名超出传统字符集时如何降级;
  5. Pushgateway:唯一的"推"通路,以及它为什么只应在极少数场景使用。

5.1 客户端库总览

5.1.1 是什么 / 为什么

客户端库是给应用代码做插桩的 SDK。它让你在应用内部定义并维护 Counter、Gauge、Histogram、Summary 等指标(这些库实现的是 Prometheus 的 metric types),然后通过一个 HTTP endpoint 把内部指标的当前状态暴露出去。当 Prometheus 抓取该 endpoint 时,客户端库会把所有被跟踪指标的当前值一次性发给服务端。

选型原则只有一条:选择与你的应用编写语言匹配的 client library。

5.1.2 官方客户端库

以下为官方(official)维护的客户端库,逐项列出:

语言 仓库地址
Go https://github.com/prometheus/client_golang
Java or Scala https://github.com/prometheus/client_java
Node.js https://github.com/prometheus/client_js
Python https://github.com/prometheus/client_python
Ruby https://github.com/prometheus/client_ruby
Rust https://github.com/prometheus/client_rust

5.1.3 非官方第三方客户端库

以下为社区维护的第三方(Unofficial third-party)客户端库,逐项列出:

语言 说明 / 仓库
Bash (Bash 实现)
C digitalocean/prometheus-client-c
C++ jupp0r/prometheus-cpp
Common Lisp (Common Lisp 实现)
Dart (Dart 实现)
Delphi (Delphi 实现)
Elixir (Elixir 实现)
Erlang (Erlang 实现)
Haskell prometheus-client(fimad/prometheus-haskell)
Haskell prometheus(bitnomial/prometheus)
Julia (Julia 实现)
Lua knyar/nginx-lua-prometheus(用于 Nginx)
Lua tarantool/metrics(用于 Tarantool)
.NET / C# (.NET / C# 实现)
OCaml (OCaml 实现)
Perl Metacpan Net::Prometheus
PHP promphp/prometheus_client_php
R cfmack/pRometheus
Swift swift-server/swift-prometheus

5.1.4 状态划分与注意事项

  • 本页只区分 official 与 unofficial third-party 两类状态,官方文档中没有"已弃用(deprecated)"分类。因此"官方/非官方"是唯一的权威状态标记。
  • 无库时的替代方案:若你的语言没有可用客户端库,或想避免引入依赖,也可以自行实现一种受支持的 exposition format(见 5.2 节)来暴露指标。
  • 实现新库的指引:实现新的 Prometheus 客户端库时,请遵循《writing client libraries》(见 6.5 节)。官方文档注明该指引仍是 work in progress,建议同时参考 development mailing list(groups.google.com/forum/#!forum/prometheus-developers);项目方乐于就"如何让库更实用、更一致"提供建议。

引自:Client libraries

引自:Instrumenting(该页为左侧导航栏的章节索引页,仅有 frontmatter,无正文;正文由 clientlibs、exposition_formats、pushing、content_negotiation、escaping_schemes 等子页面承载)

💡 补充:选择库时除"是否有官方实现"外,还应关注三件事——① 是否支持你需要的指标类型(部分小众库只实现了 Counter/Gauge);② 是否支持带标签的指标子项(labels() 类 API);③ 是否支持与 Prometheus 的 protobuf / OpenMetrics 格式对接(影响能否用 exemplar、native histogram 等新特性)。这三项决定了后续能不能平滑升级到新特性,而不是只把库引入就够了。

5.2 文本与 protobuf 暴露格式详解

总述:自 Prometheus 2.0 起,所有向 Prometheus 暴露指标的进程默认必须使用 text 格式;此外可以在 HTTP 内容协商之后使用 protobuf 格式。多种客户端库已经实现了这些格式;若你的语言没有库,可以自行实现。本节只列官方支持的 exposition formats。

5.2.1 Prometheus Text Format 0.0.4

项目 内容
Inception(诞生时间) April 2014
Supported in Prometheus >=0.4.0
Transmission(传输) HTTP
Encoding(编码) UTF-8、\n 行结束
HTTP Content-Type text/plain,带参数 version=0.0.4(缺失 version 会回退到最新 text 格式版本)
可选 HTTP Content-Encoding gzip
支持的 metric primitives Counter、Gauge、Histogram、Summary、Untyped
  • 优点:human-readable(人类可读)、易拼装(无需嵌套)、可逐行读(metadata 除外)。
  • 局限:Verbose(冗长)、类型与 docstring 非语法固有部分(导致指标契约校验几乎没有)、解析成本较高。

5.2.2 文本格式的逐行细节

  • 格式面向行,行以换行符 \n 分隔;最后一行必须以换行符结尾;空行被忽略。
  • 行内 token 可用任意数量的空格和/或 tab 分隔;若不加分隔会与前一个 token 合并,则至少需要一个分隔符;首尾空白被忽略。
  • Comments / HELP / TYPE:
    • 首个非空白字符为 # 的行是注释;除非 # 后第一个 token 是 HELP 或 TYPE,否则忽略。
    • HELP:至少再有一个 token 为 metric name,其余 token 视为该指标名的 docstring。HELP 行在指标名之后可含任意 UTF-8 字符序列,但反斜杠与换行符必须分别转义为 \\ 和 \n;同一指标名只允许一条 HELP 行。
    • TYPE:恰好再有 2 个 token,第一个是 metric name,第二个是 counter、gauge、histogram、summary 或 untyped;同一指标名只允许一条 TYPE 行;TYPE 行必须出现在该指标名第一个样本之前;若无 TYPE 行,类型置为 untyped。
    • 不符合 legacy Prometheus 指标名字符集的指标名必须加引号并转义。

5.2.3 样本行语法(EBNF)

metric_name_or_labels value [ timestamp ]

metric_name_or_labels = metric_name [ "{" labels "}" ] | "{" quoted_metric_name [ "," labels ] "}"

metric_name = identifier

quoted_metric_name = `"` escaped_string `"`

labels = [ label_pairs ]

label_pairs = label_pair { "," label_pair } [ "," ]

label_pair = label_name "=" `"` escaped_string `"`

label_name = identifier | `"` escaped_string `"`

语法说明:

  • identifier 遵循 Prometheus 表达式语言的常规限制;
  • escaped_string 为任意 UTF-8 字符,但反斜杠、双引号、换行必须转义;
  • metric_name 用双引号时,出现在大括号内而非外;
  • label_name 可选用双引号包裹;
  • 不满足表达式语言常规限制的 metric / label 名必须使用带引号语法;
  • label_value 可为任意 UTF-8 字符序列,但 \、"、\n 必须分别转义为 \\、\"、\n;
  • value 是遵循 Go ParseFloat() 的 float,另外合法值有 NaN、+Inf、-Inf;
  • timestamp 是 int64(自 epoch 的毫秒,即 1970-01-01 00:00:00 UTC,不含闰秒),遵循 Go ParseInt()。

5.2.4 Grouping and sorting(分组与排序)

  • 某指标的所有行必须作为单个 group 提供,可选的 HELP 与 TYPE 行放最前(顺序不限)。
  • 除此之外,重复 exposition 中的可复现排序是首选但非必需(若计算代价过高可不排序)。
  • 每行必须有唯一的 metric name 与 labels 组合,否则 ingest 行为未定义。

5.2.5 Histograms 与 summaries 的文本约定

  • 名为 x 的 sum 以单独样本 x_sum 给出;count 以 x_count 给出;
  • summary 的每个 quantile 为同名 x 的单独样本行,带 label {quantile="y"};
  • histogram 的每个 bucket count 为 x_bucket 样本行,带 label {le="y"}(y 为 bucket 上界);
  • histogram 必须有一个 {le="+Inf"} 桶,其值必须等于 x_count;
  • histogram 的 bucket 与 summary 的 quantile 必须按 label 值(分别为 le、quantile)升序出现。

完整示例:

# HELP http_requests_total The total number of HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="post",code="200"} 1027 1395066363000
http_requests_total{method="post",code="400"}    3 1395066363000

# Escaping in label values:
msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9

# UTF-8 metric and label names:
{"my.dotted.metric", "error.message"="Not Found"}

# Minimalistic line:
metric_without_timestamp_and_labels 12.47

# A weird metric from before the epoch:
something_weird{problem="division by zero"} +Inf -3982045

# A histogram, which has a pretty complex representation in the text format:
# HELP http_request_duration_seconds A histogram of the request duration.
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.05"} 24054
http_request_duration_seconds_bucket{le="0.1"} 33444
http_request_duration_seconds_bucket{le="0.2"} 100392
http_request_duration_seconds_bucket{le="0.5"} 129389
http_request_duration_seconds_bucket{le="1"} 133988
http_request_duration_seconds_bucket{le="+Inf"} 144320
http_request_duration_seconds_sum 53423
http_request_duration_seconds_count 144320

# Finally a summary, which has a complex representation, too:
# HELP rpc_duration_seconds A summary of the RPC duration in seconds.
# TYPE rpc_duration_seconds summary
rpc_duration_seconds{quantile="0.01"} 3102
rpc_duration_seconds{quantile="0.05"} 3272
rpc_duration_seconds{quantile="0.5"} 4773
rpc_duration_seconds{quantile="0.9"} 9001
rpc_duration_seconds{quantile="0.99"} 76656
rpc_duration_seconds_sum 1.7560473e+07
rpc_duration_seconds_count 2693

5.2.6 OpenMetrics Text Format

OpenMetrics 是基于 Prometheus text format 标准化 metric wire formatting 的努力;它可作为抓取 target 的格式,且自 Prometheus v2.23.0 起也可用于 federation。当前两个版本:1.0 与 2.0 (draft)(规范细节见第 9 章)。

项目 内容
Inception November 2020
Supported in Prometheus >=2.5.0
Encoding UTF-8、\n 行结束、以 # EOF 结尾
HTTP Content-Type application/openmetrics-text,带参数 version=1.0.0(缺失 version 会回退到最新 text OpenMetrics 版本)
可选 Content-Encoding gzip
primitives Counter、Gauge、Histogram、GaugeHistogram、Summary、Info、StateSet、Untyped
advanced features Unit metadata、Exemplars(Counters、Histogram、GaugeHistogram)、Created/Start Timestamps(Counter、Histogram、GaugeHistogram、Summary)、UTF-8 metric names and label names

优点与局限文字与 Prometheus text 格式相同。

Exemplars(实验特性):使用 OpenMetrics 格式可以 exposition 与查询 Exemplars,它是对已汇总 MetricFamily 的时间点快照,可附上 Trace ID 与 tracing 系统联动。启用需至少 v2.26.0,并在启动参数中加 --enable-feature=exemplar-storage。

5.2.7 Prometheus Protobuf Format

官方在文本表示之外还支持 protobuf exposition format。

  • payload 必须编码为一组表示 MetricFamily 的 protobuf 消息;
  • 消息必须二进制编码,并在前置其 varint 编码的无符号整数长度作为 delimitation;
  • 该 varint 长度分隔编码提供了流式能力,对大抓取目标尤其重要;
  • 所有 string 字段必须 UTF-8 编码。

何时选用 protobuf:Prometheus 3.0 默认偏好文本协议,除非满足以下之一:

  1. 通过 Prometheus 配置中的 scrape_protocols 手动偏好设置;
  2. 启用特定特性时(Protobuf format first),例如 --enable-feature=created-timestamp-zero-ingestion 或 --enable-feature=st-storage,或相应配置项(如 scrape_native_histograms: true)。

注:在 Prometheus 2.0 中 Protobuf 曾被标记为 deprecated,但该决定此后被撤销;自 Prometheus 3.0 起 Prometheus Proto 被积极使用与维护,作为文本格式的补充。

项目 内容
Inception April 2014
Supported in Prometheus >=0.4.0
Transmission HTTP
Encoding 32-Bit Varint-Encoded Record Length-Delimited Protocol Buffer Messages of io.prometheus.client.MetricFamily
HTTP Content-Type application/vnd.google.protobuf,带参数 proto=io.prometheus.client.MetricFamily、encoding=delimited
可选 Content-Encoding gzip
优点 Cross-Platform、Size、Forward/Backward Compatibility、Strict Schema、Supports Concatenation and Streaming、Composite Values
局限 Not human readable
primitives Counter、Gauge、Histogram、GaugeHistogram、Summary、Untyped
advanced features Unit metadata、Exemplars、Created/Start Timestamps、Native(sparse and exponential)Histogram/GaugeHistogram、UTF-8 metric names and label names

Why Proto over Text(为何文本之外仍维护 protobuf):文本格式人可读、压缩好、对编程使用足够高效,但社区仍维护 protobuf,原因有三:

  1. 提升新特性的质量与速度,可安全地以向后/向前兼容方式测试;
  2. protobuf 借助代码生成与灵活性,有助于 exposer/ingestor 实现;
  3. 在(意外的、罕见的)情况下二进制编码更高效。

Versioning:目前 Prometheus protobuf 稳定但明确不带版本号,而是以 Prometheus 版本号作为参照。

Schema:标识为 io.prometheus.client 的 protobuf schema 维护在 Prometheus 仓库(prompb/io/prometheus/client/metrics.proto),也可在 buf registry 获取。

5.2.8 HTTP Content-Type 要求(重要)

自 Prometheus 3.0 起,scrape target 必须为 metrics endpoint 返回合法的 Content-Type;若缺失、不可解析或非受支持媒体类型,scrape 将失败。

5.2.9 ScrapeProtocols vs Content-Type

Prometheus 的 scrape config 通过 scrape_protocols 配置项基于 content-type 进行抓取协议协商;为方便用户,抓取协议以唯一名称引用并映射到具体 content-type。但 target 应以绝对、单一的响应 content-type(如 application/openmetrics-text;version=1.0.0)暴露指标。

Historical versions:历史格式版本见 legacy Client Data Exposition Format 文档;原始 Protobuf 格式的当前版本(含 native histograms 的近期扩展)维护在 prometheus/client_model 仓库。

引自:Exposition formats

5.3 内容协商(Scrape Protocol Content Negotiation)

Abstract:本规范定义 Prometheus 抓取 target 时使用的协议协商机制,包括 Accept header 格式、受支持的 Content Types,以及选定最佳 exposition 格式的协商流程。

Introduction:Prometheus 支持多种抓取格式,含文本与二进制 protobuf;target 依据 Accept header 的值,挑选最佳可用 Content Type 回复。

5.3.1 受支持协议(5 种)

协议名 说明
PrometheusProto Binary protobuf format
PrometheusText0.0.4 Prometheus text format version 0.0.4
PrometheusText1.0.0 Prometheus text format version 1.0.0
OpenMetricsText0.0.1 OpenMetrics text format version 0.0.1
OpenMetricsText1.0.0 OpenMetrics text format version 1.0.0

5.3.2 Protocol Headers(协议 ↔ MIME 类型 ↔ 参数)

协议 MIME 类型 参数
PrometheusProto application/vnd.google.protobuf proto=io.prometheus.client.MetricFamily;encoding=delimited
PrometheusText0.0.4 text/plain version=0.0.4
PrometheusText1.0.0 text/plain version=1.0.0
OpenMetricsText0.0.1 application/openmetrics-text version=0.0.1
OpenMetricsText1.0.0 application/openmetrics-text version=1.0.0

5.3.3 Accept header 构造(Basic Format)

  1. 对 target 支持的每个协议,MUST 指定该协议的 MIME type 与参数;protobuf 协议 MUST 指定编码 delimited;对 PrometheusText1.0.0 与 OpenMetricsText1.0.0,SHOULD 追加 escaping scheme 参数;SHOULD 追加质量值(q)参数。
  2. SHOULD 追加一个最低质量值的 catch-all */*。

Quality Values:SHOULD 按协议在 Accept header 中的位置降序赋 q —— 第一个协议 q=0.{n+1},第二个 q=0.{n},依此类推,n 为受支持协议数量。

Escaping Scheme:对 PrometheusText1.0.0 与 OpenMetricsText1.0.0,Accept header SHOULD 带 escaping=<scheme>;<scheme> MUST 为以下之一:allow-utf-8、underscores、dots、values。

Compression:Accept-Encoding SHOULD 设为 gzip(启用压缩)或 identity(禁用压缩)。

5.3.4 格式选择(Selection of Format)

  1. MUST 使用 Accept header 中权重最高且被 Prometheus 支持的协议;
  2. 若无协议受支持,target MAY 使用用户配置的 fallback scrape protocol;
  3. 若无 fallback,target MUST 以 PrometheusText0.0.4 作为最后兜底。

Content-Type Response:target SHOULD 回复与某个可接受格式匹配的 Content-Type,且 MUST 包含:① 恰当的 MIME type;② version 参数;③ 对 1.0.0 及以上文本格式,附 escaping scheme 参数。

5.3.5 安全考量(Security Considerations)

  • target MUST 校验 Accept header 以防注入;
  • MUST 校验 escaping scheme 参数以防协议混淆;
  • Content-Type header MUST 正确净化以防 MIME type 混淆。

5.3.6 示例

# Default Accept Header
Accept: application/openmetrics-text;version=1.0.0;escaping=allow-utf-8;q=0.5,application/openmetrics-text;version=0.0.1;q=0.4,text/plain;version=1.0.0;escaping=allow-utf-8;q=0.3,text/plain;version=0.0.4;q=0.2,/;q=0.1

# Protobuf-First Accept Header
Accept: application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.5,application/
openmetrics-text;version=1.0.0;escaping=allow-utf-8;q=0.4,application/openmetrics-text;version=0.0.1;q=0.3,text/plain;version=1.0.0;escaping=allow-utf-8;q=0.2,text/plain;version=0.0.4;q=0.1,/;q=0.0

引自:Scrape protocol content negotiation

5.4 UTF-8 指标名与 label 名转义方案(Escaping Schemes)

Abstract:本规范定义 Prometheus 在生成文本 exposition 时,对含 legacy 字符集之外字符的 metric / label 名所使用的不同 escaping scheme;这些方案在抓取期间通过 Accept 与 Content-Type header 中的 escaping 参数协商。

Introduction:Prometheus 支持多种 escaping scheme 处理文本 exposition 中超出 legacy 字符集(a-zA-Z0-9_:)的 metric 与 label 名;escaping scheme 在抓取时协商,影响 metric 生产者应如何格式化指标名。

5.4.1 四种方案

方案 Header Value 规则 示例
No Escaping escaping=allow-utf-8 名称 MUST 是合法 UTF-8 字符串;在双引号内出现时,\、\n、" MUST 用反斜杠转义;不加引号出现时,\ 与 \n MUST 用反斜杠转义;MUST 仅在 producer 与 consumer 都支持 UTF-8 名称时使用 —
Underscore Escaping escaping=underscores 任何不在 legacy 字符集(a-zA-Z0-9_:)内的字符 MUST 替换为下划线;首字符 MUST 为字母、下划线或冒号;后续字符 MUST 为字母、数字、下划线或冒号 metric.name/with/slashes → metric_name_with_slashes
Dots Escaping escaping=dots 点(.)MUST 替换为 _dot_;已有下划线 MUST 替换为双下划线(__);其他非 legacy 字符 MUST 替换为单个下划线;首字符 MUST 为字母、下划线或冒号;后续字符 MUST 为字母、数字、下划线或冒号 metric.name.with.dots → metric_dot_name_dot_with_dot_dots
Value Encoding Escaping escaping=values 名称 MUST 以 U__ 为前缀;每个不属于 legacy 字符集(a-zA-Z0-9_:)的字符 MUST 替换为其 Unicode code point 的十六进制形式,并用下划线包围;单个下划线 MUST 替换为双下划线 metric.name → U__metric_2E_name(2E 即 . 的十六进制 Unicode code point)

5.4.2 默认行为与安全考量

  • Default Behavior:若 Accept header 未指定 escaping scheme,SHOULD 使用 underscores。
  • Security Considerations:
    1. target 在应用 escaping 前 MUST 校验输入名称;
    2. escaping scheme MUST 校验以防注入攻击;
    3. allow-utf-8 方案 MUST 仅在 producer 与 consumer 都支持 UTF-8 名称时使用。

引自:UTF-8 metric and label name escaping schemes

💡 补充:实践中最容易踩坑的是"三方字符集不一致"。Prometheus 3.x 起自身支持 UTF-8 指标名,但下游工具链(Grafana 面板变量、部分 exporter、旧版接收端)可能仍不支持。因此当被监控系统(尤其 OpenTelemetry bridge 场景)产生点分命名(process.cpu.seconds)时,建议在协商中显式指定 underscores 而非 allow-utf-8,牺牲一点可读性换取全链路兼容;只有在确认消费端全部支持 UTF-8 后,才切到 allow-utf-8。

5.5 Pushgateway

5.5.1 是什么 / 为什么

Pushgateway 是一个中间服务,允许把来自无法被 scrape 的 job 的指标推送给它,再由 Prometheus 以常规 scrape 方式抓取 Pushgateway。

它的价值在于:结合 Prometheus 简单的 text-based exposition format,即使是没有客户端库的 shell 脚本也能轻松实现 instrumentation(脚本生成文本指标 → POST 给 Pushgateway → 被 Prometheus 抓取)。

5.5.2 适用场景

通常唯一有效的用例:捕获 service-level batch job 的结果。

  • "service-level" 批处理指语义上与特定机器或 job 实例无关的 job(例如为整个服务删除一批用户)。
  • 此类 job 的指标不应包含 machine 或 instance 标签,以将具体机器/实例的生命周期与推送指标解耦,从而减轻管理陈旧指标的负担。

5.5.3 不适用场景与陷阱

官方明确:仅推荐在有限的特定场景使用。盲目用它替代 Prometheus 常规 pull 模型存在若干陷阱:

  1. 通过单个 Pushgateway 监控多个实例时,Pushgateway 同时成为单点故障和潜在瓶颈;
  2. 会失去 Prometheus 基于 up 指标(每次 scrape 生成)的自动实例健康监控;
  3. Pushgateway 永不忘记推给它的 series,会永久暴露给 Prometheus,除非通过 Pushgateway API 手动删除这些 series。

第 3 点在多实例通过 instance 标签(或类似方式)区分指标时尤其重要:实例被改名或移除后,其指标仍留在 Pushgateway。原因是 Pushgateway 作为 metrics cache 的生命周期与推送指标的进程生命周期本质上是分离的。对比 pull 模型:实例消失(无论是否故意)时其指标会自动消失;用 Pushgateway 则不是,必须手动删除陈旧指标,或自行自动化生命周期同步。

5.5.4 替代策略

  • 若入站防火墙或 NAT 阻止 pull,考虑把 Prometheus server 也移到网络屏障之后;一般建议 Prometheus server 与被监控实例跑在同一网络。否则可考虑 PushProx,它让 Prometheus 能穿越防火墙/NAT。
  • 对与机器相关的 batch job(如自动安全更新 cronjob、配置管理客户端运行),改用 Node Exporter 的 textfile collector 暴露指标,而非 Pushgateway。

5.5.5 API 与各语言使用入口

官方 pushing 页面本身只是入口导航,各语言的具体用法指向对应库的文档:

使用方式 文档入口
Pushgateway 本身及从 Unix shell 使用 见项目 README(https://github.com/prometheus/pushgateway#readme)
从 Java 使用 Pushgateway documentation(https://prometheus.github.io/client_java/exporters/pushgateway/)
从 Go 使用 Push 与 Add 方法(https://pkg.go.dev/github.com/prometheus/client_golang/prometheus/push)
从 Python 使用 Exporting to a Pushgateway(https://prometheus.github.io/client_python/exporting/pushgateway/)
从 Ruby 使用 Pushgateway documentation(https://github.com/prometheus/client_ruby#pushgateway)
其他第三方客户端库 查阅各自文档(链接指向 /docs/instrumenting/clientlibs/)

注:具体的 push 语义与限制在 best practices 的 short-lived service-level batch jobs 中,pushing 页面未展开。

删除 series:如 5.5.3 所述,Pushgateway 会永久保留推入的 series,必须通过 Pushgateway API 手动删除。Pushgateway 还提供管理 API(--web.enable-admin-api,默认关闭,启用后位于 /api/*/admin/),其中包含清空所有 metric groups 的能力(见安全模型相关章节)。

💡 补充:Pushgateway 的常规推送路径形如 POST /metrics/job/<JOB_NAME>{/<LABEL_NAME>/<LABEL_VALUE>...}(POST 为追加、PUT 为替换同组指标、DELETE 删除该组),根路径 / 提供带 landing page 的 Web UI,/metrics 暴露其自身指标供 Prometheus 抓取。这些端点细节以 Pushgateway README 为准,此处仅作定位提示,实际以所部署版本的项目文档核对。

5.5.6 与 Native Histogram 的关系

Pushgateway 逐步支持 native histogram,v1.9 起完整支持;其内部基于 classic protobuf 模型,改动主要是 UI。混合 histogram(classic + native bucket)可以推送并原样暴露;但查询 API 只能返回一种 bucket,优先 native。

5.5.7 注意事项汇总

维度 注意事项
实例标识 service-level batch job 的指标不应带 machine/instance 标签;带则需自行做生命周期同步
陈旧数据 Pushgateway 不会自动过期;必须通过 API 删除,或用 instance 隔离
健康监控 失去基于 up 的自动实例健康监控能力
单点风险 单个 Pushgateway 监控多实例时既是 SPOF 又是瓶颈
安全 任何可访问其 HTTP endpoint 者能创建/修改/删除其中指标;因其通常以 honor_labels 被 scrape,意味着能在 Prometheus 中创建任意时序
抓取模型 仍是 pull:Prometheus 主动抓 Pushgateway,被推入的数据只是"缓存在中间站"

引自:Pushing metrics

引自:When to use the Pushgateway

第 6 章 导出器与集成生态

客户端库解决"自己的代码怎么埋点",导出器(exporter)解决"别人的系统怎么变成指标"。本章先给出完整的导出器与集成清单,再给出编写导出器与客户端库的规范,最后落到三个部署实践:node_exporter、cAdvisor、multi-target exporter(blackbox 型)。

6.1 导出器清单(Exporters)

定位:有大量库与服务器用于把第三方系统的既有指标导出为 Prometheus 格式的指标。适用于无法直接对目标系统做埋点的场景(例如 HAProxy、Linux 系统统计)。

官方 vs 第三方:

  • 标记为 official 的导出器由官方 Prometheus GitHub organization 维护;其余为外部贡献与维护。
  • 官方鼓励创建更多导出器,但无法为全部导出器审核其是否符合最佳实践(见 6.4 节),因此这些导出器通常托管在 Prometheus GitHub 组织之外。
  • Default port allocations wiki 已成为另一个导出器目录,可能包含此处未列出的导出器(原因:功能重叠或仍在开发中)。
  • JMX exporter 可从各类 JVM 应用导出,例如 Kafka、Cassandra。

6.1.1 Databases(数据库)

名称 项目 / 仓库 维护状态
Aerospike aerospike/aerospike-prometheus-exporter 社区
AWS RDS qonto/prometheus-rds-exporter 社区
BetterDB Monitor (Valkey/Redis/Dragonfly) BetterDB-inc/monitor 社区
ClickHouse f1yegor/clickhouse_exporter 社区
Consul prometheus/consul_exporter official
Couchbase couchbase/couchbase-exporter 社区
CouchDB gesellix/couchdb-exporter 社区
Druid opstree/druid-exporter 社区
Elasticsearch prometheus-community/elasticsearch_exporter 社区
EventStore marcinbudny/eventstore_exporter 社区
IoTDB fagnercarvalho/prometheus-iotdb-exporter 社区
KDB+ KxSystems/prometheus-kdb-exporter 社区
Memcached prometheus/memcached_exporter official
MongoDB percona/mongodb_exporter 社区
MongoDB query raffis/mongodb-query-exporter 社区
MongoDB Node.js Driver christiangalsterer/mongodb-driver-prometheus-exporter 社区
MSSQL server awaragi/prometheus-mssql-exporter 社区
MySQL router rluisr/mysqlrouter_exporter 社区
MySQL server prometheus/mysqld_exporter official
OpenTSDB cloudflare/opentsdb_exporter 社区
Oracle DB iamseth/oracledb_exporter 社区
PgBouncer prometheus-community/pgbouncer_exporter 社区
PostgreSQL prometheus-community/postgres_exporter 社区
Presto yahoojapan/presto_exporter 社区
ProxySQL percona/proxysql_exporter 社区
RavenDB marcinbudny/ravendb_exporter 社区
Redis oliver006/redis_exporter 社区
RethinkDB oliver006/rethinkdb_exporter 社区
SQL burningalchemist/sql_exporter 社区
Tarantool metric library tarantool/metrics 社区
Twemproxy stuartnelson3/twemproxy_exporter 社区

6.1.2 Hardware related(硬件相关)

名称 项目 / 仓库 维护状态
apcupsd mdlayher/apcupsd_exporter 社区
BIG-IP ExpressenAB/bigip_exporter 社区
Bosch Sensortec BMP/BME David-Igou/bsbmp-exporter 社区
Collins soundcloud/collins_exporter 社区
Dell Hardware OMSA galexrt/dellhw_exporter 社区
Disk usage dundee/disk_usage_exporter 社区
Fortigate bluecmd/fortigate_exporter 社区
IBM Z HMC zhmcclient/zhmc-prometheus-exporter 社区
IoT Edison roman-vynar/edison_exporter 社区
InfiniBand treydock/infiniband_exporter 社区
IPMI soundcloud/ipmi_exporter 社区
knxd RichiH/knxd_exporter 社区
Meinberg LANTIME OS raphaelthomas/meinberg_ltos_exporter 社区
Modbus RichiH/modbus_exporter 社区
Netgear Cable Modem ickymettle/netgear_cm_exporter 社区
Netgear Router DRuggeri/netgear_exporter 社区
Network UPS Tools (NUT) DRuggeri/nut_exporter 社区
Node/system metrics prometheus/node_exporter official
NVIDIA DCGM (GPU) NVIDIA/dcgm-exporter 社区
ProSAFE dalance/prosafe_exporter 社区
Redfish comcast/fishymetrics 社区
SmartRAID gitlab.com/calestyo/prometheus-smartraid-exporter 社区
Waveplus Radon Sensor jeremybz/waveplus_exporter 社区
Weathergoose Climate Monitor branttaylor/watchdog-prometheus-exporter 社区
Windows prometheus-community/windows_exporter 社区
Intel® Optane™ Persistent Memory Controller intel/ipmctl-exporter 社区

6.1.3 Issue trackers and continuous integration(工单与 CI)

名称 项目 / 仓库 维护状态
Bamboo AndreyVMarkelov/bamboo-prometheus-exporter 社区
Bitbucket AndreyVMarkelov/prom-bitbucket-exporter 社区
Confluence AndreyVMarkelov/prom-confluence-exporter 社区
Jenkins lovoo/jenkins_exporter 社区
JIRA AndreyVMarkelov/jira-prometheus-exporter 社区

6.1.4 Messaging systems(消息系统)

名称 项目 / 仓库 维护状态
Beanstalkd messagebird/beanstalkd_exporter 社区
EMQ nuvo/emq_exporter 社区
Gearman bakins/gearman-exporter 社区
IBM MQ ibm-messaging/mq-metric-samples (cmd/mq_prometheus) 社区
Kafka danielqsj/kafka_exporter 社区
KafkaJS christiangalsterer/kafkajs-prometheus-exporter 社区
NATS nats-io/prometheus-nats-exporter 社区
NSQ lovoo/nsq_exporter 社区
Mirth Connect vynca/mirth_exporter 社区
MQTT blackbox inovex/mqtt_blackbox_exporter 社区
MQTT2Prometheus hikhvar/mqtt2prometheus 社区
RabbitMQ kbudde/rabbitmq_exporter 社区
RabbitMQ Management Plugin deadtrickster/prometheus_rabbitmq_exporter 社区
RocketMQ apache/rocketmq-exporter 社区
Solace solacecommunity/solace-prometheus-exporter 社区

6.1.5 Storage(存储)

名称 项目 / 仓库 维护状态
Ceph digitalocean/ceph_exporter 社区
Ceph RADOSGW blemmenes/radosgw_usage_exporter 社区
Gluster ofesseler/gluster_exporter 社区
GPFS treydock/gpfs_exporter 社区
Hadoop HDFS FSImage marcelmay/hadoop-hdfs-fsimage-exporter 社区
HPE CSI info metrics provider scod.hpedev.io/csi_driver/metrics.html 社区
HPE storage array hpe-storage.github.io/array-exporter/ 社区
Lustre HewlettPackard/lustre_exporter 社区
NetApp E-Series treydock/eseries_exporter 社区
Pure Storage PureStorage-OpenConnect/pure-exporter 社区
ScaleIO syepes/sio2prom 社区
Tivoli Storage Manager / IBM Spectrum Protect treydock/tsm_exporter 社区
IBM Storage Scale metrics IBM/ibm-spectrum-scale-bridge-for-grafana 社区

6.1.6 HTTP

名称 项目 / 仓库 维护状态
Apache Lusitaniae/apache_exporter 社区
HAProxy prometheus/haproxy_exporter official
Nginx metric library knyar/nginx-lua-prometheus 社区
Nginx VTS sysulq/nginx-vts-exporter 社区
Passenger stuartnelson3/passenger_exporter 社区
Squid boynux/squid-exporter 社区
Tinyproxy gmm42/tinyproxy_exporter 社区
Varnish jonnenauha/prometheus_varnish_exporter 社区
WebDriver mattbostock/webdriver_exporter 社区

6.1.7 IoT

名称 项目 / 仓库 维护状态
FairCom Edge FairCom-Official/faircom_exporter 社区

6.1.8 APIs

名称 项目 / 仓库 维护状态
AWS ECS slok/ecs-exporter 社区
AWS Health Jimdo/aws-health-exporter 社区
AWS SQS jmal98/sqs_exporter 社区
AWS SQS Prometheus jmriebold/sqs-prometheus-exporter 社区
Azure Health matzefriedrich/az-health-exporter 社区
BigBlueButton greenstatic/bigbluebutton-exporter 社区
Cloudflare gitlab.com/gitlab-org/cloudflare_exporter 社区
Cryptowat nbarrientos/cryptowat_exporter 社区
DigitalOcean metalmatze/digitalocean_exporter 社区
Docker Cloud infinityworks/docker-cloud-exporter 社区
Docker Hub infinityworks/docker-hub-exporter 社区
Fastly peterbourgon/fastly-exporter 社区
Garmin Connect barnes-c/garmin_exporter 社区
GitHub githubexporter/github-exporter 社区
Gmail jamesread/prometheus-gmail-exporter 社区
GraphQL ricardbejarano/graphql_exporter 社区
InstaClustr fcgravalos/instaclustr_exporter 社区
IO River ioriver/ioriver-exporter 社区
Mozilla Observatory Jimdo/observatory-exporter 社区
OpenWeatherMap RichiH/openweathermap_exporter 社区
Pagespeed foomo/pagespeed_exporter 社区
Rancher infinityworks/prometheus-rancher-exporter 社区
Speedtest nlamirault/speedtest_exporter 社区
Tankerkönig API lukasmalkmus/tankerkoenig_exporter 社区

6.1.9 Logging(日志)

名称 项目 / 仓库 维护状态
Fluentd V3ckt0r/fluentd_exporter 社区
Google mtail log data extractor google/mtail 社区
Grok fstab/grok_exporter 社区

6.1.10 FinOps

名称 项目 / 仓库 维护状态
AWS Cost electrolux-oss/aws-cost-exporter 社区
Azure Cost electrolux-oss/azure-cost-exporter 社区
Kubernetes Cost electrolux-oss/kubernetes-cost-exporter 社区

6.1.11 Other monitoring systems(其他监控系统)

名称 项目 / 仓库 维护状态
Akamai Cloudmonitor ExpressenAB/cloudmonitor_exporter 社区
Alibaba Cloudmonitor aylei/aliyun-exporter 社区
AWS CloudWatch prometheus/cloudwatch_exporter official
Azure Monitor RobustPerception/azure_metrics_exporter 社区
CCF HuaTuo ccfos/huatuo 社区
Cloud Foundry Firehose cloudfoundry-community/firehose_exporter 社区
Collectd prometheus/collectd_exporter official
Google Stackdriver frodenas/stackdriver_exporter 社区
Graphite prometheus/graphite_exporter official
Heka dashboard docker-infra/heka_exporter 社区
Heka imgix/heka_exporter 社区
Huawei Cloudeye huaweicloud/cloudeye-exporter 社区
InfluxDB prometheus/influxdb_exporter official
ITM rafal-szypulka/itm_exporter 社区
Java GC loyispa/jgc_exporter 社区
JavaMelody fschlag/javamelody-prometheus-exporter 社区
JMX prometheus/jmx_exporter official
Munin pvdh/munin_exporter 社区
Nagios / Naemon Griesbacher/Iapetos 社区
Neptune Apex dl-romero/neptune_exporter 社区
New Relic mrf/newrelic_exporter 社区
NRPE robustperception/nrpe_exporter 社区
Osquery zwopir/osquery_exporter 社区
OTC CloudEye tiagoReichert/otc-cloudeye-prometheus-exporter 社区
Pingdom giantswarm/prometheus-pingdom-exporter 社区
Promitor (Azure Monitor) promitor.io 社区
scollector tgulacsi/prometheus_scollector 社区
Sensu reachlin/sensu_exporter 社区
site24x7 svenstaro/site24x7_exporter 社区
SNMP prometheus/snmp_exporter official
StatsD prometheus/statsd_exporter official
TencentCloud monitor tencentyun/tencentcloud-exporter 社区
ThousandEyes sapcc/1000eyes_exporter 社区
StatusPage sergeyshevch/statuspage-exporter 社区

6.1.12 Miscellaneous(杂项)

名称 项目 / 仓库 维护状态
ACT Fibernet git.captnemo.in/nemo/prometheus-act-exporter 社区
BIND prometheus-community/bind_exporter 社区
BIND query DRuggeri/bind_query_exporter 社区
Bitcoind LePetitBloc/bitcoind-exporter 社区
Blackbox prometheus/blackbox_exporter official
Bungeecord weihao/bungeecord-prometheus-exporter 社区
BOSH cloudfoundry-community/bosh_exporter 社区
cAdvisor google/cadvisor 社区
Cachet ContaAzul/cachet_exporter 社区
ccache virtualtam/ccache_exporter 社区
c-lightning lightningd/plugins (prometheus) 社区
DHCPD leases DRuggeri/dhcpd_leases_exporter 社区
Dovecot kumina/dovecot_exporter 社区
Dnsmasq google/dnsmasq_exporter 社区
eBPF cloudflare/ebpf_exporter 社区
eBPF network traffic kasd/texporter 社区
Ethereum Client 31z4/ethereum-prometheus-exporter 社区
FFmpeg domcyrus/ffmpeg_exporter 社区
File statistics michael-doubez/filestat_exporter 社区
JFrog Artifactory peimanja/artifactory_exporter 社区
Hostapd Fundacio-i2CAT/hostapd_prometheus_exporter 社区
IBM Security Verify Access / Security Access Manager gitlab.com/zeblawson/isva-prometheus-exporter 社区
IPsec torilabs/ipsec-prometheus-exporter 社区
ipset hatamiarash7/ipset-exporter 社区
IRCd dgl/ircd_exporter 社区
Linux HA ClusterLabs ClusterLabs/ha_cluster_exporter 社区
JMeter plugin johrstrom/jmeter-prometheus-plugin 社区
JSON prometheus-community/json_exporter 社区
Kannel apostvav/kannel_exporter 社区
Kemp LoadBalancer giantswarm/prometheus-kemp-exporter 社区
Kibana pjhampton/kibana-prometheus-exporter 社区
kube-state-metrics kubernetes/kube-state-metrics 社区
Locust ContainerSolutions/locust_exporter 社区
Meteor JS web framework atmospherejs.com/sevki/prometheus-exporter 社区
Minecraft exporter module Baughn/PrometheusIntegration 社区
Minecraft dirien/minecraft-prometheus-exporter 社区
NetBird matanbaruch/netbird-api-exporter 社区
Nomad gitlab.com/yakshaving.art/nomad-exporter 社区
nftables Intrinsec/nftables_exporter 社区
Open OnDemand OSC/ondemand_exporter 社区
OpenClaw SammyLin/openclaw-exporter 社区
OpenStack openstack-exporter/openstack-exporter 社区
OpenStack blackbox infraly/openstack_client_exporter 社区
OpenVPN Fadi-hamwi/OpenVPN-Metrics-Exporter 社区
oVirt czerwonk/ovirt_exporter 社区
Pact Broker ContainerSolutions/pactbroker_exporter 社区
PHP-FPM bakins/php-fpm-exporter 社区
Pi-Hole eko/pihole-exporter 社区
Podman containers/prometheus-podman-exporter 社区
Postfix sarab97/postfix_exporter 社区
Prefect2 pathfinder177/prefect2-prometheus-exporter 社区
Process ncabatoff/process-exporter 社区
rTorrent mdlayher/rtorrent_exporter 社区
Rundeck phsmith/rundeck_exporter 社区
SABnzbd msroest/sabnzbd_exporter 社区
SAML DoodleScheduling/saml-exporter 社区
Scraparr thecfu/scraparr 社区
Script adhocteam/script_exporter 社区
Shield cloudfoundry-community/shield_exporter 社区
Slurm sckyzo/slurm_exporter 社区
Smokeping prober SuperQ/smokeping_prober 社区
SMTP/Maildir MDA blackbox prober cherti/mailexporter 社区
SoftEther dalance/softether_exporter 社区
SSH treydock/ssh_exporter 社区
Tailscale adinhodovic/tailscale-exporter 社区
Teamspeak3 hikhvar/ts3exporter 社区
Transmission metalmatze/transmission-exporter 社区
Unbound kumina/unbound_exporter 社区
WireGuard MindFlavor/prometheus_wireguard_exporter 社区
Xen lovoo/xenstats_exporter 社区
ZLMediaKit guohuachan/ZLMediaKit_exporter 社区

6.1.13 实现新导出器的建议

实现新 Prometheus 导出器时,请遵循《guidelines on writing exporters》(见 6.4 节);也建议咨询 development mailing list(https://groups.google.com/forum/#!forum/prometheus-developers)。官方乐于就"如何让导出器尽可能有用、一致"提供建议。

引自:Exporters and integrations

6.2 Software exposing Prometheus metrics(自身直接暴露指标)

以下软件自身直接暴露指标、无需独立导出器。标记 direct 的软件同时也用 Prometheus 客户端库做了直接埋点。

软件 备注
Ansible Automation Platform Automation Controller (AWX)
Apache APISIX
App Connect Enterprise
Ballerina
BFE
Caddy direct
Ceph
ClickHouse
CockroachDB
Collectd
Concourse
CRG Roller Derby Scoreboard direct
Diffusion
Docker Daemon
Doorman direct
Dovecot
Envoy
Etcd direct
Flink
FreeBSD Kernel
GitLab
Grafana
JavaMelody
Kong
Kubernetes direct
LavinMQ
Linkerd
mgmt
MidoNet
midonet-kubernetes direct
MinIO
PATROL with Monitoring Studio X
Netdata
OpenZiti
Pomerium
Pretix
Quobyte direct
RabbitMQ
RobustIRC
RTPEngine
ScyllaDB
Skipper
SkyDNS direct
Telegraf
Traefik
Triton Inference Server
Vector
VerneMQ
Flux
Xandikos direct
Zipkin

Other third-party utilities(其他第三方工具/库,非客户端库本身):这些库/工具用于辅助在特定语言中埋点,本身不是 Prometheus 客户端库,而是底层使用某个标准 Prometheus 客户端库;与所有独立维护的软件一样,官方无法为其审核最佳实践。

语言 工具 / 库
Clojure iapetos(https://github.com/clj-commons/iapetos)
Go go-metrics instrumentation library(https://github.com/armon/go-metrics)
Go gokit(https://github.com/peterbourgon/gokit)
Go prombolt(https://github.com/mdlayher/prombolt)
Java/JVM EclipseLink metrics collector(https://github.com/VitaNuova/eclipselinkexporter)
Java/JVM Hystrix metrics publisher(https://github.com/ahus1/prometheus-hystrix)
Java/JVM Jersey metrics collector(https://github.com/VitaNuova/jerseyexporter)
Java/JVM Micrometer Prometheus Registry(https://micrometer.io/docs/registry/prometheus)
Python-Django django-prometheus(https://github.com/korfuri/django-prometheus)
Node.js swagger-stats(https://github.com/slanatech/swagger-stats)

6.3 如何编写导出器(Writing exporters)

适用前提:如果你是在为自己的代码做埋点,应遵循客户端库埋点的通用规则;但从其他监控/埋点系统取指标时,事情往往不那么黑白分明。本节给出编写导出器或自定义 collector 时应考虑的事项;其中理论对做直接埋点的人也很有价值。

6.3.1 Maintainability and purity(可维护性与纯粹性)

编写导出器的主要决策是:你愿意投入多少工作以从系统中得到"完美"的指标。

系统形态 建议 示例
指标数量少且极少变化 追求完美是容易的选择 HAProxy exporter
有数百个指标且随新版本频繁变化 追求完美等于签下大量持续性工作 MySQL exporter 处于光谱另一端
混合 复杂度按 module 而异 node exporter:mdadm collector 手工解析一个文件并暴露专为该 collector 创建的指标,不妨把指标做对;meminfo collector 的结果随内核版本变化,因此只做"刚好够"的转换以产出合法指标

6.3.2 Configuration(配置)

  • 处理应用类目标时,应追求用户除了告诉它应用在哪里之外无需任何自定义配置。
  • 若某些指标过于细粒度、在大型环境中开销太大,可能需要提供过滤掉某些指标的能力,例如 HAProxy exporter 允许过滤 per-server 统计;同样,可能有开销很大的指标默认被禁用。
  • 处理其他监控系统、框架与协议时,往往需要提供额外配置或定制才能产出适合 Prometheus 的指标:
    • 最好的情况:监控系统的数据模型与 Prometheus 足够相似,可以自动决定如何转换——Cloudwatch、SNMP、collectd 属于此类。这里最多只需要让用户选择想抽取哪些指标。
    • 其他情况:指标完全非标准,取决于系统与底层应用的用法,此时须由用户告知如何转换。JMX exporter 是这方面最典型的(worst offender),Graphite 与 StatsD 导出器也需要配置来提取标签。
  • 建议:确保导出器开箱即用(无需配置),并在需要转换时提供一批示例配置。
  • YAML 是 Prometheus 的标准配置格式,所有配置默认都应使用 YAML。

6.3.3 Metrics · Naming(指标命名)

  • 遵循指标命名最佳实践。
  • 一般来说,指标名应让熟悉 Prometheus 但不熟悉该特定系统的人也能较好地猜出指标含义。http_requests_total 就不是很有用——这些请求是在进入时、在某个过滤器中、还是到达用户代码时被计量的?requests_total 更糟——是什么类型的请求?
  • 直接埋点时,一个给定指标应恰好存在于一个文件中。相应地,在导出器与 collector 中,一个指标应恰好适用于一个子系统并据此命名。
  • 指标名绝不应程序化生成(procedurally generated),除非是在编写自定义 collector 或导出器时。
  • 应用的指标名一般应以导出器名作为前缀,例如 haproxy_up。
  • 指标必须使用基本单位(例如秒、字节),把转换为更易读单位的工作留给绘图工具。
  • 无论最终使用什么单位,指标名中的单位必须与实际使用的单位一致。
  • 同理,暴露比率(ratios),而不是百分比。更好的做法是为比率两个组成部分各自指定一个 counter。
  • 指标名不应包含其导出时所用的标签,例如 by_type——因为如果该标签被聚合掉,名字就不成立了。
    • 唯一例外:当你通过多个指标以不同标签导出同一份数据时,这通常是最合理的区分方式。对直接埋点而言,这只会出现在"带全部标签导出一个指标会导致基数过高"的情况下。
  • Prometheus 的指标名与标签名使用 snake_case。把 camelCase 转成 snake_case 是可取的,但自动转换对 myTCPExample、isNaN 之类不一定产生好结果,所以有时最好保持原样。
  • 暴露的指标不应包含冒号,冒号保留给用户自定义的 recording rules 在做聚合时使用。
  • 指标名中只有 [a-zA-Z0-9:_] 是合法的。
  • _sum、_count、_bucket、_total 后缀被 Summaries、Histograms、Counters 使用。除非你产出的就是这几种,否则应避免这些后缀。
  • _total 是 counter 的约定;如果你使用 COUNTER 类型就应当使用它。
  • process_ 与 scrape_ 前缀是保留的;如果加上自己的前缀后仍符合匹配语义,是可以的。例如 Prometheus 有 scrape_duration_seconds 表示刮取耗时,良好实践是同时提供一个以导出器为中心的指标,例如 jmx_scrape_duration_seconds,表示该导出器自身做事花了多久。对于有 PID 的进程统计,Go 与 Python 都提供了替你处理此事的 collector,好例子是 HAProxy exporter。
  • 当同时有成功请求数与失败请求数时,最好的暴露方式是一个总请求数指标 + 一个失败请求数指标,这样易于计算失败率。不要用一个带 failed/success 标签的指标。 同理,缓存的 hit/miss 最好是"总量一个指标 + 命中一个指标"。
  • 要考虑使用者做代码/网页搜索指标名的可能性。如果名称非常成熟且不太可能在这些名称圈子之外被使用(例如 SNMP 与网络工程师),那么保持原样可能是好主意;这个逻辑并不适用于所有导出器(例如 MySQL exporter 的指标可能被各种人使用,不只是 DBA)。在 HELP 字符串中写入原始名称可以提供与使用原始名称大体相当的好处。

6.3.4 Metrics · Labels(标签)

  • 避免把 type 用作标签名,它太泛且常常没有意义。
  • 应尽可能避免那些可能与 target labels 冲突的名字,例如 region、zone、cluster、availability_zone、az、datacenter、dc、owner、customer、stage、service、environment、env。
  • 但是,如果这就是应用对某资源的称呼,最好不要为了改名而造成混淆。
  • 避免仅仅因为共享前缀就把东西塞进一个指标。除非你确信某组东西作为单个指标是合理的,否则多个指标更安全。
  • 标签 le 对 Histogram 有特殊含义,quantile 对 Summary 有特殊含义,一般应避免这些标签。
  • 读/写 与 发送/接收 最好是各自独立的指标,而不是作为标签。 这通常是因为你一次只关心其中之一,分开使用更容易。
  • 经验法则:一个指标在被求和或求平均时应当是有意义的。
  • 导出器中还会出现另一种情况:数据本质上是**表格型(tabular)**的,若不这样做用户就得对指标名做正则才能使用。比如主板上的电压传感器,对它们做数学运算没有意义,但把它们放在一个指标里比每个传感器一个指标更合理。一个指标内的所有值(几乎)总是应当具有相同单位——设想风扇转速和电压混在一起,而你没有自动区分它们的办法。
  • 不要这样(错误示范):
my_metric{label="a"} 1
my_metric{label="b"} 6
my_metric{label="total"} 7

或这样:

my_metric{label="a"} 1
my_metric{label="b"} 6
my_metric{} 7

前者会破坏对它做 sum() 的人;后者既破坏 sum 又非常难用。某些客户端库(例如 Go)会在自定义 collector 中主动阻止后一种做法,所有客户端库在直接埋点中都应阻止后一种做法。两种都绝不要做,要依赖 Prometheus 的聚合。

  • 如果你的监控这样暴露了一个 total,就去掉这个 total。若因某些原因必须保留(例如该 total 包含未被单独计数的东西),请使用不同的指标名。
  • 埋点标签应尽量少:每多一个标签,用户在写 PromQL 时就多需要考虑一个。因此应避免那些"移除后不影响时间序列唯一性"的埋点标签。指标周边的附加信息可通过 info 指标来添加。
  • 但也有这样的情形:几乎该指标的所有使用者都想要该附加信息。此时添加一个非唯一(non-unique)标签,而不是 info 指标,才是正确的解法。例如 mysqld_exporter 的 mysqld_perf_schema_events_statements_total 的 digest 标签是完整查询模式的哈希,足以保证唯一性;但仅有它用处不大,还需要人类可读的 digest_text 标签,而它对长查询只包含查询模式的开头、因此不唯一。于是最终同时保留供人阅读的 digest_text 标签和用于唯一性的 digest 标签。

6.3.5 Target labels, not static scraped labels

  • 如果你发现自己想给所有指标加同一个标签,停下来。
  • 这通常出现在两种情形:
    1. 某个标签在指标上会很有用,例如软件版本号。此时应改用 "How to have labels for machine roles" 中描述的方法。
    2. 该标签实际上是一个 target label。这类东西例如 region、cluster 名称等,来自你的基础设施设置而非应用本身。应用不应自行宣称自己在你的标签分类体系中的位置——那是运行 Prometheus 服务器的人来配置的,而且不同的人监控同一个应用可能会给它起不同的名字。
  • 相应地,这些标签属于 Prometheus 的刮取配置(通过你所用的服务发现)。把 machine roles 的概念也用在这里是可以的,因为对至少一部分刮取者来说这很可能是有用信息。

6.3.6 Types(类型)

  • 应尽量把指标类型对应到 Prometheus 类型。通常意味着 counters 与 gauges;summaries 的 _count 与 _sum 也相对常见,偶尔会见到 quantiles;Histograms 很少见,若遇到,请记住暴露格式中暴露的是累积值(cumulative)。
  • 常常不容易判断指标的类型,尤其是在自动处理一批指标时。一般来说 UNTYPED 是安全的默认值。
  • Counter 不能下降,所以如果你从其他埋点系统(例如 Dropwizard metrics)拿到一个可被递减的 counter 类型,那它不是 counter,而是 gauge。此时 UNTYPED 可能是最好的类型,因为若它被当作 counter 使用,标为 GAUGE 会有误导。

6.3.7 Help strings(帮助字符串)

  • 在转换指标时,用户能够回溯原始指标是什么、以及当时应用了哪些规则,会很有用。把 collector 或 exporter 的名称、所应用规则的 ID、以及原始指标的名称与细节写进 help 字符串,会极大地帮助用户。
  • Prometheus 不喜欢一个指标有不同的 help 字符串。 如果你从许多指标合成一个指标,请选择其中一个放进 help 字符串。
  • 示例:SNMP exporter 使用 OID;JMX exporter 写入一个示例 mBean 名称;HAProxy exporter 使用手写字符串;node exporter 也有丰富多样的示例。

6.3.8 Drop less useful statistics(丢弃不太有用的统计量)

  • 某些埋点系统会暴露 1m/5m/15m 速率、自应用启动以来的平均速率(在 Dropwizard metrics 中称为 mean),以及最小值、最大值和标准差。
  • 这些都应丢弃,因为它们不太有用且增加杂乱。Prometheus 可以自己计算速率,而且通常更准确——因为暴露出来的平均值通常是指数衰减的。你不知道 min/max 是在哪个时间区间上计算的;标准差在统计上没有用处,而且如果你真需要计算它,随时可以暴露平方和、_sum 和 _count。
  • 分位数(quantiles)有类似问题,可以选择丢弃它们,或者放进一个 Summary。

6.3.9 Dotted strings(点分字符串)

  • 许多监控系统没有标签,而是用类似 my.class.path.mymetric.labelvalue1.labelvalue2.labelvalue3 的形式。
  • Graphite 与 StatsD 导出器共用一套小型配置语言来转换这些。其他导出器也应实现相同的方式。 该转换目前仅在 Go 中实现,若能抽取为独立库会更好。

6.3.10 Collectors(采集器)

  • 实现导出器的 collector 时,绝不应使用常见的直接埋点方式、然后在每次刮取时更新指标。
  • 而应每次创建新的指标。在 Go 中这通过 Collect() 方法里的 MustNewConstMetric 完成;Python 参见自定义 collector 文档;Java 则是在 collect 方法中生成 List<MetricFamilySamples>。
  • 这样做的原因有两点:
    1. 两次刮取可能同时发生,而直接埋点使用的是事实上的文件级全局变量,会产生竞态条件(race conditions);
    2. 如果某个标签值消失了,它仍然会被导出。
  • 对导出器自身做直接埋点是没问题的,例如导出器在所有刮取中传输的总字节数或总调用次数。
  • 对于 blackbox exporter 和 SNMP exporter 这类不绑定单个 target 的导出器,这些自身指标应只在一个普通的 /metrics 调用中暴露,而不是在对某个特定 target 的刮取中暴露。

6.3.11 Metrics about the scrape itself

  • 有时你想导出关于刮取本身的指标,例如耗时多久或处理了多少条记录。
  • 这些应作为 gauges 暴露(因为它们关乎一个事件——那次刮取),且指标名以导出器名作前缀,例如 jmx_scrape_duration_seconds。通常去掉 _exporter;如果该导出器同时也可作为单纯的 collector 使用,那务必去掉。
  • 其他刮取"元"指标应当避免,例如刮取次数的 counter、或刮取耗时的 histogram。让导出器跟踪这些指标会与 Prometheus 自动生成的指标重复,并增加每个导出器实例的存储成本。

6.3.12 Machine and process metrics(机器与进程指标)

  • 许多系统(例如 Elasticsearch)会暴露 CPU、内存、文件系统等机器指标。由于 node exporter 已在 Prometheus 生态中提供这些,此类指标应当被丢弃。
  • 在 Java 世界,许多埋点框架会暴露进程级与 JVM 级统计(如 CPU、GC)。Java 客户端与 JMX exporter 已通过 DefaultExports.java 以推荐形式包含这些,因此这些也应被丢弃。
  • 其他语言与框架同理。

6.3.13 Deployment(部署)

  • 每个导出器应只监控恰好一个应用实例,最好与它同机紧邻运行。这意味着:每运行一个 HAProxy,就运行一个 haproxy_exporter 进程;每台跑 Mesos worker 的机器上都运行 Mesos exporter,如果一台机器同时有 master,则再跑一个。
  • 背后的理论是:对直接埋点而言这就是你会做的事,我们只是尽量在其他架构中贴近它。这意味着所有服务发现都在 Prometheus 中完成,而不是在导出器中完成。这还有个好处:Prometheus 拥有它所需的目标信息,可让用户用 blackbox exporter 探测你的服务。
  • 有两个例外:
    1. 在你所监控的应用旁边运行完全说不通的情况。SNMP、blackbox、IPMI 三类导出器是主要例子。IPMI 与 SNMP 的目标设备往往是黑盒、无法在其上运行代码(不过如果能在其上跑一个 node exporter 会更好);blackbox exporter 则是在监控诸如 DNS 名称之类的东西,那里同样没有东西可运行。此时 Prometheus 仍应做服务发现,并把要刮取的 target 传递过去。
      • 注意:目前只有 Go、Python、Java 客户端库能编写这类导出器。
    2. 你从某个系统的随机实例拉取一些统计、且不关心具体在与哪个实例通信。设想有一组 MySQL 副本,你想对数据跑一些业务查询然后导出。让导出器用你平常的负载均衡方式去和某一个副本通信,是最合理的做法。
      • 当监控的系统存在 master-election 时这不适用;那种情况下应逐个实例监控,并在 Prometheus 中处理 "masterness"。因为并不总是恰好存在一个 master,而且在 Prometheus 脚下改变 target 的含义会引起怪异现象。

6.3.14 Scheduling(调度)

  • 指标应当只在 Prometheus 刮取它们时才从应用拉取;导出器不应基于自己的定时器进行刮取。也就是说,所有刮取都应是同步的。
  • 相应地,不要给你暴露的指标设置时间戳,让 Prometheus 去处理。如果你认为自己需要时间戳,那你可能需要的是 Pushgateway。
  • 如果某个指标获取特别昂贵(例如耗时超过一分钟),可以接受缓存它,但必须在 HELP 字符串中注明。
  • Prometheus 的默认刮取超时是 10 秒。如果你的导出器预计会超过这个值,应在用户文档中明确指出。

6.3.15 Pushes(推送型系统)

  • 有些应用与监控系统只会推送指标,例如 StatsD、Graphite、collectd。
  • 两个考量:
    1. 何时让指标过期? Collectd 与对接 Graphite 的东西都是定期导出的,当它们停止时我们就应停止暴露指标。Collectd 自带过期时间,所以用它;Graphite 没有,所以在导出器上做成一个 flag。StatsD 稍有不同,因为它处理的是事件而非指标;最好的模型是在每个应用旁运行一个导出器,并在应用重启时同时重启它们,以清除状态。
    2. 这类系统往往允许用户发送 delta 或原始 counter。应尽可能依赖原始 counter,因为那才是 Prometheus 的通用模型。
  • 对于服务级指标(例如服务级批处理作业),应让导出器把数据推入 Pushgateway 并在事件结束后退出,而不是自己维护状态。
  • 对于实例级批处理指标,目前还没有清晰的模式。可选方案:滥用 node exporter 的 textfile collector、依赖内存状态(若不需要跨重启持久化可能最好)、或实现与 textfile collector 类似的功能。

6.3.16 Failed scrapes(刮取失败)

  • 当所通信的应用不响应或存在其他问题时,目前有两种模式:
    1. 返回 5xx 错误;
    2. 提供一个类似 myexporter_up(例如 haproxy_up)的变量,取值 0 或 1,取决于刮取是否成功。
  • 后一种在刮取失败时仍能获取一些有用指标的情况下更好,例如 HAProxy exporter 仍能提供进程统计。
  • 前一种对用户来说略微更容易处理,因为 up 按平常方式工作,但你无法区分是导出器挂了还是应用挂了。

6.3.17 Landing page & Port numbers

  • 着陆页:用户访问 http://yourexporter/ 时看到一个简单的 HTML 页面(含导出器名称和指向 /metrics 页面的链接)会更好。
  • 端口号:用户可能在同一台机器上有许多导出器与 Prometheus 组件,为便于处理,每个都应有一个唯一端口号。
    • https://github.com/prometheus/prometheus/wiki/Default-port-allocations 是我们记录它们的地方,该页面公开可编辑。
    • 开发导出器时可自由取用下一个空闲端口号,最好在公开宣布之前就取好。如果还没准备好发布,写上你的用户名和 WIP 也可以。
    • 这是一个为了让用户生活更方便的注册表,不是对开发特定导出器的承诺。对于内部应用的导出器,建议使用默认端口分配范围之外的端口。

6.3.18 Announcing(对外发布)

准备好向世界宣布你的导出器后,给邮件列表发邮件,并提交一个 PR 把它加入可用导出器列表(具体是编辑 https://github.com/prometheus/docs/blob/main/docs/instrumenting/exporters.md)。

引自:Writing exporters

6.4 客户端库编写规范(Writing client libraries)

文档目的:规定 Prometheus 客户端库应提供哪些功能与 API,目标是各库之间保持一致,让常见用例变简单,并避免提供可能把用户引向错误道路的功能。撰写时已有 10 种语言受支持,因此对"如何写客户端库"已有较成熟认识;这些准则用于帮助新客户端库作者写出好的库。

6.4.1 Conventions(约定)

  • MUST / MUST NOT / SHOULD / SHOULD NOT / MAY 的含义依 RFC 2119。
  • 额外定义 ENCOURAGED:表示某特性是库应当具备的(desirable),但即使没有也可以——即 "nice to have"。
  • 需牢记的原则:
    • 充分利用各语言自身的特性;
    • 常见用例应当简单易用;
    • 正确的做法应当就是容易的做法;
    • 更复杂的用例应当是可实现的(possible)。
  • 常见用例(按重要性排序):
    1. 不带标签的 Counter,在库/应用中大量散布使用;
    2. 在 Summaries/Histograms 中对函数/代码块计时;
    3. Gauge 用于跟踪事物的当前状态(及其上限);
    4. 批处理作业(batch jobs)的监控。

6.4.2 Overall structure(整体结构)

  • 客户端内部必须(MUST)是回调式(callback based)。客户端**应当(SHOULD)**大体遵循本节描述的结构。
  • 关键类是 Collector。它有一个方法(通常叫 collect),返回零个或多个指标及其样本(samples)。Collector 注册到 CollectorRegistry。
  • 数据暴露方式:把 CollectorRegistry 传给某个类/方法/函数的 "bridge",由 bridge 以 Prometheus 支持的格式返回指标。每次 CollectorRegistry 被刮取时,都必须回调每个 Collector 的 collect 方法。
  • 大多数用户直接交互的接口是 Counter、Gauge、Summary、Histogram 这四个 Collector。它们代表单个指标,应覆盖用户为自己的代码埋点时的绝大多数用例。
  • 更高级的用例(例如从其他监控/埋点系统代理指标)需要编写自定义 Collector。也可能需要写一个 "bridge",接收 CollectorRegistry 并产出其他监控/埋点系统能理解的格式,使用户只需关心一套埋点系统。
  • CollectorRegistry **应当(SHOULD)**提供 register() / unregister() 函数;一个 Collector 应当允许被注册到多个 CollectorRegistry。
  • 客户端库必须是线程安全的(MUST be thread safe)。
  • 对于 C 这类非面向对象语言,客户端库应在切实可行的范围内遵循此结构的精神。

6.4.3 Naming(命名)

  • 客户端库**应当(SHOULD)**沿用本文档中提到的函数/方法/类名,同时遵循所用语言的命名规范。示例:set_to_current_time() 适合 Python 方法名;Go 里更适合 SetToCurrentTime();Java 约定是 setToCurrentTime()。
  • 若因技术原因导致名称不同(例如不允许函数重载),**文档/帮助字符串应当(SHOULD)**把用户引导到其他名称。
  • 库**绝不能(MUST NOT)**提供与本文档给出的函数/方法/类同名或近似名、但语义不同的成员。

6.4.4 Metrics(指标)

  • Counter、Gauge、Summary、Histogram 是用户的主要接口。
  • Counter 与 Gauge 必须(MUST)包含在客户端库中。Summary 与 Histogram 至少必须提供其中一个(MUST)。
  • 这些指标应主要作为 file-static 变量使用,即与其所埋点的代码定义在同一文件中的全局变量。客户端库**应当(SHOULD)**支持这一点。常见用例是整体地对一段代码埋点,而不是在某个对象实例的上下文中对代码埋点。用户不应被迫在代码各处传递(plumbing)指标对象,客户端库应替他们处理。
  • 必须(MUST)有一个默认的 CollectorRegistry;标准指标必须默认隐式注册到其中,用户无需做任何额外工作。
  • 必须(MUST)提供一种方式让指标不注册到默认 CollectorRegistry,用于批处理作业和单元测试。自定义 Collector **应当(SHOULD)**也遵循这一点。
  • 指标具体如何创建因语言而异:有些语言(Java、Go)最好用 builder 方式;其他语言(Python)函数参数足够丰富,一次调用即可。
  • Java Simpleclient 示例:
class YourClass {
  static final Counter requests = Counter.build()
      .name("requests_total")
      .help("Requests.").register();
}
  • 这会用默认 CollectorRegistry 注册 requests;调用 build() 而非 register() 则不会注册(方便单元测试);也可以向 register() 传入 CollectorRegistry(方便批处理作业)。

6.4.5 Counter

  • Counter 是单调递增计数器。绝不能允许值下降,但可以重置为 0(例如服务器重启)。
  • Counter **必须(MUST)**有以下方法:
    • inc():加 1;
    • inc(double v):加指定数量;必须检查 v >= 0。
  • Counter 是 ENCOURAGED 具备:统计某段代码中抛出/引发的异常数量,并可选只统计特定异常类型。即 Python 中的 count_exceptions。
  • Counter 必须从 0 开始。

6.4.6 Gauge

  • Gauge 表示可升可降的值。
  • Gauge **必须(MUST)**有以下方法:
    • inc():加 1;
    • inc(double v):加指定数量;
    • dec():减 1;
    • dec(double v):减指定数量;
    • set(double v):设为指定值。
  • Gauge 必须从 0 开始;**可以(MAY)**提供让某 Gauge 从其他数值开始的方式。
  • Gauge **应当(SHOULD)**有以下方法:set_to_current_time():把 Gauge 设为当前 unixtime(秒)。
  • Gauge 是 ENCOURAGED 具备:
    • 跟踪某段代码/函数中"进行中"的请求数(Python 中为 track_inprogress);
    • 对一段代码计时并把 Gauge 设为其耗时秒数(对批处理作业有用)。Java 中为 startTimer/setDuration,Python 中为 time() 装饰器/上下文管理器。这应当与 Summary/Histogram 中的模式一致(只是用 set() 而非 observe())。

6.4.7 Summary

  • Summary 在滑动时间窗口上采样观测值(通常是请求耗时之类),提供对其分布、频率与总和的即时洞察。
  • 绝不能允许用户把 quantile 用作标签名,因为该名被内部用于标识 summary 的分位数。
  • Summary 是 ENCOURAGED 提供分位数作为导出项,但这些无法聚合且往往很慢。
  • Summary 必须允许不设分位数,因为仅 _count/_sum 就已经相当有用,并且这必须是默认行为。
  • Summary **必须(MUST)**有以下方法:observe(double v):观测给定数值。
  • Summary **应当(SHOULD)**有以下方法:为使用者提供按秒计时的方式(Python 中为 time() 装饰器/上下文管理器;Java 中为 startTimer/observeDuration)。绝不能提供秒以外的单位(若用户想要其他单位,可自行手工处理)。应与 Gauge/Histogram 的模式一致。
  • Summary 的 _count/_sum 必须从 0 开始。

6.4.8 Histogram

  • Histogram 允许可聚合的事件分布(例如请求延迟),其本质是"每个 bucket 一个 counter"。
  • 绝不能(MUST NOT)允许 le 作为用户设置的标签,因为 le 被内部用于标识 bucket。
  • **必须(MUST)**提供手动选择 buckets 的方式。**应当(SHOULD)**提供按 linear(start, width, count) 与 exponential(start, factor, count) 方式设置 buckets 的途径。count 必须包含 +Inf bucket。
  • Histogram **应当(SHOULD)**使用与其他客户端库相同的默认 buckets。
  • buckets 一旦指标创建后绝不可更改(MUST NOT)。
  • Histogram **必须(MUST)**有以下方法:observe(double v):观测给定数值。
  • Histogram **应当(SHOULD)**有以下方法:为使用者提供按秒计时的方式(Python time() 装饰器/上下文管理器;Java startTimer/observeDuration)。绝不能提供秒以外的单位。 应与 Gauge/Summary 的模式一致。
  • Histogram 的 _count/_sum 以及 buckets 必须从 0 开始。

Further metrics considerations:

  • 在本文档所述内容之外,按语言合理性地提供额外功能是 ENCOURAGED 的。
  • 如果存在可以让常见用例更简单的做法,那就去做——前提是它不会鼓励不良行为(例如次优的指标/标签布局,或在客户端做计算)。

6.4.9 Labels(标签)

  • 标签是 Prometheus 最强大的方面之一,但也容易被滥用。因此客户端库向用户提供标签的方式必须非常谨慎。
  • 客户端库**不应当(SHOULD NOT)**允许用户对同一个 Gauge/Counter/Summary/Histogram 或库提供的任何其他 Collector 使用不同的标签名。
  • 来自自定义 collector 的指标几乎总是应有一致的标签名。由于仍存在罕见但有效的例外,客户端库不应对此做校验。
  • 虽然标签很强大,但大多数指标不会有标签。因此 API 应允许标签,但不应让标签主导 API。
  • **必须(MUST)**允许在 Gauge/Counter/Summary/Histogram 创建时可选地指定标签名列表。**应当(SHOULD)**支持任意数量的标签名。**必须(MUST)**校验标签名符合文档要求。
  • 访问指标带标签维度的通用方式:通过 labels() 方法,它接受标签值列表或标签名→标签值的 map,返回一个 "Child"。随后可在 Child 上调用常用的 .inc()/.dec()/.observe() 等方法。
  • labels() 返回的 Child **应当(SHOULD)**可被用户缓存,避免重复查找——这在延迟敏感的代码中很重要。
  • 带标签的指标**应当(SHOULD)**支持 remove() 方法(签名与 labels() 相同),用于从指标中移除某个 Child 从而不再导出它;并支持 clear() 方法,移除指标的所有 Child。这两者都会使 Child 的缓存失效。
  • **应当(SHOULD)**有一种用默认值初始化指定 Child 的方式,通常就是调用 labels()。
  • 不带标签的指标必须始终被初始化,以避免指标缺失问题。

6.4.10 Metric names(指标名)

  • 指标名必须遵循规范。与标签名一样,这**必须(MUST)**在 Gauge/Counter/Summary/Histogram 以及库提供的任何其他 Collector 的使用中得到满足。
  • 许多客户端库以三部分设置名称:namespace_subsystem_name,其中只有 name 是必填的。
  • 必须(MUST)劝阻动态/生成的指标名或指标名的子部分,除非是自定义 Collector 从其他埋点/监控系统代理指标时。生成/动态的指标名说明你应该改用标签。

6.4.11 Metric description and help

  • Gauge/Counter/Summary/Histogram **必须(MUST)**要求提供指标描述/help。
  • 客户端库自带的所有自定义 Collector 的指标**必须(MUST)**有描述/help。
  • 建议把它做成必填参数,但不要校验其长度——因为如果某人真的不想写文档,我们也没法说服他。库自带的 Collector(以及生态内所有可能的地方)**应当(SHOULD)**有良好的指标描述,以树立榜样。

6.4.12 Exposition(暴露格式)

  • 客户端**必须(MUST)**实现 exposition formats 文档中所述基于文本的暴露格式。
  • 暴露指标的可复现顺序(reproducible order)是 ENCOURAGED 的(尤其是对人类可读格式),前提是能做到而不带来显著资源开销。

6.4.13 Standard and runtime collectors

  • 客户端库**应当(SHOULD)**尽可能提供下述 Standard exports。
  • 这些**应当(SHOULD)**实现为自定义 Collector,并默认注册到默认 CollectorRegistry 上。
  • **应当(SHOULD)**有一种禁用它们的方式,因为存在一些非常小众的用例中它们会碍事。

Process metrics(进程指标):这些指标以 process_ 为前缀。若在所使用语言/运行时中获取某个必要值有问题甚至不可能,客户端库应当(SHOULD)优先省略对应指标,而不是导出伪造、不准确或特殊值(如 NaN)。所有内存值单位为字节,所有时间单位为 unixtime/秒。

Metric name Help string Unit
process_cpu_seconds_total Total user and system CPU time spent in seconds. seconds
process_open_fds Number of open file descriptors. file descriptors
process_max_fds Maximum number of open file descriptors. file descriptors
process_virtual_memory_bytes Virtual memory size in bytes. bytes
process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes. bytes
process_resident_memory_bytes Resident memory size in bytes. bytes
process_heap_bytes Process heap size in bytes. bytes
process_start_time_seconds Start time of the process since unix epoch in seconds. seconds
process_threads Number of OS threads in the process. threads

Runtime metrics(运行时指标):此外,客户端库 ENCOURAGED 也提供对各自语言运行时合理的指标(如垃圾回收统计),并使用合适的语言前缀,例如 go_、hotspot_ 等。

6.4.14 Unit tests(单元测试)

  • 客户端库**应当(SHOULD)**有覆盖核心埋点库与暴露格式的单元测试。
  • 客户端库 ENCOURAGED 提供让用户易于对自己使用埋点代码进行单元测试的方式,例如 Python 中的 CollectorRegistry.get_sample_value。

6.4.15 Packaging and dependencies(打包与依赖)

  • 理想情况下,客户端库可以被引入任意应用以添加一些埋点,而不破坏该应用。
  • 因此给客户端库添加依赖时需要谨慎。例如:如果你添加的库使用了 Prometheus 客户端并要求某个库的版本 x.y,而应用在别处使用 x.z,这是否会对应用造成不利影响?
  • 建议在可能出现该问题的场景下,把核心埋点与各格式的 bridge/暴露分离。例如 Java simpleclient 的 simpleclient 模块无依赖,而 simpleclient_servlet 承载 HTTP 相关部分。

6.4.16 Performance considerations(性能考量)

  • 由于客户端库必须线程安全,需要某种并发控制,并且必须考虑在多核机器与应用上的性能。
  • 经验上性能最差的是互斥锁(mutexes)。
  • 处理器原子指令(atomic instructions)通常居中,一般可接受。
  • 避免不同 CPU 修改同一块 RAM 的做法效果最好,例如 Java simpleclient 中的 DoubleAdder——但有内存成本。
  • 如前所述,labels() 的结果应可缓存。支撑带标签指标的并发 map 往往相对较慢;对不带标签的指标做特化处理以避免 labels() 式查找能带来很大帮助。
  • 指标在 inc/dec/set 等操作时应当(SHOULD)避免阻塞,因为在刮取进行中把整个应用卡住是不可取的。
  • ENCOURAGED:为主要埋点操作(包括带标签的情况)建立基准测试(benchmarks)。
  • 在做暴露时应留意资源消耗,尤其是 RAM。可考虑通过流式输出结果来降低内存占用,并可能对并发刮取数量设上限。

引自:Writing client libraries

6.5 集成生态清单(Operating integrations)

除 client libraries 与 exporters/相关库外,Prometheus 还有许多其他通用集成点。本页列出部分集成;并非全部列出(功能重叠或仍在开发中)。exporter 默认端口 wiki 页也含少量非 exporter 的集成。

6.5.1 File Service Discovery

对 Prometheus 原生不支持的服务发现机制,file-based service discovery 提供集成接口(自行产出 target 文件)。

集成 说明
ElastiCache AWS ElastiCache 的 file SD
Kuma Kuma 服务网格
Lightsail AWS Lightsail
Netbox NetBox
Packet Packet(Equinix Metal)
Scaleway Scaleway

6.5.2 HTTP Service Discovery

允许从 HTTP endpoint 获取 targets(由外部服务动态提供目标列表)。

集成 说明
fastly-exporter 发现 Fastly 服务
ns1_exporter 发现 NS1 zones
NetBox via plugin 通过插件
NetBox via export templates 通过导出模板

6.5.3 Remote Endpoints and Storage

remote write / remote read 可透明地发送与接收样本,主要面向长期存储;建议仔细评估方案能否处理你的数据量。下表括号内 w=仅写、rw=读写。

远程存储 / 端点 能力
AppOptics w
AWS Timestream rw
Azure Data Explorer rw
Azure Event Hubs w
Chronix w
Cortex rw
CrateDB rw
Elasticsearch w
Gnocchi w
Google BigQuery rw
Google Cloud Spanner rw
Grafana Mimir rw
Graphite w
GreptimeDB rw
InfluxDB rw
Instana w
IRONdb rw
Kafka w
Last9 w
M3DB rw
Mezmo w
New Relic w
Oodle w
OpenObserve w
OpenTSDB w
QuasarDB rw
SignalFx w
Splunk rw
Sysdig Monitor w
TiKV rw
Thanos rw
VictoriaMetrics w
Wavefront w
  • 另有 Prom-migrator:在远程存储系统之间迁移数据的工具。

6.5.4 Alertmanager Webhook Receiver

对 Alertmanager 原生不支持的通知机制,webhook receiver 提供集成(涵盖值班/事件管理、IM 聊天、工单、短信/语音、日志与告警收敛分析等渠道)。以下为条目(部分):

AlertINT、alertmanager-webhook-logger、Alertsnitch、All Quiet、Asana、AWS SNS、Better Uptime、Canopsis、DingTalk、Discord、GitLab、Gotify、GELF、Grafana OnCall、HeyOnCall、Icinga2、iLert、IRC Bot、ITOC360、JIRAlert、Matrix(两个)、Notion、Parny、Phabricator/Maniphest、prom2teams、Rootly、Runframe、Ansible Tower(prom2tower)、Robusta、Signal、SIGNL4、Simplepush、SMS(sachet)、SMSEagle、SNMP traps、Squadcast、STOMP、Telegram bot、xMatters、XMPP Bot、Zenduty、Zoom、Zulip。

6.5.5 Management

Prometheus 不包含配置管理功能,可与现有系统集成或在其上构建。

集成 说明
Prometheus Operator 在 Kubernetes 上管理 Prometheus
Promgen Prometheus 与 Alertmanager 的 Web UI 与配置生成器

6.5.6 Other

集成 说明
Alert analysis(am2ch) 告警存 ClickHouse 并提供分析 dashboard
karma 告警 dashboard
PushProx 穿越 NAT 等网络环境的代理
Promdump kubectl 插件,导出与恢复数据块
Promregator Cloud Foundry 应用的发现与抓取
pint Prometheus 规则 linter

引自:Integrations

引自:Operating(该页仅有 frontmatter,无正文;为 Operating 章节的导航/索引页)

6.6 node_exporter 部署实践

node_exporter 是官方维护的机器指标导出器(prometheus/node_exporter,official),用于暴露大量硬件与内核相关的系统级指标,供 Prometheus 抓取。

⚠️ node_exporter *面向 nix 系统(Linux/Unix);Windows 有对应的 Windows exporter(prometheus-community/windows_exporter),流程与本节一致,仅二进制不同。

6.6.1 下载与运行

node_exporter 是单个静态二进制(以 tarball 分发),下载解压后直接运行即可:

# NOTE: Replace the URL with one from the "downloads" page.
# Node Exporter is available for multiple OS targets and architectures.
# Downloads are available at:
# https://github.com/prometheus/node_exporter/releases/download/v<VERSION>/node_exporter-<VERSION>.<OS>-<ARCH>.tar.gz
# <VERSION>、<OS>、<ARCH> 为占位符(如 1.10.2 / linux / amd64)
# 本例使用 Node Exporter 1.10.2,Linux amd64

wget https://github.com/prometheus/node_exporter/releases/download/v1.10.2/node_exporter-1.10.2.linux-amd64.tar.gz
tar xvfz node_exporter-1.10.2.linux-amd64.tar.gz
cd node_exporter-1.10.2.linux-amd64
./node_exporter

运行后日志会表明其监听 9100 端口(日志含 Listening on :9100)。验证 metrics endpoint:

curl http://localhost:9100/metrics                    # 输出全部指标
curl http://localhost:9100/metrics | grep "node_"     # 只看系统指标(前缀 node_)

6.6.2 配置 Prometheus 抓取

在 prometheus.yml 中新增 job(全局 scrape_interval 设为 15s):

global:
  scrape_interval: 15s

scrape_configs:
- job_name: node
  static_configs:
  - targets: ['localhost:9100']

安装并启动 Prometheus,用 --config.file 指向上述配置:

wget https://github.com/prometheus/prometheus/releases/download/v*/prometheus-*.*-amd64.tar.gz
tar xvf prometheus-*.*-amd64.tar.gz
cd prometheus-*.*
./prometheus --config.file=./prometheus.yml

6.6.3 expression browser 探索与示例指标

在 expression browser(http://localhost:9090/graph)的表达式栏输入 PromQL 探索。Node Exporter 专属指标前缀为 node_,如 node_cpu_seconds_total、node_exporter_build_info。常用示例:

Metric Meaning
rate(node_cpu_seconds_total{mode="system"}[1m]) 最后一分钟每秒处于 system mode 的平均 CPU 时间(秒)
node_filesystem_avail_bytes 非 root 用户可用的文件系统空间(bytes)
rate(node_network_receive_bytes_total[1m]) 最后一分钟每秒平均接收网络流量(bytes)

6.6.4 注意事项

  • node_exporter 属于"在目标机器上运行"的导出器,符合"每个导出器只监控恰好一个应用实例、与它同机紧邻运行"的部署原则(node_exporter 处理的是整机系统指标)。
  • 它只应被 Prometheus 抓取时才拉取指标,不应自己起定时器(同步抓取原则)。
  • node_exporter 的 collector 复杂度按 module 而异:mdadm 之类手工解析单文件的 collector 可以把指标做"对";meminfo 之类随内核版本变化的 collector 只做"刚好够"的转换。
  • 对与机器相关的 batch job,应使用 node_exporter 的 textfile collector 暴露指标,而不要用 Pushgateway(见 5.5 节)。

引自:Getting started with Prometheus

引自:Exporters and integrations

引自:node-exporter guide

💡 补充:生产部署还应在 systemd 中托管 node_exporter 并加 --collector. 白/黑名单裁剪掉不需要的 collector,减少抓取体积;同时注意 textfile collector 目录的权限与原子写入(先写临时文件再 mv),否则会出现读取到半截文件导致抓取失败。

6.7 cAdvisor 实践

cAdvisor(container Advisor,google/cadvisor,社区维护,列于 Miscellaneous 类)分析并暴露运行中容器的资源使用与性能数据,开箱即用提供 Prometheus metrics。在容器/编排场景中它与 node_exporter 互补:node_exporter 看整机,cAdvisor 看每个容器。

本节指南目标:用 Docker Compose 建立一个含 Prometheus、cAdvisor、Redis 的多容器环境,并查看 Redis 容器的容器级指标。

6.7.1 prometheus.yml 抓取配置

cAdvisor 自身即提供 Prometheus 可抓取的 /metrics endpoint,只需在 prometheus.yml 中加 job:

scrape_configs:
- job_name: cadvisor
  scrape_interval: 5s
  static_configs:
  - targets:
    - cadvisor:8080

💡 注意 cadvisor:8080 用的是 compose 网络中的服务名,而非 localhost。

6.7.2 Using Bind Mounts:完整 docker-compose.yml

version: '3.2'
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
    - 9090:9090
    command:
    - --config.file=/etc/prometheus/prometheus.yml
    volumes:
    - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
    depends_on:
    - cadvisor
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    ports:
    - 8080:8080
    volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:rw
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro
    depends_on:
    - redis
  redis:
    image: redis:latest
    container_name: redis
    ports:
    - 6379:6379

三个 service 的分工:

Service 作用
prometheus 使用本地 prometheus.yml(经 volumes 挂载进容器 /etc/prometheus/)
cadvisor 暴露 8080(cAdvisor metrics 默认端口),依赖多个宿主 volumes(/、/var/run、/sys、/var/lib/docker/)读取容器与宿主数据
redis 标准 Redis server,cAdvisor 会自动采集其容器指标,无需额外配置

⚠️ cAdvisor 的 volumes 挂载与权限模式(ro/rw)是它能否读到容器指标的关键,缺项会导致容器指标为空。

6.7.3 启动与验证

docker-compose up

启动成功后可见日志 msg="Server is ready to receive web requests.";用 docker-compose ps 确认三个容器均处于运行状态。

6.7.4 cAdvisor web UI

cAdvisor 自带 Web UI,可直接查看容器资源使用:

  • 总览:http://localhost:8080
  • 特定容器统计:http://localhost:8080/docker/<container>,如 http://localhost:8080/docker/redis、http://localhost:8080/docker/prometheus

6.7.5 expression browser 示例

在 Prometheus 的 expression browser(http://localhost:9090/graph)中查询容器指标。例:container_start_time_seconds 记录容器启动时间(秒),用 name 标签选择容器(对应 compose 的 container_name):

container_start_time_seconds{name="redis"}

6.7.6 替代方案:Inline Docker Configs

上述方案把 prometheus.yml 作为独立文件放在宿主上管理。Inline Docker Configs(Remote Deployments) 方案改用 Docker 的 configs 特性,把配置内联进 docker-compose.yml(version: '3.8'),无需在宿主上维护独立配置文件。要点:用 configs: prometheus_config: content: | 内联 scrape 配置;prometheus 服务下用 configs: - source: prometheus_config / target: /etc/prometheus/prometheus.yml 引用。

⚠️ Required Fields:使用 Docker configs 时必须显式给出数值形式的 uid、gid、mode:

  • uid: "65534"——65534 是 Prometheus 镜像中的 nobody 用户
  • gid: "65534"——nobody 组
  • mode: 0400——owner 只读

省略这些字段,或写成 "nobody" 之类字符串,会报错:strconv.Atoi: parsing "nobody": invalid syntax。

引自:Exporters and integrations

引自:cadvisor guide

💡 补充:采用 Kubernetes 时,通常不单独部署 cAdvisor——kubelet 已内置 cAdvisor 并在其 /metrics/cadvisor 路径暴露容器指标(该路径由 kubelet 服务发现或 kube-state-metrics 体系覆盖)。独立部署 cAdvisor 的常见难点是宿主 --volume 挂载与 cgroup v2 下的指标差异,跨内核版本时容器级指标命名可能变化,需在 dashboard 中做兼容。以上为经验补充,具体 endpoint 与挂载参数请以 cAdvisor 项目 README 与你所用版本为准。

6.8 multi-target exporter 模式(blackbox 型)

6.8.1 模式定义

multi-target exporter(多目标 exporter) 是指:exporter 通过网络协议获取 target 的指标;exporter 不必运行在被采集机器上;exporter 把 targets 与 query config string 作为 Prometheus GET 请求的参数;收到 GET 请求后才开始 scrape;可查询多个 target。

仅用于 blackbox、SNMP exporter 等场景——当目标上无法运行 exporter,或关心从网络外某点看网站的距离/延迟/可达性时。

6.8.2 运行 blackbox exporter

可作为普通程序、容器、后台服务、裸机、虚拟机运行;因通过网络查询与收发,需开放合适端口。用 Docker 启动:

docker run -p 9115:9115 prom/blackbox-exporter

(视系统配置可能需要 sudo。)成功日志最后一行应报 msg="Listening on address",如 address=:9115。

6.8.3 两种查询方式

  1. 查 exporter 自身指标(通常 /metrics):这是 whitebox monitoring。
curl 'localhost:9115/metrics'

返回 blackbox_exporter_build_info、go_gc_duration_seconds、go_goroutines、process_cpu_seconds_total、process_max_fds、process_open_fds、process_resident_memory_bytes、process_start_time_seconds、process_virtual_memory_bytes 等。

  1. 让 exporter 去 scrape 另一个 target("descriptive" endpoint,如 /probe):这是使用 multi-target exporter 时主要关心的方式。需在 HTTP GET 中提供 target 与 module 参数(target 是 URI 或 IP,module 必须在 exporter 配置中定义):
curl 'localhost:9115/probe?target=prometheus.io&module=http_2xx'

返回 probe_dns_lookup_time_seconds、probe_duration_seconds、probe_failed_due_to_regex、probe_http_content_length、probe_http_duration_seconds{phase="connect|processing|resolve|tls|transfer"}、probe_http_redirects、probe_http_ssl、probe_http_status_code、probe_http_version、probe_ip_protocol、probe_success 等。首次使用 Docker 时可能看到大多为 0、probe_success 0、probe_ip_protocol 6——Docker daemon 默认阻止 IPv6,故容器内 exporter 无法经 IPv6 连接。

6.8.4 配置 modules

容器内配置文件 config.yml 是 GitHub 仓库中 blackbox.yml 的副本,下载:

curl -o blackbox.yml https://raw.githubusercontent.com/prometheus/blackbox_exporter/master/blackbox.yml

原始片段定义 http_2xx(prober: http)与 http_post_2xx(prober: http,http.method: POST)。为 http_2xx 增加 preferred_ip_protocol: "ip4":

modules:
  http_2xx:
    prober: http
    http:
      preferred_ip_protocol: "ip4"
  http_post_2xx:
    prober: http
    http:
      method: POST

用 --config.file="blackbox.yml" 指定配置;Docker 下先用 --mount 把文件挂进容器(macOS 需先在 Preferences→File Sharing 允许目录,再 Apply & Restart)。停止旧容器用 ctrl+c:

docker \
  run -p 9115:9115 \
  --mount type=bind,source="$(pwd)"/blackbox.yml,target=/blackbox.yml,readonly \
  prom/blackbox-exporter \
  --config.file="/blackbox.yml"

该命令含义:run 容器并把宿主 9115 映射到容器 9115;mount 当前目录 $(pwd) 下 blackbox.yml 到容器 /blackbox.yml(readonly);使用镜像 prom/blackbox-exporter;以 --config.file 指定 /blackbox.yml。成功日志含 "Starting blackbox_exporter"、"Loaded config file"、"Listening on address"。

再次探测可见 probe_success 1、probe_http_status_code 200、probe_ip_protocol 4、probe_http_ssl 1、probe_http_redirects 1、probe_ssl_earliest_cert_expiry、probe_tls_version_info{version="TLS 1.3"}、probe_http_uncompressed_body_length 等。exporter 还提供小型 Web UI(localhost:9115)查看最近探测、已加载配置与调试信息。

6.8.5 让 Prometheus 抓取 exporter 自身

最小配置让 Prometheus 抓取 exporter 自身(metrics_path: /metrics,target localhost:9115)。Docker for Mac/Windows 需用 host.docker.internal:9115(不建议用于生产)。

docker \
  run --network="host"\
  --mount type=bind,source="$(pwd)"/prometheus.yml,target=/prometheus.yml,readonly \
  prom/prometheus \
  --config.file="/prometheus.yml"

macOS/Windows 版本改为 run -p 9090:9090 加同样的 --mount 与 prom/prometheus --config.file="/prometheus.yml"。检查 localhost:9090/targets 中 blackbox 为绿色 UP(DOWN 说明 exporter 未运行,UNKNOWN 说明再等几秒)。

6.8.6 让 Prometheus 查询 /probe

新增 job blackbox-http,设 metrics_path: /probe,并在 params: 下指定 module: [http_2xx] 与 target: [prometheus.io];保存后 ctrl+C 停止并重启容器。终端应出现 "Server is ready to receive web requests."。

该做法缺点:真实 targets 放在 param config 中不直观;instance label 值是 exporter 地址而非关心的目标;看不出探测的是哪个 URL,多个 URL 会混在一条指标里。

6.8.7 用 relabeling 修正

三条关键事实:

  1. 所有以 __ 开头的 label 在 scrape 后被丢弃;
  2. 可设置 __param_<name> 内部 label 来设置 scrape 请求的 URL 参数;
  3. __address__ 由 static_configs 的 targets 设置,是 scrape 请求的 hostname,默认随后用于设置每指标附带的 instance label。

改进配置:params 只留 module: [http_2xx],targets 移到 static_configs(http://prometheus.io、https://prometheus.io、http://example.com:8080),relabel_configs 三条:

  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - source_labels: [__param_target]
      target_label: instance
    - target_label: __address__
      replacement: localhost:9115  # The blackbox exporter’s real hostname:port

效果:relabel 前请求为 "http://prometheus.io/probe?module=http_2xx",relabel 后为 "http://localhost:9115/probe?target=http://prometheus.io&module=http_2xx"。

逐条解释:

  • 从 __address__ 取值写入 __param_target(加 target 参数);
  • 从 __param_target 取值创建 instance label(返回指标带 instance="http://prometheus.io");
  • 把 __address__ 写为 localhost:9115,使请求发给 exporter 而非直接发给 target URI。

这些规则可与 service discovery 组合使用(服务发现同样写入 __address__)。

6.8.8 多 target、多 module 单 job

job-level module 是 list 但 blackbox 只用第一个;改为在每个 static_config 内用 labels.module 指定(如 module: http_2xx、module: icmp),并新增 relabel 规则注入 __param_module:

  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - source_labels: [__param_target]
      target_label: instance
    - source_labels: [module]
      target_label: __param_module
    - target_label: __address__
      replacement: localhost:9115

6.8.9 小结

  • 理解多目标 exporter 模式及其适用范围;
  • 运行定制 module 的 blackbox exporter;
  • 用 relabeling 让 Prometheus 抓取带 prober label 的指标。

引自:Understanding and using the multi-target exporter pattern

第 7 章 应用集成实战

本章把第 5、6 章的规则落到可运行的代码上:先用官方 Go 指南做一次完整的"最小可用"埋点(7.1),再做一次"HTTP 服务埋点"端到端教程(7.2),最后补上 OpenTelemetry 接入(7.3)。

7.1 Go 应用埋点教程

Prometheus 官方提供 Go 客户端库 github.com/prometheus/client_golang;本教程创建一个通过 HTTP 暴露 Prometheus 指标的简单 Go 应用。完整 API 文档见 GoDoc。

7.1.1 安装依赖

go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promauto
go get github.com/prometheus/client_golang/prometheus/promhttp

7.1.2 Go 暴露机制

需提供 /metrics HTTP endpoint,使用 prometheus/promhttp 库的 Handler 作为 handler 函数。

7.1.3 最小示例:只暴露 Go 默认指标

通过 http://localhost:2112/metrics 暴露 Go 默认指标。引入包路径为 net/http、github.com/prometheus/client_golang/prometheus、github.com/prometheus/client_golang/prometheus/collectors、github.com/prometheus/client_golang/prometheus/promhttp:

func main() {
	reg := prometheus.NewRegistry()
	reg.MustRegister(
		collectors.NewGoCollector(),
		collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
	)
	http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
	http.ListenAndServe(":2112", nil)
}

启动与访问:

go run main.go
curl http://localhost:2112/metrics

7.1.4 添加自定义指标

注册一个 myapp_processed_ops_total counter,每 2 秒自增一次。引入包路径 net/http、time、github.com/prometheus/client_golang/prometheus、.../promauto、.../promhttp:

type metrics struct {
	opsProcessed prometheus.Counter
}

func newMetrics(reg prometheus.Registerer) *metrics {
	m := &metrics{
		opsProcessed: promauto.With(reg).NewCounter(prometheus.CounterOpts{
			Name: "myapp_processed_ops_total",
			Help: "The total number of processed events",
		}),
	}
	return m
}

func recordMetrics(m *metrics) {
	go func() {
		for {
			m.opsProcessed.Inc()
			time.Sleep(2 * time.Second)
		}
	}()
}

在 main 中组装:reg := prometheus.NewRegistry()、m := newMetrics(reg)、recordMetrics(m),再 http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) 与 http.ListenAndServe(":2112", nil)。

输出中可见 help、type 与当前值:

# HELP myapp_processed_ops_total ...
# TYPE myapp_processed_ops_total counter
myapp_processed_ops_total 5

7.1.5 抓取配置

scrape_configs:
- job_name: myapp
  scrape_interval: 10s
  static_configs:
  - targets:
    - localhost:2112

7.1.6 其他 Go 客户端特性

gauges、histograms、non-global registries(prometheus.Registry)、推送指标到 PushGateway 的 prometheus/push、Prometheus 与 Graphite 的桥接等。

小结:共创建两个示例应用(仅默认 Go 指标 / 含自定义 counter),并配置 Prometheus 抓取。

引自:Instrumenting a Go application for Prometheus

7.2 Go HTTP 服务埋点完整教程

目标:创建简单 Go HTTP server,通过添加 counter 指标来插桩,统计服务器处理的总请求数。

7.2.1 初始服务器

含 /ping 端点,返回 pong:

package main

import (
   "fmt"
   "net/http"
)

func ping(w http.ResponseWriter, req *http.Request) {
   fmt.Fprintf(w,"pong")
}

func main() {
   http.HandleFunc("/ping", ping)

   http.ListenAndServe(":8090", nil)
}

编译运行:

go build server.go
./server

检查点:浏览器打开 http://localhost:8090/ping,必须看到 pong。

7.2.2 选择指标类型

统计 ping 端点请求数,counter 合适,因为请求数只增不减。

7.2.3 创建 Prometheus counter

type metrics struct {
	pingCounter prometheus.Counter
}

func newMetrics(reg prometheus.Registerer) *metrics {
	m := &metrics{
		pingCounter: promauto.With(reg).NewCounter(
			prometheus.CounterOpts {
				Name: "ping_request_count",
				Help: "No of requests handled by Ping handler",
			}),
	}
	return m
}

7.2.4 更新 ping Handler

用 metrics.pingCounter.Inc() 增加计数:

func ping(m *metrics) func(w http.ResponseWriter, req *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		m.pingCounter.Inc()
		fmt.Fprintf(w, "pong")
	}
}

7.2.5 注册指标并暴露

func main() {
	reg := prometheus.NewRegistry()
	m := newMetrics(reg)

	http.HandleFunc("/ping", ping(m))
	http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
	http.ListenAndServe(":8090", nil)
}

promhttp 相关要点:

  • prometheus.MustRegister 函数将 pingCounter 注册到 default registry;
  • 暴露指标需 Go Prometheus client library 的 promhttp 包;
  • promhttp.Handler() 提供一个 http.Handler,暴露注册在 default registry 中的指标。

完整示例代码(main.go 结构):import 包含 "fmt"、"net/http"、github.com/prometheus/client_golang/prometheus、github.com/prometheus/client_golang/prometheus/promauto、github.com/prometheus/client_golang/prometheus/promhttp;定义 type metrics struct{ pingCounter prometheus.Counter };newMetrics(reg prometheus.Registerer) *metrics;ping(m *metrics) func(...);main() 中 reg := prometheus.NewRegistry()、m := newMetrics(reg)、http.HandleFunc("/ping", ping(m))、http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))、http.ListenAndServe(":8090", nil)。

7.2.6 运行与验证

go mod init prom_example
go mod tidy
go run server.go

多次访问 localhost:8090/ping,再请求 localhost:8090/metrics 查看指标。ping_request_count 显示 /ping 端点被调用了 3 次。

说明:default registry 自带 Go runtime metrics 的 collector,因此会看到 go_threads、go_goroutines 等其他指标。

7.2.7 接入 Prometheus 抓取

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ["localhost:9090"]
  - job_name: simple_server
    static_configs:
      - targets: ["localhost:8090"]

启动:prometheus --config.file=prometheus.yml

引自:Instrumenting HTTP server written in Go

7.2.8 配套告警与可视化

官方教程延续此例,用 ping_request_count > 5 作为告警条件(教程简化场景,真实最佳实践另见 practices/alerting),配置 Alertmanager 的 webhook receiver,并通过 rules.yml 定义 CountGreaterThan5 规则(for: 10s);再用 Grafana 建 Ping Request Count 面板。这两条链路分别是第 4 篇第 4 章(Alertmanager)与本章 8.2 节(Grafana)的直接应用,此处不再重复配置细节。

引自:Alerting based on metrics

7.3 OpenTelemetry 接入(OTLP)

Prometheus 支持通过 HTTP 方式接收 OTLP(OpenTelemetry Protocol)数据,可作为 OTel 指标的后端。

7.3.1 启用 OTLP receiver

默认关闭(与 Remote Write receiver 类似),因为 Prometheus 可无认证运行,未显式配置就接收来流量不安全。开启 CLI flag:

$ prometheus --web.enable-otlp-receiver

之后 Prometheus 在 HTTP 路径 /api/v1/otlp/v1/metrics 接收 OTLP 指标。

7.3.2 发送端配置

发送端需告知 Prometheus endpoint 并使用 OTLP 的 HTTP 模式(gRPC 通常是默认)。OTel SDK/插桩库可经标准环境变量配置:

export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:9090/api/v1/otlp

注意:OpenTelemetry 规范要求 OTEL_EXPORTER_OTLP_METRICS_ENDPOINT 作为 base URL,/v1/metrics 会自动追加(参见 opentelemetry-python #2443)。

关闭 traces 与 logs:

export OTEL_TRACES_EXPORTER=none
export OTEL_LOGS_EXPORTER=none

OTLP 指标默认推送间隔 60 秒,设为 15 秒:

export OTEL_METRIC_EXPORT_INTERVAL=15000

若插桩库未默认提供 service.name 与 service.instance.id,强烈建议设置:

export OTEL_SERVICE_NAME="my-example-service"
export OTEL_RESOURCE_ATTRIBUTES="service.instance.id=$(uuidgen)"

约定:service.instance.id 每个实例唯一;资源属性变化时须生成新的 service.instance.id;推荐每次实例启动生成新 UUID。

配置示例文件:documentation/examples/prometheus-otlp.yml。

7.3.3 启用 out-of-order ingestion

OTel collector 鼓励 batching,多个 collector 副本发送数据且样本无序,可能乱序:

storage:
  tsdb:
    out_of_order_time_window: 30m

30 分钟乱序窗口对多数场景够用,可按需调整。

7.3.4 提升(promote)resource attributes

默认不提升任何属性。推荐提升集合(写到 otlp.promote_resource_attributes):

service.instance.id、service.name、service.namespace、service.version、cloud.availability_zone、cloud.region、container.name、deployment.environment、deployment.environment.name、k8s.cluster.name、k8s.container.name、k8s.cronjob.name、k8s.daemonset.name、k8s.deployment.name、k8s.job.name、k8s.namespace.name、k8s.pod.name、k8s.replicaset.name、k8s.statefulset.name。

7.3.5 查询时纳入 resource attributes

所有 OTel resource attributes 默认(除 service.instance.id、service.namespace、service.name 外)都转成特殊 target_info 指标的 label,可通过 join target_info 在查询中使用未提升的属性。推荐用实验性 PromQL 函数 info,需开启:

--enable-feature=promql-experimental-functions

示例查询:

info(rate(http_server_request_duration_seconds_count[2m]), {k8s_cluster_name=~".+"})

等价原始 join 查询:

rate(http_server_request_duration_seconds_count[2m])
* on (job, instance) group_left (k8s_cluster_name)
target_info

原理:rate(...) 结果被 target_info 中共享相同 job/instance 的 k8s_cluster_name label 增强,job/instance 类似 SQL 外键;k8s_cluster_name 对应 OTel 资源属性 k8s.cluster.name(Prometheus 默认把点转为下划线)。

info 函数通常比原始 join 更快,因为它只选择 job/instance 匹配的 target_info 序列;更重要的是它解决 join 的老问题:当 identifying labels 之外的 label 值发生 churn 时,旧 target_info 未被标记 stale,会在 PromQL lookback delta(默认 5 分钟)内新旧重叠,导致 join 查询因存在两条匹配序列而失败;info 总是选最新样本的序列,不受影响。

7.3.6 target_info 与资源属性的关系

处理 OTLP 写请求时,若 resource 含 service.instance.id 和/或 service.name,Prometheus 为每个 resource 生成 info 指标 target_info:

  • instance label 取 service.instance.id;
  • job label 取 service.name;
  • 若存在 service.namespace,会加前缀到 job(<service.namespace>/<service.name>)。

默认 service.name、service.namespace、service.instance.id 本身不加入 target_info(已转为 job/instance)。可启用以下参数在转换之外再直接加入:

otlp:
  keep_identifying_resource_attributes: true

其余资源属性也作为 label 加入 target_info,名称按 Prometheus 格式转换(默认点转下划线)。若 resource 既无 service.instance.id 也无 service.name,则不生成 target_info。 每个 resource 的 OTel 指标都被转成对应 Prometheus 时间序列,并在生成 target_info 时附加正确的 instance/job label。

7.3.7 UTF-8 与 translation_strategy

从 3.x 起 Prometheus 支持指标名与 label 的 UTF-8,可省略 OpenTelemetry 的 Prometheus normalization translator package。内容协商允许 UTF-8 并不要求指标名必须含新字符;OTLP 指标转换方式取决于 endpoint 配置。UTF-8 在存储与 UI 中默认启用,但 OTLP receiver 的 translation_strategy 默认仍为旧的归一化 UnderscoreEscapingWithSuffixes。

四种 translation strategy(其中两种需启用 UTF-8):

策略 行为 是否需要 UTF-8
UnderscoreEscapingWithSuffixes(默认) 完全转义以兼容经典 Prometheus 指标名,并追加 type/unit 后缀 否
UnderscoreEscapingWithoutSuffixes 同样完全转义但不追加 type/unit 后缀,可能造成指标名冲突,需谨慎测试 否
NoUTF8EscapingWithSuffixes 不把特殊字符转 _,原生使用 OTel 格式,尤配合语义约定;会附加单位与 counter 的 _total 等后缀以防同名不同型/单位冲突 是
NoTranslation 完全绕过指标与 label 名翻译,原样透传;可能因缺少后缀导致同名不同型/单位冲突 是

配置示例:

otlp:
  # Ingest OTLP data keeping UTF-8 characters in metric/label names.
  translation_strategy: NoTranslation

7.3.8 Delta Temporality

OTel 规范同时支持 Delta 与 Cumulative temporality;delta 常见于 statsd/graphite,而 cumulative 是 Prometheus 默认。当前 Prometheus 内嵌 OpenTelemetry-Collector-contrib 的 delta to cumulative processor,可摄入 delta 并在存入 TSDB 前转成等价 cumulative。该特性为 experimental,需以 feature-flag otlp-deltatocumulative 启动 Prometheus 才能使用。团队仍在研究更高效的 OTLP delta 处理方式。

引自:Using Prometheus as your OpenTelemetry backend

💡 补充:接入 OTLP 时最容易踩的两个坑——① 忘记 --web.enable-otlp-receiver,客户端一直报 404;② endpoint 写成 http://localhost:9090/api/v1/otlp/v1/metrics(把规范自动追加的 /v1/metrics 又写了一遍),导致路径变成 .../otlp/v1/metrics/v1/metrics。正确做法是只写 base http://localhost:9090/api/v1/otlp。

第 8 章 可视化

指标存下来之后,下一步是"看见它"。Prometheus 官方文档给出四种可视化路径:Server 自带的表达式浏览器(ad-hoc 查询)、Grafana(推荐的生产级 dashboard)、Perses(新兴的 dashboard-as-code 平台)、以及 Console 模板(Go 模板驱动的自定义 console,但 3.0 起不再随包分发)。本章逐一说明,最后给出选型建议。

8.1 表达式浏览器(Expression browser)

  • 位置:expression browser 位于 Prometheus server 的 /graph,可输入任意 expression 并以 table 形式或随时间绘制的 graph 查看结果。
  • 定位:主要用于 ad-hoc queries 与 debugging。
  • 绘制 graph 的推荐工具:使用 Grafana 或 Console templates。

引自:Expression browser

8.2 Grafana 集成

8.2.1 是什么 / 为什么

Grafana 是开源的分析与可视化平台,用于监控和分析来自多种数据源的指标,支持创建、探索、分享交互式 dashboard,可集成 Prometheus、InfluxDB、Elasticsearch 等数据库;广泛用于 observability,提供告警、插件扩展性与灵活查询编辑器。

版本说明:Grafana 自 2.5.0(2015-10-28) 起内置 Prometheus 数据源。

8.2.2 安装与访问

8.2.3 创建 Prometheus 数据源步骤

  1. 点击侧边栏 "cogwheel"(齿轮)打开 Configuration 菜单;
  2. 点击 "Data Sources";
  3. 点击 "Add data source";
  4. 类型选择 "Prometheus";
  5. 设置合适的 Prometheus server URL(例如 http://localhost:9090/);
  6. 按需调整其他数据源设置(如选择正确的 Access method);
  7. 点击 "Save & Test" 保存新数据源。

8.2.4 创建 Prometheus graph 步骤

按标准方式新增 Grafana graph,然后:

  1. 点击 graph 标题,再点 "Edit";
  2. 在 "Metrics" tab 下选择你的 Prometheus 数据源(右下角);
  3. 在 "Query" 字段输入任意 Prometheus expression,同时用 "Metric" 字段通过自动补全查找指标;
  4. 用 "Legend format" 输入格式化时间序列图例名,例如仅显示返回结果的 method 与 status label 并以短横分隔,可用图例格式串 {{method}} - {{status}};
  5. 调整其他 graph 设置直至可用。

版本建议:在 Grafana 7.2 及之后,推荐在 rate 与 increase 函数中使用 $__rate_interval 变量。

8.2.5 从 Grafana.com 导入预建 dashboard

Grafana.com 维护共享 dashboard 集合(https://grafana.com/grafana/dashboards/),可下载用于独立 Grafana 实例;用 Grafana.com 的 "Filter" 选项仅浏览 "Prometheus" 数据源的 dashboard。当前必须手动编辑下载的 JSON 文件,把 datasource: 条目改为你为 Prometheus server 所选的 Grafana 数据源名;再用 "Dashboards" → "Home" → "Import" 导入编辑后的 dashboard 文件。

引自:Grafana support for Prometheus

8.2.6 可视化实战(教程)

目标:用 Grafana 创建简单 dashboard,可视化 ping_request_count 指标(来自 7.2 节的 Go HTTP 服务埋点教程)。

为何用 Grafana:在 Prometheus 上跑查询看到的图是用于 ad-hoc 查询的。Grafana 与 Console Templates 是推荐的两种建图方式。

安装与设置:按 https://grafana.com/docs/grafana/latest/installation/requirements/#supported-operating-systems 中对应操作系统的步骤安装并运行。

  1. 浏览器打开 http://localhost:3000;
  2. 使用默认凭据登录:用户名 admin、密码 admin,登录后设置新凭据。

添加 Prometheus 数据源:

  1. 点击侧边栏齿轮图标,选择 Data Sources(⚙ > Data Sources);
  2. Data Sources 界面可见 Grafana 支持多种数据源(如 Graphite、PostgreSQL 等),选择 Prometheus 设置;
  3. 在 HTTP 段输入 URL http://localhost:9090,点击 Save and Test。

创建第一个 dashboard:

  1. 点击侧边栏 + 图标,选择 Dashboard;
  2. 下一屏点击 Add new panel;
  3. 在 Query 标签输入 PromQL 查询,此处直接输入 ping_request_count;
  4. 访问 ping 端点几次并刷新 Graph,验证是否正常工作;
  5. 右侧 Panel Options 下将 Title 设为 Ping Request Count;
  6. 点击右上角 Save 图标保存 dashboard。

引自:Visualizing metrics using Grafana

8.3 Perses

  • Perses 是开源 dashboard 与可视化平台,面向 observability,原生支持 Prometheus 作为数据源;可让用户创建、管理、分享 dashboard 以监控指标与可视化数据。
  • 定位:Perses 旨在提供简单、灵活、可扩展的替代方案,聚焦易用性、社区驱动开发、GitOps 能力与 dashboard as code 方法。
  • 安装:见官方 Perses 文档(安装于容器:https://perses.dev/perses/docs/installation/in-a-container/)。
  • 使用:默认 Perses 监听端口 8080,web UI 在 http://localhost:8080;默认无登录。
  • 创建 Prometheus 数据源:在 Perses 中设置数据源的方式参见 Perses 文档 datasources 概念页;与 Prometheus 实例的连接配置完成后,即可从 Dashboard 与 Explore 视图查询。
  • 导入预建 dashboard:Perses 提供一组预建 dashboard 可导入实例;这些 dashboard 由社区维护,位于 Perses dashboard 仓库(https://github.com/perses/community-dashboards)。

引自:Perses support for Prometheus

8.4 Console 模板(Console templates)

8.4.1 重要变更(3.0 起不再打包)

Caution

:自 Prometheus 3.0 起,console templates 与 libraries 不再随 Prometheus 打包。若要使用,必须通过 --web.console.templates 与 --web.console.libraries 命令行参数自行提供 templates 与 libraries。官方页面仅为历史参考并演示 console templates 能力;请知悉,任何引自 Prometheus 2.x 分支的 console libraries 已不再维护,且可能含已知安全漏洞(CVEs)。

能力与取舍:

  • console templates 使用 Go templating language 创建任意 console,由 Prometheus server 提供。
  • 这是最强大的模板方式,易于源码管理;但有学习曲线,新手建议先用 Grafana。

8.4.2 Getting started

Prometheus 自带一套示例 console,运行中的 Prometheus 在 /consoles/index.html.example;若 Prometheus 抓取带 job="node" label 的 Node Exporter,会显示 Node Exporter consoles。

示例 console 的 5 个部分:① 顶部 navigation bar;② 左侧 menu;③ 底部 time controls;④ 中间 main content(通常是 graphs);⑤ 右侧 table。

  • navigation bar 用于链接到其他系统(如其他 Prometheis、文档等);
  • menu 用于同一 Prometheus server 内部导航(便于在新标签页打开 console 关联信息);
  • 两者均在 console_libraries/menu.lib 中配置;
  • time controls 可改变 graph 的 duration 与 range;console URL 可分享且他人看到相同 graphs;
  • 主内容通常是 graphs,提供可配置的 JavaScript 绘图库,负责向 Prometheus 请求数据并通过 Rickshaw 渲染;
  • 右侧 table 以比 graphs 更紧凑的形式展示统计。

示例 console 代码(展示 tasks 数、up 数、平均 CPU、平均内存,以及 queries-per-second 图):

{{template "head" .}}

{{template "prom_right_table_head"}}
<tr>
  <th>MyJob</th>
  <th>{{ template "prom_query_drilldown" (args "sum(up{job='myjob'})") }}
      / {{ template "prom_query_drilldown" (args "count(up{job='myjob'})") }}
  </th>
</tr>
<tr>
  <td>CPU</td>
  <td>{{ template "prom_query_drilldown" (args
      "avg by(job)(rate(process_cpu_seconds_total{job='myjob'}[5m]))"
      "s/s" "humanizeNoSmallPrefix") }}
  </td>
</tr>
<tr>
  <td>Memory</td>
  <td>{{ template "prom_query_drilldown" (args
       "avg by(job)(process_resident_memory_bytes{job='myjob'})"
       "B" "humanize1024") }}
  </td>
</tr>
{{template "prom_right_table_tail"}}

{{template "prom_content_head" .}}
<h1>MyJob</h1>

<h3>Queries</h3>
<div id="queryGraph"></div>
<script>
new PromConsole.Graph({
  node: document.querySelector("#queryGraph"),
  expr: "sum(rate(http_query_count{job='myjob'}[5m]))",
  name: "Queries",
  yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
  yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
  yUnits: "/s",
  yTitle: "Queries"
})
</script>

{{template "prom_content_tail" .}}

{{template "tail"}}
  • prom_right_table_head 与 prom_right_table_tail 模板承载右侧表格,可选。
  • prom_query_drilldown 会求值传入的 expression、格式化,并链接到 expression browser 中的表达式;第 1 个参数是表达式,第 2 个是用单位,第 3 个是输出格式;仅第 1 个必需。
  • prom_query_drilldown 第 3 参数的合法输出格式:
格式 含义
未指定 默认 Go 显示输出
humanize 用 metric prefixes 显示
humanizeNoSmallPrefix 绝对值 >1 用 metric prefixes,绝对值 <1 显示 3 位有效数字(避免 humanize 产生的 milliqueries/s 之类单位)
humanize1024 以 1024 而非 1000 为基做 humanize(常与第二参数 B 搭配产生 KiB、MiB)
printf.3g 显示 3 位有效数字

自定义格式可定义,示例见 prom.lib(release-2.55 分支)。

8.4.3 Graph Library 调用方式

<div id="queryGraph"></div>
<script>
new PromConsole.Graph({
  node: document.querySelector("#queryGraph"),
  expr: "sum(rate(http_query_count{job='myjob'}[5m]))"
})
</script>

head 模板加载所需的 Javascript 与 CSS。

Graph library 参数表(逐项):

参数 必需 含义 默认值
expr 必需 要绘制的表达式,可为列表 —
node 必需 渲染进的 DOM 节点 —
duration 可选 图的时间跨度 1 小时
endTime 可选 图结束的 Unixtime now
width 可选 图宽度(不含标题) 自动检测
height 可选 图高度(不含标题与图例) 200 像素
min 可选 x 轴最小值 最低数据值
max 可选 y 轴最大值 最高数据值
renderer 可选 图类型 line 或 area(堆叠图) line
name 可选 图例与 hover detail 中的标题;传字符串时 [[ label ]] 会被替换为 label 值,传函数时会被传入 labels map 且应返回字符串,可为列表 —
xTitle 可选 x 轴标题 Time
yUnits 可选 y 轴单位 空
yTitle 可选 y 轴标题 空
yAxisFormatter 可选 y 轴数字格式化器 PromConsole.NumberFormatter.humanize
yHoverFormatter 可选 hover detail 数字格式化器 PromConsole.NumberFormatter.humanizeExact
colorScheme 可选 绘图配色,可为十六进制色码列表或 Rickshaw 支持的 color scheme 名之一 'colorwheel'
  • 若 expr 与 name 均为列表,长度必须相同,name 会应用于对应 expression 的 plots。
  • yAxisFormatter 与 yHoverFormatter 的合法选项:PromConsole.NumberFormatter.humanize(用 metric prefixes)、PromConsole.NumberFormatter.humanizeNoSmallPrefix(绝对值 >1 用 metric prefixes,<1 用 3 位有效数字)、PromConsole.NumberFormatter.humanize1024(以 1024 而非 1000 为基)。

引自:Console templates

8.4.4 Console 设计实践(Consoles and dashboards)

重要警示(版本变更):自 Prometheus 3.0 起,console templates 与 console libraries 不再随 Prometheus 一起打包。若要使用 console templates,必须自备模板与库,并通过命令行参数 --web.console.templates 与 --web.console.libraries 指定。官方页面仅为历史参考,用于演示 console templates 的能力;2.x 分支中引用的 console libraries 已不再维护,可能包含已知安全漏洞(CVE)。

设计理念:不要试图在一张 dashboard 上展示尽可能多的数据,否则会因信息过载而变得难以理解,连系统专家也难以提炼含义。

应改为思考最可能的失败模式,以及如何用 console 区分它们;善用服务的结构。例如在线服务系统是一棵服务树时,下层服务延迟是典型问题——不要把所有服务信息放进一张大 dashboard,而应为每个服务建独立 dashboard,包含它所对话的每个服务的延迟与错误,从而从顶层逐层向下定位到出问题的服务。

有效指南:

  • 一个 console 上的图不超过 5 个;
  • 每个图上不超过 5 条线(plots);若是堆叠/面积图可以多些;
  • 使用官方模板示例时,右侧表格不超过 20–30 行。

超出上述限制时,应降低次要信息的可见性:改画聚合而非细分数据、移到右侧表格、或在很少用到时直接删除(随时可在 expression browser 中查看)。

多主人问题:一组 console 很难同时服务多个"主人"——oncall 时想知道"什么坏了",开发时想知道"多少人命中了 corner case X",两者需求差异很大;此时可维护两套独立的 console。

引自:Consoles and dashboards

8.5 可视化选型建议

引自:Visualization(该页仅有 frontmatter,无正文;为 Visualization 章节的导航/索引页,正文由 browser、grafana、perses、consoles 子页面承载)

场景 推荐方案 理由
临时排查、验证一条 PromQL 对不对 表达式浏览器(/graph) 无需额外部署,table/graph 两种视图,官方定位就是 ad-hoc 与 debugging
生产级 dashboard、团队共享、告警联动 Grafana 自 2.5.0 起内置 Prometheus 数据源;生态成熟、面板丰富、支持 $__rate_interval 等实用变量;有 Grafana.com 预建 dashboard 库
dashboard as code / GitOps 流水线 Perses 原生支持 Prometheus,聚焦 GitOps 与 dashboard as code,默认无登录、轻量(端口 8080)
极客式的自定义 console(Go 模板、可拼接任意 HTML/JS) Console 模板 最强大、易源码管理,但有学习曲线;3.0 起不再随包分发,需自备模板并通过 --web.console.templates / --web.console.libraries 指定,且官方不再维护、可能存在 CVE
新手 / 快速上手 Grafana(而非 Console 模板) 官方明确建议新手先用 Grafana

通用建图原则(来自 console 设计实践,对 Grafana 同样适用):

  • 一张图不要塞太多信息,思考"最可能的失败模式"而非"能放多少数据";
  • 图数量、线条数、表格行数都应有上限(5 / 5 / 20–30 的经验值);
  • 按服务/子系统拆分 dashboard,而不是做一张超级大盘;
  • 长期维护的 dashboard 应以代码方式管理(Perses / Grafana 的 dashboard JSON / Mixins)。

第 9 章 规范(OpenMetrics / Remote Write / Native Histograms)

本章是全篇最"硬"的一章,逐份梳理 Prometheus 生态的四套规范性文档:OpenMetrics 1.0 / 2.0(暴露格式)、Remote Write 1.0 / 2.0(远程写)、Native Histograms(原生直方图数据模型与编码),最后给出 OpenMetrics 2.0 的迁移指南。

导航说明:/docs/specs/、/docs/specs/om/、/docs/specs/prw/ 三个 URL 均为仅含 frontmatter 的章节索引页,正文为空,实质内容全部在同目录的规范文件中。以下各节分别以对应规范文件为源。

引自:Specifications(导航页)

引自:OpenMetrics(导航页)

引自:Remote Write(导航页)

9.1 OpenMetrics 1.0 规范

元信息:Version 1.0,Status: Published,Date: November 2020;作者/编辑 Richard Hartmann (Grafana Labs)、Ben Kochie (GitLab)、Brian Brazil (Robust Perception)、Rob Skillington (Chronosphere)。定位为 Prometheus exposition format 0.0.4(2014 起稳定)的清理收紧版,目标提交 IETF。明确声明:对 0.0.4 或 OpenMetrics 1.0 的颠覆性改动属 out of scope;未来版本将始终要求语法与语义上支持 1.0。

关于 2.0 的两处原文注记:① NOTE:OpenMetrics 2.0 开发进行中,加入方式见 github.com/prometheus/OpenMetrics/issues/276;② NOTE:Prometheus 生态不支持 OpenMetrics protobuf schema,改用相近的 io.prometheus.client 格式;protobuf schema 在 2.0 的前景讨论见 issues/296。全文未把 gaugehistogram / UTF-8 / 单位处理描述为"2.0 新增"——gaugehistogram 与 UTF-8 label value 在 1.0 中即已存在。

9.1.1 Overview 与数据模型

  • Metrics 是一类遥测数据,代表当前状态快照,区别于 logs/events(面向单事件)。主要是 wire format,与 transport 无关,期望周期消费且跨多次 exposition 有意义。
  • Implementers MUST 响应简单 HTTP GET 到文档化 URL 暴露文本格式;该 endpoint SHOULD 名为 /metrics;MAY 以其他方式(如 HTTP push)暴露。

Data Types:

  • Values MUST 为浮点或整数;ingestor MAY 仅支持 float64。NaN、+Inf、-Inf MUST 支持;NaN MUST NOT 视为缺失值,MAY 表示除零。
  • Booleans MUST 1==true、0==false。Timestamps MUST 为 Unix Epoch 秒,负值 MAY。
  • Strings MUST 仅合法 UTF-8,MAY 零长度;NULL(0x0) MUST 支持。
  • Label:key-value 字符串对;下划线开头的 label name 为 RESERVED,MUST NOT 使用(规范指明者除外);空 label value SHOULD 视同不存在。LabelSet MUST 由 Label 组成、MAY 空,label name MUST 唯一。
  • Exemplars:MUST 由一个 LabelSet + 一个 value 构成,MAY 带 timestamp,其 LabelSet/timestamp MAY 与 MetricPoint 不同;LabelSet 的 name+value 总长 MUST NOT 超过 128 UTF-8 code point(",= 等不计入)。Ingestor MAY 丢弃。
  • Metric:在 MetricFamily 内由唯一 LabelSet 定义,MUST 含一个或多个 MetricPoint;同名 Metric SHOULD 有相同 label name 集;MetricPoint SHOULD NOT 带显式 timestamp;多个 MetricPoint 时 timestamp MUST 单调递增。
  • MetricFamily:MAY 含零或多个 Metric;MUST 有 name、HELP、TYPE、UNIT;每 Metric MUST 唯一 LabelSet。Name MUST 在 MetricSet 内唯一、SHOULD 为 snake_case;冒号 RESERVED(表示计算/聚合而来);下划线开头 RESERVED。
  • Suffixes 冲突:MetricFamily 名 MUST NOT 与另一 MetricFamily 产生样本名冲突(如 gauge foo_created 撞 counter foo 生成的 foo_created)。各类型后缀见下表。
  • Type 合法值(8):unknown、gauge、counter、stateset、info、histogram、gaugehistogram、summary。
  • Unit:非空时 MUST 是 name 以 underscore 分隔的后缀;后续规则可能使其在文本格式中成 infix。Help SHOULD 非空且简短。
  • MetricSet:顶层对象,MUST 由 MetricFamily 组成、MAY 空;name MUST 唯一;同一 label name/value SHOULD NOT 出现在每个 Metric 上。无强制排序;名为 target 的 Info MetricFamily SHOULD 排最前。

各 MetricFamily 类型后缀:

类型 后缀
Counter _total、_created
Summary _count、_sum、_created、``(空)
Histogram _count、_sum、_bucket、_created
GaugeHistogram _gcount、_gsum、_bucket
Info _info
Gauge / StateSet / Unknown ``(空)

9.1.2 Metric Types(全部 8 类)

类型 定义与关键约束
Gauge 当前测量值,关注绝对值;MetricPoint MUST 单值;MAY 增/减/不变;MAY 编码多状态枚举
Counter 度量离散事件,关注增速;MetricPoint MUST 有一个 Total;Total 非 NaN 且 MUST 随时间单调非减、从 0 起。SHOULD 有名为 Created 的 Timestamp。Total MAY 重置为 0,此时 Created MUST 同步设为重置时刻。Total MAY 带 exemplar
StateSet 一组相关布尔值(bitset),可编码 ENUM;MetricPoint MAY 多状态、MUST 每 State 一个布尔;State 有 String 名。Metric 的 LabelSet MUST NOT 含与 MetricFamily 同名的 label。ENUM 编码时 MetricPoint 内 MUST 恰一个布尔为 true。Unit MUST 为空
Info 暴露进程生命周期内 SHOULD NOT 变化的文本信息;MetricPoint 含一 LabelSet,其 LabelSet MUST NOT 含与其 Metric 的 LabelSet 中某 label 同名的 label。MAY 编码不变 ENUM。Unit MUST 为空
Histogram 度量离散事件分布;MetricPoint MUST 至少一个 bucket,SHOULD 含 Sum 与 Created;每 bucket MUST 有 threshold 与 value。MUST 有 +Inf bucket;bucket MUST 累积。+Inf 计全部请求;若含 Sum,Sum MUST 等于被测事件值之和;同一 MetricPoint 内 threshold MUST 唯一。Sum 与 bucket value 语义上是 counter,MUST NOT 为 NaN 或负数。MAY 用负 threshold bucket,但此时 MUST NOT 含 sum。threshold MUST NOT 为 NaN;Count 与 bucket value MUST 为整数。SHOULD 有 Created。Metric 的 LabelSet MUST NOT 含 le。Bucket value MAY 带 exemplar,每 bucket MUST NOT 多于一个;exemplar value MUST 落在该 bucket 范围(≤该值),SHOULD 放入最高值 bucket
GaugeHistogram 度量当前分布;MetricPoint MUST 有一个 +Inf bucket,SHOULD 含 Gsum;每 bucket MUST 有 threshold 与 value,bucket 规则同 Histogram。bucket 与 Gsum 概念上是 gauge,但 bucket value MUST NOT 为负或 NaN;若有负 threshold bucket 则 sum MAY 为负;Gsum MUST NOT 为 NaN;bucket value MUST 为整数。Metric LabelSet MUST NOT 含 le。bucket value 可有 exemplar,规则同上
Summary 度量离散事件分布,Histogram 过贵或均值足够时 MAY 用;MAY 用于向后兼容(旧库暴露预计算 quantile,SHOULD NOT 使用,因不可聚合、时间跨度难定)。MetricPoint MAY 含 Count、Sum、Created 及一组 quantile。Count/Sum 语义上是 counter,MUST NOT 为 NaN 或负数;Count MUST 为整数。含 Count/Sum 时 SHOULD 有 Created,且 Created MUST NOT 关联 quantile 采集周期。quantile 是 quantile→value 映射;无事件时值 MUST 为 NaN。Metric LabelSet MUST NOT 含 quantile。quantile MUST 在 [0,1];quantile value MUST NOT 为负;SHOULD 反映近期值(通常最近 5–10 分钟)
Unknown SHOULD NOT 使用,仅无法判断第三方系统类型时 MAY 用;MetricPoint MUST 单值

9.1.3 Wire formats 与协商

  • 文本格式 MUST 支持且为默认;protobuf MAY 支持且 MUST 仅在协商后使用。两者均为 Regular Chomsky Grammar。Partial 或 invalid exposition MUST 整体视为错误。
  • 所有 ingestor MUST 能接收 TLS 1.2+ 数据;exposer SHOULD 能发出 TLS 1.2+;ingestor SHOULD 能接收无 TLS 的 HTTP;所有实现 SHOULD 用 TLS。版本协商 out-of-band;pull 用 HTTP content-type negotiation,无更新版本请求时 MUST 默认最旧版本(1.0.0);push 场景 producer MUST 用 1.0.0,除非 ingestor 另有要求。

9.1.4 ABNF(RFC 5234,exposition 为顶层 token)

exposition = metricset HASH SP eof [ LF ]
metricset = *metricfamily
metricfamily = *metric-descriptor *metric
metric-descriptor = HASH SP type SP metricname SP metric-type LF
metric-descriptor =/ HASH SP help SP metricname SP escaped-string LF
metric-descriptor =/ HASH SP unit SP metricname SP *metricname-char LF
metric = *sample
metric-type = counter / gauge / histogram / gaugehistogram / stateset
metric-type =/ info / summary / unknown
sample = metricname [labels] SP number [SP timestamp] [exemplar] LF
exemplar = SP HASH SP labels SP number [SP timestamp]
labels = "{" [label *(COMMA label)] "}"
label = label-name EQ DQUOTE escaped-string DQUOTE
number = realnumber
number =/ [SIGN] ("inf" / "infinity")   ; 大小写不敏感
number =/ "nan"
timestamp = realnumber
realnumber = [SIGN] 1*DIGIT
realnumber =/ [SIGN] 1*DIGIT ["." *DIGIT] [ "e" [SIGN] 1*DIGIT ]
realnumber =/ [SIGN] *DIGIT "." 1*DIGIT [ "e" [SIGN] 1*DIGIT ]
eof = %d69.79.70 ; 大写;type/help/unit 同为字面大写
counter = %d99.111.117.110.116.101.114 ; 及 gauge/histogram/stateset/info/summary/unknown 小写字面量
gaugehistogram = gauge histogram
BS="\"; EQ="="; COMMA=","; HASH="#"; SIGN="-" / "+"
metricname = metricname-initial-char 0*metricname-char
metricname-char = metricname-initial-char / DIGIT
metricname-initial-char = ALPHA / "_" / ":"
label-name = label-name-initial-char *label-name-char
label-name-char = label-name-initial-char / DIGIT
label-name-initial-char = ALPHA / "_"
escaped-string = *escaped-char
escaped-char = normal-char
escaped-char =/ BS ("n" / DQUOTE / BS)
escaped-char =/ BS normal-char
normal-char = %x00-09 / %x0B-21 / %x23-5B / %x5D-D7FF / %xE000-10FFFF ; 除换行/双引号/反斜杠外任意 Unicode

9.1.5 Overall Structure 与 Escaping

  • MUST 用 UTF-8;BOM MUST NOT 使用(byte 0 合法、byte 255 非法)。
  • Content type MUST 为 application/openmetrics-text; version=1.0.0; charset=utf-8。
  • 行尾 MUST 用 LF(\n)、MUST NOT 含 CR(\r)。Exposition MUST 以 EOF 结束,SHOULD 以 EOF\n 结束。
  • Escaping(ABNF 标注处 MUST 应用):换行 \n(0x0A)→\\n(0x5c 0x6e);双引号→\\"(0x5c 0x22);反斜杠→\\\\(0x5c 0x5c)。表示反斜杠 SHOULD 用双反斜杠;未定义转义序列 SHOULD NOT 用单反斜杠。

9.1.6 Numbers / Canonical Numbers

  • 整数 MUST NOT 含小数点(23、0042);浮点 MUST 用小数点或科学计数法(8903.123421、1.89e-7),MUST 落在 IEEE 754 64 位范围内(可丢精度,可用于纳秒时间戳)。
  • quantile/le label value MUST NOT 用任意渲染,其他位置 MAY。
  • 目标渲染等价 Go float64 的 %g 并在无小数点/指数时补 .0;Exposer MUST 将正无穷输出为 +Inf;SHOULD 对 0.0–10.0 按 0.001 步进、对 1e-10–1e+10 按 10 的幂输出。示例:0.0 0.001 0.002 0.01 0.1 0.9 0.95 0.99 0.999 1.0 1.7 10.0;1e-10 1e-09 1e-05 0.0001 0.1 1.0 100000.0 1e+06 1e+10。
  • Parser MUST NOT 仅因不符合 canonical 而拒收(如 1.1e-4)。C 语言 printf 需 17 位有效数字(%.17g)。纳秒时间戳 SHOULD NOT 用指数浮点渲染。

9.1.7 结构约束

  • MetricFamily 间 MUST NOT 有显式分隔,下一 MetricFamily MUST 以元数据或不可能属于前者的新样本名标示;MetricFamily MUST NOT 交错。
  • 元数据四要素 name/TYPE/UNIT/HELP;无 TYPE 则 MUST 为 Unknown。
  • 有 unit 则 MUST 提供 UNIT 行且 underscore+unit MUST 为 name 后缀(foo_seconds+seconds 合法;foo+seconds 非法);已知 unit SHOULD 提供。
  • UNIT/HELP 值 MAY 为空,等同无该行。每类元数据 MUST NOT 多于一条,顺序 SHOULD 为 TYPE、UNIT、HELP。
  • 除元数据与 EOF 外 MUST NOT 暴露 # 开头行。Metric MUST NOT 交错;MetricPoint MUST NOT 交错。

9.1.8 各类型文本行格式

类型 文本行规则
Gauge 样本名 MUST 无后缀(foo 17.0)
Counter Total 样本名 MUST 带 _total;Created 若存在 MUST 带 _created;exemplar MAY 挂 Total
StateSet 样本名 MUST 无后缀;每 State 一个样本,样本 MUST 有以 MetricFamily 名为 label name、State 名为 label value 的 label;值 true→1、false→0(foo{foo="bb"} 1)
Info 样本名 MUST 带 _info;样本值 MUST 恒为 1;Metric label 与 value label MAY 任意顺序
Summary Sum→_sum、Count→_count、Created→_created;quantile 用 label name quantile;quantile MAY 任意顺序
Histogram bucket 样本名 MUST 带 _bucket;Sum→_sum;Created→_created;当且仅当存在 Sum 时,+Inf bucket 值 MUST 同时以 _count 后缀样本出现。bucket MUST 按 le 数值升序,le MUST 遵循 Canonical Numbers
GaugeHistogram bucket 样本名 MUST 带 _bucket;Sum→_gsum;当且仅当存在 Sum 时 +Inf bucket 值 MUST 同时以 _gcount 后缀样本出现
Unknown 样本名 MUST 无后缀

Exemplar 示例(Histogram):

foo_bucket{le="0.1"} 8 # {} 0.054
foo_bucket{le="1"} 11 # {trace_id="KOO5S4vxi0o"} 0.67
foo_bucket{le="10"} 17 # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789

无 label 的 exemplar MUST 表示为 {}。

9.1.9 Protobuf format(1.0)

  • 消息 MUST 二进制编码,content type MUST 为 application/openmetrics-protobuf; version=1.0.0;payload MUST 是单个 MetricSet 消息。
  • MUST 遵循 proto3。字符串字段 MUST UTF-8。
  • Timestamp MUST 遵循 google.protobuf.Timestamp:epoch 秒 int64 + 非负纳秒小数 int32,MUST 在 0–999,999,999 闭区间。
  • schema 见 OM 仓库 proto/openmetrics_data_model.proto。

9.1.10 Design Considerations(关键条目)

主题 要点
Scope 无实时保证;ingestor 与 exposer 的互相发现属 out of scope
Extensions 基于 0.0.4 刻意不加大改;无非透明二进制类型(sketch/t-digest);unit 在 name 中重复
Units 基于 SI base unit(seconds、bytes、joules、grams、meters、ratios、volts、amperes、celsius);bytes 而非 bits;grams 而非 kilograms;ratio 而非百分比;decibels 非 base;非 base 无法避免时仍应把单位写进 name
Statelessness 跨 exposition 无状态,先前暴露 MUST NOT 影响后续;同一自包含 exposition MUST 提供给新老 ingestor;exposer MUST NOT 剔除近期无变化的指标
Metric Evolution SHOULD 避免改变 label 顺序;Metrics/samples SHOULD NOT 忽现忽隐;counter 应持续暴露到进程终止。改 type 或增删 label 通常破坏性。非破坏性:给 Info value 加 label、改 Help、float/int 互切、给 stateset 加状态。Histogram bucket SHOULD NOT 跨 exposition 变化
NaN 无特殊含义,MUST NOT 用作缺失/坏数据标记。Missing Data:无特殊标记,后续 exposition 直接不含该 Metric
Performance SHOULD 不超过 1 秒;SHOULD 暴露最新状态、SHOULD NOT 依赖缓存。Concurrency:并发 exposition MUST 支持,遵循并发 BCP
Naming snake_case;建议公司前缀(acme_);scrape_ 前缀由 ingestor 用于 exposition 信息,应用不应直接暴露;生态有 process_ 系列(process_open_fds、process_max_fds);避免 metric/timer/stats/counter/total/float64 等冗余子串;不把 label name 写进 metric name;不把 "openmetrics"/"prometheus" 等实现细节写进名字
Label Namespacing label name 不推荐显式命名空间;避免易冲突名:region、zone、cluster、availability_zone、az、datacenter、dc、owner、customer、stage、service、team、job、instance、environment、env;type 过于泛化应避免(HTTP 场景用 method)。label name 无元数据
Metric Names vs Labels 求和/平均应仍有意义;把 total 与成员并列暴露是错的(会双计);另宜分开 total/failure 及 read/write、send/receive
Name Characters 向后兼容是核心目标,字符集不扩不缩;label value 支持完整 UTF-8
Metadata 两类 Target metadata(来自服务发现/CMDB,优先由 ingestor 加 label)与 Exposer metadata(软件版本、编译器版本、Git SHA)。提供 target metadata 首选方案是名为 target 的 Info MetricFamily(target_info{env="prod",...} 1),SHOULD 排最前;exposer MUST NOT 给所有 Metric 加 target metadata label(除非为特定 ingestor 显式配置),MUST NOT 因 target metadata 前缀化/变更 MetricFamily 名;通用监控系统 exposition 中 metric 名 MUST NOT 因 target metadata 变化(如不得全局加 staging_ 前缀)
Client Calculations 计算应留给 ingestor(Summary quantile 为兼容例外);不应暴露 counter 平均增速 gauge、histogram/summary 平均事件大小、标准差(应暴露 sum of squares counter,因 64 位精度不足未纳入)、请求失败率(应分别暴露失败与总数 counter)
Number Types counter 每秒百万次需一世纪才丢精度,100 Gbps 满吞吐约 20 小时即丢精度;高吞吐整数可选 int64;Summary quantile MUST 为 float64
Exposing Timestamps 一般 MetricPoint timestamp 不应暴露,应由 ingestor 自加;建议把最后变更时刻做成独立 Gauge(如 my_counter_last_increment_timestamp_seconds);绝对时间戳(epoch)比 elapsed 稳健(如 my_boot_time_seconds)。exemplar timestamp 无最佳实践限制;截断到秒可能重复,此时 MUST 用最早 timestamp 的 MetricPoint
Size Limits 不规定样本/label/state 数上限;ingestor MAY 自设限制(防攻击),任一 value/metric/exposition 超限则整个 exposition 必须被拒。性能三要素:唯一时间序列数、随时间样本数、唯一字符串数。2020 年单实例 ingestor 约 1000 万时间序列为大;单个 exposition SHOULD NOT 超 10k 时间序列;singleton target 几十万可接受。exemplar 硬上限 128 UTF-8 字符

9.1.11 Security 与 IANA

  • Security:实现 MAY 提供认证/授权/计费,SHOULD 在 OpenMetrics 之外处理;exposer SHOULD 能 TLS 1.2+ 保护 HTTP;不支持加密时 SHOULD 用反向代理/防火墙/ACL;/metrics 不宜置于 TCP/80、443、8080、8443(面向公开服务)。
  • IANA:IANA 为 OpenMetrics 客户端暴露数据分配的端口为 9099。多 endpoint 共用 IP:port 时建议反向代理,endpoint SHOULD 在路径带自己名字(如 /node_exporter/metrics)。拟注册两个 MIME 类型:application/openmetrics-text 与 application/openmetrics-proto(编辑注:前者自 2018 年已在用,后者尚未投入使用)。

引自:OpenMetrics 1.0

9.2 OpenMetrics 2.0 规范

9.2.1 版本与状态

  • Version: 2.0.0-rc0,Status: Experimental,Date: March 2026。
  • 作者:Arthur Silva Sens、Bartłomiej Płotka、David Ashpole、György Krajcsovits、Owen Williams、Richard Hartmann;Emeritus:Ben Kochie、Brian Brazil、Rob Skillington。
  • 定位:以 OpenMetrics 1.0 为基础,对齐现代 Prometheus 数据模型与 OpenTelemetry 数据模型/命名约定;本文件为 standalone spec。
  • 明确注释:RC 版本,不预期大改动,但保留基于早期采用者反馈破坏兼容性的权利;反馈走 prometheus/openmetrics issue。
  • 历史脉络:2012 Prometheus 诞生;Prometheus text exposition 0.0.4 自 2014 稳定;2020 发布 OpenMetrics 1.0;约 2024 OpenMetrics 项目并入 CNCF Prometheus 项目伞下。

9.2.2 Overview 与规范性语言

  • OpenMetrics 首要是一个 wire format(线格式),不绑定任何特定传输;期望被周期性消费,跨多次 exposition 有意义。
  • 实现 SHOULD 以 HTTP GET 响应形式在文档化 URL 暴露,该端点 SHOULD 叫 /metrics;MAY 以 push 等其它方式暴露。
  • 指标表达为数值:counts、current values、distributions、enumerations、boolean states。Time series 示例:网络接口计数器、设备温度、BGP 连接状态、延迟分布、告警状态。
  • 规范性关键词按 RFC 2119 / RFC 8174,仅当全大写时生效。
  • RESERVED 一词表示保留给未来或本标准自用;被标 RESERVED 的值/名/字段 MUST NOT 使用,除非本标准或未来版本明确允许。
  • Data Model 一节 MUST 与 ABNF 一节一起读;两者冲突时以 ABNF 的限制优先。

9.2.3 Data Model(数据模型)

Sample Values 与数据类型:

  • Metric 值 MUST 为 Number 或 CompositeValue。
  • Number MUST 为浮点或整数。Ingestor MAY 仅支持 float64(IEEE 754-2008 binary64,约 15–17 位有效十进制数字)。非实数值 NaN、+Inf、-Inf MUST 被支持;NaN MUST NOT 视为缺失值,但 MAY 用于表示除零或其它未定义/不定结果。
  • Boolean MUST 用 Number 表示:1 = true,0 = false。
  • CompositeValue MUST 包含在 MetricFamily 内重建该 sample 值所需的全部信息。MUST 使用 CompositeValue 的类型:Histogram、GaugeHistogram、Summary;其它 MetricFamily 类型 MUST 用 Number。

Timestamps:MUST 为 Unix Epoch 秒;SHOULD 用浮点表示亚秒精度(毫秒/微秒);负时间戳 MAY 使用。出现位置:Exemplar 的 Timestamp、Sample 的 Timestamp、Sample 的 Start Timestamp。

Strings / Label / LabelSet:

  • String MUST 仅含合法 UTF-8,MAY 为零长度;NULL (ASCII 0x0) MUST 被支持。
  • Label 为字符串键值对。label name 以一个或多个下划线开头的 RESERVED,MUST NOT 使用(除非本标准规定);此类名 MAY 在 MetricFamily 元数据可能冲突时(如 metric federation)替代 TYPE/UNIT 元数据。
  • Label names SHOULD 遵循 ABNF 的 label-name;MAY 为任意带引号转义 UTF-8 字符串(注意可降低可用性)。空 label 值 SHOULD 视作该 label 不存在。
  • LabelSet MUST 由 Label 组成,MAY 为空;同一 LabelSet 内 label name MUST 唯一。

Exemplars:

  • Exemplar 是 MetricSet 外数据的引用(常见用途:trace ID)。MUST 含 LabelSet + Number 值,MUST 有 timestamp。
  • 其 LabelSet SHOULD NOT 含 Metric LabelSet 中已有的 label name;timestamp SHOULD ≤ Sample timestamp(若存在),且 SHOULD ≥ Sample start timestamp(若存在)。
  • 同一 Sample 的 exemplar SHOULD 有相同 label names。timestamp SHOULD 接近观测时刻但不必精确。
  • 引用 W3C Trace Context 时 SHOULD 用 key trace_id(对应 trace-id)与 span_id(对应 parent-id)。
  • Ingestor MAY 截断 exemplar LabelSet 或丢弃 exemplar;截断时 SHOULD 保留 trace_id 与 span_id。

Sample / Metric / MetricFamily:

  • Sample:Metric 内单个数据点;MUST 有 Value,MAY 有 Timestamp、MAY 有 Exemplars、MAY 有 Start Timestamp(视类型)。Sample SHOULD NOT 带 Timestamp。Start Timestamp 存在时 SHOULD 表示测量周期开始时间(帮助区分新/长期指标、检测 counter reset)。
  • Metric:由 MetricFamily 内唯一 LabelSet 定义;MUST 含 1 个或多个 Sample;多于一个 Sample 时其 timestamp MUST 单调递增。同名 Metric SHOULD 有相同 label name 集合。
  • MetricFamily:MAY 含 0 或多个 Metric;每个 Metric 的 LabelSet MUST 唯一;MUST 有 name,SHOULD 有 Help、Type、Unit。
    • Name:MUST 为 string;MUST 在 MetricSet 内唯一;MUST 与族内每个 Metric 的 Name 相同。SHOULD 用 snake_case;SHOULD 遵循 ABNF metricname;MAY 为带引号转义 UTF-8。名字中冒号 RESERVED,表示该族是通用监控系统计算/聚合的结果;以一个或多个下划线开头的名字 RESERVED 且 MUST NOT 使用。
    • Discouraged Suffixes:族名 SHOULD NOT 以 _count、_sum、_gcount、_gsum、_bucket 结尾;SHOULD NOT 在转换为 1.0 文本时产生 MetricName 碰撞。Ingestor MAY 拒收此类 MetricSet。反例:gauge foo_bucket 与 histogram foo 冲突。
    • Type:合法值 unknown、gauge、counter、stateset、info、histogram、gaugehistogram、summary。
    • Unit:非空时 SHOULD 是族名的后缀(下划线分隔),类型特定后缀在 unit 后缀之后。单位取值见 Units and Base Units。
    • Help:string,SHOULD 非空,简短、可作 tooltip。
  • MetricSet:顶层对象;MUST 由 MetricFamily 组成,MAY 为空;每个族名 MUST 唯一;同一 label name+value SHOULD NOT 出现在集合内每个 Metric 上。族间无强制排序(MAY 为可读性排序)。若存在符合 target metadata 约定的 Info 族 target_info,SHOULD 放在最前。

9.2.4 MetricFamily Types(八种类型)

  • Gauge:当前测量(内存字节、队列项数),关注绝对值。Sample MUST 为 Number;MAY 增/减/恒定(仅单向也仍可能是 gauge)。
  • Counter:测量离散事件(HTTP 请求数、CPU 秒、发送字节),关注增长率。族名 SHOULD 以 _total 结尾。Sample SHOULD 有 Start Timestamp;MUST 为非 NaN Number,MUST 单调非递减(除非重置为 0),从 0 开始;MAY 重置为 0,此时 Start Timestamp MUST 设为近似重置时间。MAY 有 exemplars。
  • StateSet:一组相关 boolean(bitset),ENUM 可经此编码。结构为每个 state 一个 Metric(StateSet MetricGroup)。MetricGroup 含 1+ 个 state,每个 state MUST 有一个 boolean Metric。若作 ENUM 编码,MUST 在单个 Timestamp 下恰好一个 Sample 为 1。Unit MUST 为空字符串。
    • 与 1.0 差异:1.0 的 Metric 由 MetricPoint 组成;1.0 Metric ≈ 2.0 MetricGroup,1.0 MetricPoint ≈ 2.0 Metric。
  • Info:暴露进程生命周期内 SHOULD NOT 变化的文本信息(版本、commit、编译器版本)。族名 MUST 以 _info 结尾;Unit MUST 为空字符串。
  • Histogram:测量离散事件分布。Sample MUST 含 Count 与 Sum。
    • Count MUST = 测量次数(语义上是 counter),SHOULD 为整数,MUST NOT 负,SHOULD NOT 为 +Inf/NaN。允许 float Count(因直方图算术运算可能超整数范围)。
    • Sum MUST = 所有被测量值之和;仅当无负测量值时语义上才是 counter。
    • MUST 测量非 NaN 值(在 Classic 或 Native bucket 中,或两者)。bucket 边界 MUST NOT 为 NaN。
    • Bucket MUST 有良定义边界与值;bucket 值是 counter 语义,SHOULD 为整数,MUST NOT 负,SHOULD NOT 为 +Inf/NaN;允许 float bucket 值。
    • SHOULD NOT 含 NaN 测量;若含,NaN MUST 计入 Count 且 Sum MUST 为 NaN。含 +Inf/-Inf 时 MUST 计入 Count 并加到 Sum(可能得 +Inf/-Inf/NaN)。
    • Sample SHOULD 有 Start Timestamp。若用 Classic Buckets,Metric LabelSet MUST NOT 含 le label name(与 _bucket 生成的 le 冲突)。
    • 类型随时间累积,MAY 重置;重置时 Sum/Count/Classic/Native buckets MUST 归零,若有 Start Timestamp MUST 设为近似重置时间。
    • MAY 有 exemplars;exemplar 值 SHOULD 均匀分布(如每个 Classic Bucket 保留一个)。
    • Classic Buckets:每个 MUST 有 threshold,同 Sample 内 threshold MUST 唯一,MAY 为负。MUST 统计 ≤ threshold 的测量值(含被更低 bucket 统计的),即累积。含 Classic Buckets 的 Sample MUST 有一个 +Inf threshold bucket;+Inf bucket 统计所有测量;Count MUST 等于 +Inf bucket 的值。阈值 SHOULD 随时间与目标保持恒定。若允许 NaN,NaN MUST 计入 +Inf bucket 且 MUST NOT 计入其它 bucket。
    • Native Buckets:MUST 有 Schema 值,为 8-bit 有符号整数,范围 -4 到 8(含),称 Standard (exponential) schema;范围外 RESERVED 且 MUST NOT 使用。对 Standard Schema n:MAY 含正/负 Native Buckets,MUST 含 zero Native Bucket;空的正/负 bucket SHOULD NOT 出现。
      • 边界公式(Python 语法):正 bucket 上含限 (2**2**-n)**i,正 bucket 下开限 (2**2**-n)**(i-1);负 bucket 下含限 -((2**2**-n)**i),负 bucket 上开限 -((2**2**-n)**(i-1));i 为整数,MAY 为负。
      • 例外:含 MaxFloat64 的正 bucket 上含限取 MaxFloat64(避免 overflow);下一个正 bucket 下开限 MaxFloat64、上含限 +Inf(正 overflow bucket);含 MinFloat64 的负 bucket 下含限取 MinFloat64(避免 underflow);下一个负 bucket 上开限 MinFloat64、下含限 -Inf(负 overflow bucket)。超出 ±Inf bucket 的 Native Buckets MUST NOT 使用。
      • zero Native Bucket 边界为 [-threshold, threshold](含);Zero threshold MUST 为非负 float64。若 threshold > 0,落入 zero bucket 的值 MUST 只计入 zero bucket。Zero threshold SHOULD 等于某 Native Bucket 的下限。
      • 若不允许 NaN:Count MUST = 负+正+zero 三 bucket 之和。若允许 NaN:NaN MUST NOT 计入任何 bucket 而 MUST 计入 Count,Count 与三 bucket 和之差 MUST 为 NaN 观测数。
  • GaugeHistogram:测量当前分布。Sample MUST 含 Gcount、Gsum。Gcount = 当前测量数(gauge 语义),SHOULD 为整数,SHOULD NOT 为 -Inf/+Inf/NAN/负(允许 float 与负值以支持对直方图做算术,如变化率)。Gsum = 当前所有被测量值之和(gauge 语义)。
    • 若从 Classic 或 Native 之一停止测量而继续另一个,MUST 清除且不再暴露停止的那类 bucket。Bucket 值 SHOULD 为整数、gauge 语义、SHOULD NOT 为 -Inf/+Inf/NaN/负。SHOULD NOT 含 NaN;若含,NaN MUST 计入 Gcount 且 Gsum MUST 为 NaN;含 ±Inf 时 MUST 计入 Gcount 并加入 Gsum。
    • 用 Classic Buckets 时 Metric LabelSet MUST NOT 含 le。Classic/Native buckets 与 exemplars 规则同 Histogram,Gcount/Gsum 对应 Count/Sum。无 Start Timestamp。
  • Summary:测量离散事件分布,用于 Histogram 太贵且少量预计算分位数够用时。SHOULD NOT 使用(分位数不可聚合、用户常无法推出覆盖时间窗),仅为向后兼容 MAY 使用。Sample MUST 含 Count、Sum、一组 quantiles。Count/Sum 语义为 counter,MUST NOT 为 NaN 或负;Count MUST 为整数。SHOULD 有 Start Timestamp,且 Start Timestamp MUST NOT 基于分位数值的采集周期。
    • Quantile 是从 quantile 到 value 的映射(如 0.95→0.2 表示 95th 百分位 200ms、时间窗未知)。无事件时量化值 MUST 为 NaN。Quantile Metric 的 LabelSet MUST NOT 含 quantile label name。Quantile MUST 在 [0,1] 含端点内;值 MUST NOT 为负;SHOULD 表示近值(通常最近 5–10 分钟)。
  • Unknown:SHOULD NOT 使用;仅在无法从第三方系统判定类型时 MAY 使用。Sample MUST 为 Number 或 CompositeValue。

9.2.5 Text Format

  • OpenMetrics 格式是 Regular Chomsky Grammars(允许快速小型 parser)。部分或无效的 exposition MUST 整体视为错误。
  • 注:早期版本曾定义 protobuf 格式,2.0 不含 protobuf 表示。

Protocol Negotiation:Ingestor MUST 能摄取 TLS 1.2+ 保护的数据,SHOULD 支持 TLS 1.3+;Exposer SHOULD 能发出 TLS 1.3+ 保护的数据。Ingestor SHOULD 能摄取无 TLS 的 HTTP 数据。所有实现 SHOULD 使用 TLS。版本协商 out-of-band:HTTP pull 用标准 content type negotiation,无更新版本请求时 MUST 默认最老版本(1.0.0)。Push 场景出题方通常发起连接,Producers MUST 用最老版本(1.0.0)除非 ingestor 另有请求。

ABNF(RFC 7405;%s=区分大小写,%i=不区分):

exposition = metricset HASH SP %s"EOF" [ LF ]

metricset = *metricfamily
metricfamily = *metric-descriptor *sample
metric-descriptor = HASH SP %s"TYPE"  SP (metricname / metricname-utf8) SP metric-type LF
metric-descriptor =/ HASH SP %s"HELP" SP (metricname / metricname-utf8) SP escaped-string LF
metric-descriptor =/ HASH SP %s"UNIT" SP (metricname / metricname-utf8) SP *metricname-char LF
metric-type = %s"counter"/%s"gauge"/%s"histogram"/%s"gaugehistogram"/%s"stateset"/%s"info"/%s"summary"/%s"unknown"
sample = metricname-and-labels SP value [SP timestamp] [SP start-timestamp] *exemplar LF
value = number / "{" composite-value "}"
timestamp = realnumber
start-timestamp = %s"st" "@" timestamp
exemplar = SP HASH SP labels-in-braces SP number SP timestamp
metricname-and-labels = metricname [labels-in-braces] / name-and-labels-in-braces
labels-in-braces = "{" [label *(COMMA label)] "}"
name-and-labels-in-braces = "{" metricname-utf8 *(COMMA label) "}"
label = label-key "=" DQUOTE escaped-string DQUOTE
number = realnumber
number =/ [SIGN] (%i"inf"/%i"infinity")
number =/ %i"nan"
realnumber = [SIGN] 1*DIGIT ["." *DIGIT] ["e" [SIGN] 1*DIGIT]
realnumber =/ [SIGN] *DIGIT "." 1*DIGIT ["e" [SIGN] 1*DIGIT]
integer = [SIGN] 1*"0" / [SIGN] positive-integer
positive-integer = *"0" positive-digit *DIGIT
positive-digit = "1".."9"
; 注:non-negative-integer 产生式同属该 ABNF(UPPER/lower 未展开,以规范原文为准)
BS="\"; COMMA=","; HASH="#"; SIGN="-" / "+"
metricname = metricname-initial-char 0*metricname-char
metricname-char = metricname-initial-char / DIGIT
metricname-initial-char = ALPHA / "_" / ":"
metricname-utf8 = DQUOTE escaped-string-non-empty DQUOTE
label-key = label-name / DQUOTE escaped-string-non-empty DQUOTE
label-name-initial-char = ALPHA / "_"
label-name-char = label-name-initial-char / DIGIT
escaped-string = *escaped-char
escaped-string-non-empty = 1*escaped-char
escaped-char = normal-char / BS("n"/DQUOTE/BS) / BS normal-char
normal-char = %x00-09 / %x0B-21 / %x23-5B / %x5D-D7FF / %xE000-10FFFF

Composite 相关产生式:

composite-value = histogram-value / gauge-histogram-value / summary-value
histogram-value = h-count "," h-sum "," histogram-buckets
gauge-histogram-value = %s"g" h-count "," %s"g" h-sum "," histogram-buckets
h-count = %s"count" ":" number
h-sum = %s"sum" ":" number
histogram-buckets = classic-buckets / native-buckets [ "," classic-buckets ]
classic-buckets = %s"bucket" ":" "[" [ch-le-counts ","] ch-pos-inf-bucket "]"
ch-le-counts = (ch-neg-inf-bucket / ch-le-bucket) *("," ch-le-bucket)
ch-pos-inf-bucket = "+" %s"Inf" ":" number
ch-neg-inf-bucket = "-" %s"Inf" ":" number
ch-le-bucket = realnumber ":" number
native-buckets = nh-schema "," nh-zero-threshold "," nh-zero-count ["," nh-negative-spans "," nh-negative-buckets] ["," nh-positive-spans "," nh-positive-buckets]
nh-schema = %s"schema" ":" integer
nh-zero-threshold = %s"zero_threshold" ":" realnumber
nh-zero-count = %s"zero_count" ":" number
nh-negative-spans / nh-positive-spans = %s"negative_spans"/%s"positive_spans" ":" "[" [nh-spans] "]"
nh-spans = nh-start-span *("," nh-span)
nh-start-span = integer ":" non-negative-integer
nh-span = non-negative-integer ":" non-negative-integer
nh-negative-buckets / nh-positive-buckets = %s"negative_buckets"/%s"positive_buckets" ":" "[" [nh-buckets] "]"
nh-buckets = number *("," number)
summary-value = cs-count "," cs-sum "," cs-quantile
cs-count = %s"count" ":" number
cs-sum = %s"sum" ":" number
cs-quantile = %s"quantile" ":" "[" [cs-q-counts] "]"
cs-q-counts = cs-q-count *("," cs-q-count)
cs-q-count = realnumber ":" number

Overall Structure:

  • MUST 使用 UTF-8;BOM MUST NOT 使用(NULL 0x00 合法,0xFF 非法)。
  • Content type MUST 为:application/openmetrics-text; version=2.0.0; charset=utf-8。
  • 行结束 MUST 用 LF (\n),MUST NOT 含 CR (\r)。Exposition MUST 以 EOF 结束,SHOULD 以 EOF\n 结束。
  • 完整示例含:summary 族(count/sum/quantile 复合值 + st@)、gauge 族、counter 族(process_cpu_seconds_total 4.20072246e+06)、histogram 族(同时含 native 与 classic bucket)、gaugehistogram 族(acme_http_request_seconds:rate5m)、带引号 UTF-8 名族("foodb.read.errors" → {"foodb.read.errors","service.name"="my_service"} 3482)。

UTF-8 Quoting:

  • 不符合 ABNF metricname 的指标名 MUST 用双引号包裹并采用 UTF-8 替代语法:引号名 MUST 移到括号内作为第一项、无 label name 与等号;TYPE/UNIT/HELP 行中 MUST 用双引号。引号语法 MAY 用于任何名字。label name 不符合 label-name 时 MUST 双引号,任何 label name MAY 双引号。
  • 正则:无需引号的指标名 ^[a-zA-Z_:][a-zA-Z0-9_:]*$;label name ^[a-zA-Z_][a-zA-Z0-9_]*$。

Escaping(转义 MUST 应用于 ABNF 标注处,含引号 UTF-8 串):

原字符 转义序列
Line feed \n (0x0A) 字面 \n(0x5c 0x6e)
双引号 \"(0x5c 0x22)
反斜杠 \\(0x5c 0x5c)

双反斜杠 SHOULD 表示反斜杠字符;单反斜杠 SHOULD NOT 用于未定义转义序列(如 \\a 优于 \a)。

Numbers / CompositeValues / Timestamps / Exemplars:

  • 整数 MUST NOT 有小数点(23、0042、1341298465647914)。浮点 MUST 用小数点或科学计数法(8903.123421、1.89e-7),MUST 落在 IEEE 754 64-bit 范围内(可因尾数丢精度,可用于纳秒时间戳)。
  • CompositeValue 为结构化字段;字段周围 MUST NOT 有空白。
  • 时间戳若需纳秒精度 SHOULD NOT 用指数浮点渲染(float64 精度不足,如 1604676851.123456789)。
  • 无 Label 的 Exemplar MUST 表示为 {}。

9.2.6 MetricFamily 与文本行规则

  • MetricFamily 间 MUST NOT 有显式分隔符;下一个族 MUST 用元数据或新 Metric 名标识。族 MUST NOT 交错(interleaved)。同族的 Name 与 Metric Name SHOULD 使用相同引号方式。
  • 元数据四要素:MetricFamily name、TYPE、UNIT、HELP。
    • 无 TYPE 时族 MUST 按 Unknown 解释。
    • 指定 unit 时 MUST 出现在 UNIT 行;下划线+单位 SHOULD 是族名后缀(Counter 则在 _total 前作中缀)。已知单位时 SHOULD 提供。
    • UNIT/HELP 行的值 MAY 为空 → MUST 视作该行不存在。
    • 同一族每类元数据行 MUST NOT 多于一行;顺序 SHOULD 为 TYPE、UNIT、HELP。
    • 除元数据与末尾 EOF 行外,MUST NOT 暴露以 # 开头的行。
    • Ingestor MUST 支持无元数据行的族。
  • Metric:MUST NOT 交错。无标签无时间戳且值为 0 的 Sample 应渲染为 bar_seconds_count 0 或 bar_seconds_count{} 0。label 值可为任意 UTF-8,故 MUST 按 ABNF 转义(示例 bar_seconds_count{a="x",b="escaping\" example \n "} 0)。指标名与 label name 亦可为 UTF-8,特定情况 MUST 引号转义(如 {"\"bar\".seconds.count","b\\"="escaping\" example \n "} 0)。

9.2.7 各类型文本行细则

st@ 通用规则:Sample 的 Start Timestamp 若存在,MUST 以 st@ 前缀与 Sample 内联;若 value 的 timestamp 存在,Start Timestamp MUST 紧随其后;若 exemplar 存在,Start Timestamp MUST 放在 exemplar 之前。GaugeHistogram 除外(无 Start Timestamp)。

类型 文本行规则
Gauge Sample 值 MUST 为 Number;无推荐后缀
Counter 值 MUST 为 Number;st@ 内联规则同上;MAY 有 Exemplars。示例:foo_total 17.0 1520879607.789 st@1520430000.123 # {trace_id="KOO5S4vxi0o"} 0.67 1520879606.1;无 _total 后缀合法但降低可用性
StateSet 无推荐后缀。MUST 每 state 一个 Metric;每 state Metric MUST 有以族名作 label name、state 名作 label value 的 label;Sample 值 MUST 为 1(true)/ 0(false)。示例 foo{foo="bb"} 1。StateSet MetricGroup MUST NOT 交错
Info Sample 值 MUST 恒为 1。示例 foo_info{name="pretty name",version="8.2.7"} 1;Metric label 与 Sample value label MAY 任意顺序
Summary 值 MUST 为 CompositeValue,MUST 含字段 count、sum、quantile(按此顺序)。quantile MUST 按分位数递增排序。示例 foo {count:17,sum:324789.3,quantile:[]} st@1520430000.123
Histogram with Classic Buckets 值 MUST 为 CompositeValue,字段为 count、sum、bucket(按序)。Classic Buckets MUST 按 threshold 数值递增排序;所有 Classic Buckets MUST 存在,即使值为 0
Histogram with Native Buckets 字段 MUST 为 count、sum、schema、zero_threshold、zero_count(按序)。无负 Native Buckets 时 negative_spans/negative_buckets SHOULD 省略;无正 Native Buckets 时 positive_spans/positive_buckets SHOULD 省略;有则 MUST 按此顺序出现在 zero_count 之后。bucket 值 MUST 按 index 排序。值 MUST 为绝对计数(非相对前一个 bucket 的 delta)。值为 0 的 Native Bucket SHOULD NOT 出现
Histogram with both Classic and Native Buckets 字段 MUST 含 count、sum(按序),之后先 Native 字段、再 Classic(bucket)字段;顺序便于实现跳过 Classic
GaugeHistogram(Classic / Native / 两者) 语法同 Histogram,但 Count/Sum 用字段 gcount/gsum,且无 Start Timestamp
Unknown 值 MUST 为 Number 或 CompositeValue;无推荐后缀;示例含无元数据行的 foo 42.23

Native bucket spans 构造:每 span 为 (offset:length);仅列表首 span 可负 offset,定义对应 buckets 列表首个 bucket 的 index;length 为连续 bucket 数;后续 span 的 offset 表示被排除(未填充)的 bucket 数。各 span list 的 length 之和 MUST 等于对应 bucket 列表长度。保留空 bucket 可减少 span 数(当两 span offset 差恰为 1 时)。

示例:

{count:59,sum:1.2e2,schema:7,zero_threshold:1e-4,zero_count:0,negative_spans:[1:2],negative_buckets:[5,7],positive_spans:[-1:2,3:4],positive_buckets:[5,7,10,9,8,8]}

无 bucket 时:

{count:0,sum:0,schema:3,zero_threshold:1e-4,zero_count:0}

Exemplars(Histogram):MAY 附加;若 Classic 与 Native 各存一套,MAY 只附一套以兼顾性能与兼容,SHOULD 附 Classic 关联的那套。

9.2.8 Design Considerations(设计考量)

  • Scope:为在线系统提供遥测;无硬/软实时保证;足够用于聚合决策但不反映单个事件。支持各种规模(每小时几请求 → 400Gb 网络口)。以固定节奏传输状态快照。Ingestor 与 exposer 的相互发现不在范围。
  • Failure Modes:主张事务性处理——任何编码/解码/校验错误 MUST 拒收整个 MetricSet(失败抓取优于不准确/部分视图)。唯一例外:exemplar 专属失败不应导致整个 exposition 失败,malformed/invalid exemplar 应被丢弃或忽略。
  • Extensions and Improvements:基于 Prometheus text 0.0.4、Prometheus Protobuf、OpenMetrics 1.0。未来 minor 版本将始终要求同时支持 2.0 的语法与语义。去掉元数据与结构后,作为无序 sample 集合仍应可用。
  • 与 1.0 的显著差异汇总:
# 差异
1 Start Timestamp 改为内联 st@,取代单独 _created sample
2 Metric name 必须与 MetricFamily name 完全一致,移除隐式后缀剥离
3 Counter 的 _total 后缀与单位后缀由 MUST 改为 SHOULD
4 Histogram/GaugeHistogram/Summary 值合并为单行 CompositeValue,Count 与 Sum 变为必需
5 引入带指数 bucket schema 的 Native Histograms
6 允许带引号的 UTF-8 指标名与 label 名
7 Exemplar 时间戳变为强制,每 sample 允许多个 exemplar
8 移除 Protobuf 格式规范
  • Units and Base Units:以 SI 基本单位为准(seconds、bytes、joules、grams、meters、ratios、volts、amperes、celsius)。ratio 为基本单位而非百分比;优先暴露原始分子/分母 gauge 或 counter。分贝不是基本单位。非基本单位不可避时仍应在名中体现。并非所有族都有单位(如 HTTP 请求计数)。
  • Statelessness:wire format 跨 exposition 无状态;先前暴露信息 MUST 对未来无影响。同样的自包含 exposition MUST 提供给新老 ingestor。Exposer MUST NOT 因某指标近期无变化/无观测就排除它。
  • Exposition Across Time and Metric Evolution:跨 exposition 应保持标签暴露顺序(避免破坏 parser 缓存)。Metric/Sample SHOULD NOT 忽现忽隐。打破兼容:改变族 Type、增删 label。不打破:Info Metric 增 label、改 Help、float/int 互换、stateset 增 state、在不改变名的情况下增 unit 元数据。Histogram buckets SHOULD NOT 跨 exposition 变化。
  • NaN:NaN 是普通数字(常源自除零),无特殊含义,MUST NOT 用作缺失/坏数据标记。
  • Missing Data:数据消失无特殊标记/信号,后续 exposition 不含该 Metric 即可。
  • Exposition Performance:SHOULD 不超过 1 秒;SHOULD 为最新状态(避免依赖缓存值)。
  • Concurrency:MUST 支持并发 exposition;遵循并发 BCP,避免死锁、竞态、过粗锁。
  • Metric Naming and Namespaces:snake_case;以公司/组织→库/子系统/应用分形命名(如 acme_http_router_request_seconds)。scrape_ 前缀供 ingestor 使用,应用不应直接暴露(可用 myexposer_scrape_ 前缀,如 myexposer_scrape_duration_seconds)。process_ 为生态一致前缀(如 process_open_fds、process_max_fds)。避免冗余子串 metric/timer/stats/counter/total/float64;避免把 label 名、其它层实现细节(如 openmetrics/prometheus)写进指标名。
  • Label Namespacing:不建议对 label 名做公司/库命名空间;避免易与目标标签冲突的名(region、zone、cluster、availability_zone、az、datacenter、dc、owner、customer、stage、service、team、job、instance、environment、env);type 过于泛化应避免(HTTP 场景用 method)。label 名无元数据(元数据仅 HELP/TYPE/UNIT)。
  • Metric Names versus Labels:对族求和/平均应有意义(电压与风扇转速混合无意义)。错误:同时暴露 total 汇总与各项会造成下游重复计数(wrong_metric{label="total"} 反例)。label 应最少化;可跨多族的 label 宜移入 _info 指标。经验上分离 total/failure、read/write、send/receive 族优于用 {result=...} label。
  • Metric and Label Name Characters:向后兼容是核心目标;字符集扩展/收缩会与兼容性冲突;查询语言依赖这些精确字符集;label 值支持完整 UTF-8。
  • Types of Metadata:Target metadata(外部,如服务发现/CMDB,优选由 ingestor 添加以更灵活、开销更低)vs Exposer metadata(如软件版本、编译器版本、Git SHA)。
  • Supporting Target Metadata in Push/Pull Systems:push 场景常由 exposer 提供目标元数据,pull 场景常由 ingestor 预先知道。OpenMetrics 无状态、对所有 ingestor 提供相同 exposition,与 push 风格冲突。首选方案:用 Info MetricFamily target_info(单 Metric 无 label)承载目标元数据;该族 SHOULD 放在 exposition 最前(供 ingestor 免缓冲)。Exposer MUST NOT 除非显式配置否则给 exposition 所有 Metric 加目标元数据 label;MUST NOT 根据目标元数据给族名加前缀或变体。通用监控系统的 exposition 可能含多个 target_info;MUST NOT 因目标元数据改变指标名(如不得统一加 staging_ 前缀)。
  • Client Calculations and Derived Metrics:计算应交由 ingestor;唯一例外是 Summary quantile(向后兼容所需)。不要暴露 5 分钟平均增长率 gauge、平均事件大小、request failure ratio;改用独立 counter。标准偏差未纳入 Histogram 值(float64 精度不足,53 位尾数平方后仅剩半数,10k 事件/秒时约 2 小时即丢精度;int64 因无浮点小数点、纳秒精度下约 19 次观测即溢出);可待 128-bit 浮点普及时重议。
  • Number Types:float64 有 53 位尾数,每秒百万次递增的 counter 需一个多世纪才开始丢精度,但 100Gbps 网络接口在约 20 小时内可能开始丢精度;高吞吐整型数据可考虑 int64。Summary quantiles 必须 float64。
  • Exposing Timestamps:一般 Sample 时间戳不应暴露,由 ingestor 自行打时间戳。已带时间戳的数据(尤其被通用系统摄取过的)可携带时间戳;实时/原始数据不应带。同一 Sample 值+同一时间戳跨 exposition 暴露合法,但底层指标已缺失时则不合法。Ingestor 应能附加自身视角的当前时间戳。
    • Tracking When Metrics Last Changed:正确写法是不带时间戳 my_counter_total 1;带时间戳 my_counter_total 1 123 错误。若需记录最后变化时间,应另建 gauge(如 my_counter_last_increment_timestamp_seconds 123)。暴露绝对时间戳(epoch)优于 elapsed/since(my_boot_time_seconds 1256060124 优于 my_time_since_boot_seconds 123)。
    • Exemplar 时间戳无 best-practice 限制;可能因竞态/时钟不同步略超前。当降采样到秒导致两个 Sample 时间戳看似重复时,MUST 使用时间戳最早的那个 Sample。
  • Thresholds:通用真值可发 gauge(如 HVAC 的 setpoints);随规模/部署/时间变化的阈值暴露可能适得其反。示例 acme_notifications_queue_capacity 10000 与 acme_notifications_queue_length 42。
  • Size Limits:本标准不规定 sample 数、label 数、stateset state 数、info label 数、各类字符长度上限。Ingestor 可自设限制(防攻击/防中断),但须顾及合理用例;超限则整个 exposition MUST 被拒收。影响性能的三要素:唯一时间序列数、跨时间 sample 数、唯一字符串数(名/label name/label value/HELP)。唯一时间序列数 ≈ 文本格式非注释行数;截至 2020,1000 万条时间序列被视为很大,常为单实例 ingestor 上界量级;单次 exposition 未经审慎评估不应超 1 万条时间序列。若目标是单例(暴露整集群),几十万条可能合理。关键是时间序列总量的数量级,而非族数或单 label/bucket/stateset 基数(1000 个各含 1 Metric 的 gauge 与 1 个含 1000 Metric 的 gauge 等价)。所有目标暴露相同时间序列集时,新增目标字符串几乎无增量成本;各自唯一字符串则有成本(1000 个目标各暴露唯一 36 字符 UUID 比单个 10k 字符指标名贵三倍以上)。1000 万时间序列下每小时 100MB 唯一字符串可能更接近事件日志而非指标。

9.2.9 Security 与 IANA

  • 实现 MAY 提供认证/授权/计费;若提供 SHOULD 在 OpenMetrics 之外处理。
  • 所有 exposer 实现 SHOULD 能用 TLS 1.3+ 保护 HTTP;不支持加密时运维 SHOULD 用反代/防火墙/ACL。
  • 指标暴露应与面向终端用户的生产服务独立;对公网暴露服务,不建议在 TCP/80、TCP/443、TCP/8080、TCP/8443 上放 /metrics。
  • 多数 Prometheus exposition 实现仍用非 IANA 注册端口;OpenMetrics 有明确定义的端口。IANA 为暴露数据的客户端分配端口为 <9099 requested for historical consistency>。
  • 多端点共用同一 IP:port 时可用反代经 localhost 通信;为便于多路复用,端点 SHOULD 在路径中带自身名(如 /node_exporter/metrics);exposition SHOULD NOT 合并为一个(允许独立摄取、避免单点故障)。
  • OpenMetrics 希望注册 MIME 类型 application/openmetrics-text。

引自:OpenMetrics 2.0

9.3 Remote Write 1.0 规范

元信息:Version 1.0;Status Published;Date April 2023。定位为记录并标准化现有已被广泛采用的事实协议,不提出任何新东西。关键词按 RFC 2119 解读。文首 NOTE 指向存在 2.0 版本。

9.3.1 背景与设计取舍

  • 目标:把样本实时、无损地从 Sender 可靠传播到 Receiver。
  • 协议无状态,消息间严格无通信,因此不算"流式";要产生流式效果应在同一连接上连发多条消息(HTTP/1.1 或 HTTP/2)。
  • 曾考虑 gRPC,但当时未被广泛采用,且把 gRPC 服务暴露到 AWS EC2 ELB 这类负载均衡器后面很困难。
  • 协议支持批处理(单个请求里放多个不同 series 的样本);同一 series 的多样本同请求并不常见,但协议支持。
  • 协议不是给应用直接 push 指标用的;设计意图是兼容"发送方抓取(scrape)已插桩的应用/exporter"再发 remote write 消息给服务端。
  • 一致性测试套件:https://github.com/prometheus/compliance/tree/main/remotewrite/sender。

9.3.2 术语(Glossary,MUST 遵守)

  • Sender:发送 Prometheus Remote Write 数据的东西。
  • Receiver:接收 remote write 数据的东西。
  • Sample:(timestamp, value) 二元组。
  • Label:(key, value) 二元组。
  • Series:由唯一标签集标识的一个 sample 列表。

9.3.3 协议(Protocol)

协议 MUST 由如下签名的 RPC 构成(1.0 的 protobuf schema):

func Send(WriteRequest)

message WriteRequest {
  repeated TimeSeries timeseries = 1;
  // Cortex uses this field to determine the source of the write request.
  // We reserve it to avoid any compatibility issues.
  reserved  2;

  // Prometheus uses this field to send metadata, but this is
  // omitted from v1 of the spec as it is experimental.
  reserved  3;
}

message TimeSeries {
  repeated Label labels   = 1;
  repeated Sample samples = 2;
}

message Label {
  string name  = 1;
  string value = 2;
}

message Sample {
  double value    = 1;
  int64 timestamp = 2;
}
  • Sender MUST 把 WriteRequest 编码进 HTTP POST 的 body,按给出的 URL 路径发给 Receiver;Receiver MAY 指定任意 HTTP URL 路径收指标。
  • 时间戳 MUST 为 int64,以 Unix epoch 起算的毫秒;值 MUST 为 float64。
  • HTTP 请求 MUST 带以下头:
Content-Encoding: snappy
Content-Type: application/x-protobuf
User-Agent: <name & version of the sender>
X-Prometheus-Remote-Write-Version: 0.1.0
  • 客户端 MAY 允许用户自定义 HTTP 头,但 MUST NOT 允许以能发送保留头的方式配置(参见 prometheus/prometheus#8416)。
  • body MUST 用 Google's Snappy 压缩,且 MUST 用 block format,MUST NOT 用 framed format。
  • 请求 MUST 用 Google Protobuf 3 编码,且 MUST 使用上面定义的 schema。注:Prometheus 实现使用了 gogoproto 优化,非 Golang 的接收方 MAY 用等价的行级类型替换 gogoproto 类型。
  • Receiver 的响应体 SHOULD 为空;客户端 MUST 忽略响应体(响应体为未来预留)。

9.3.4 前后向兼容与版本协商

  • 协议遵循 SemVer 2.0:任意 1.x 兼容 Receiver MUST 能读任意 1.x 兼容 Sender,以此类推;破坏性/不兼容变更将产生 2.x 版本。
  • proto 格式自身的兼容性:删除字段 = major bump;新增(可选)字段 = minor bump。
  • 协商规则:
    • Sender MUST 在 header 中发送版本号;
    • Receiver MAY 在响应头 X-Prometheus-Remote-Write-Version 中返回其支持的最高版本号;
    • 想用 >1.x 格式的 Sender MUST 先发一个空的 1.x,看响应是否声明支持别的版本;Sender MAY 使用任意被支持的版本。若响应中无版本头,Sender MUST 假定仅 1.x 兼容。

9.3.5 标签(Labels)规则

  • MUST 随每个 sample 发送完整标签集;并且与样本关联的标签集:
    • SHOULD 含 __name__ 标签;
    • MUST NOT 含重复标签名;
    • MUST 按字典序排列标签名;
    • MUST NOT 含空标签名或空标签值。
  • Sender MUST 只发送合法的 metric name / label name / label value:
    • Metric name MUST 匹配正则 [a-zA-Z_:]([a-zA-Z0-9_:])*;
    • Label name MUST 匹配正则 [a-zA-Z_]([a-zA-Z0-9_])*;
    • Label value MAY 为任意 UTF-8 字符序列。
  • Receiver MAY 对标签数量与长度设限(receiver 特定,超出本文范围)。
  • 以 __ 开头的标签名 RESERVED 给系统用,SHOULD NOT 使用。
  • 含非法样本的写请求:Receiver MAY 只写入其中合法样本,但 MUST 返回 HTTP 400 Bad Request;SHOULD 在响应体给人类可读错误信息。Sender MUST NOT 尝试解析错误信息,SHOULD 原样记日志。

9.3.6 顺序(Ordering)

Sender MUST 对任一给定 series 按时间戳顺序发送样本;MAY 对不同 series 并行发多个请求。

9.3.7 重试与退避(Retries & Backoff)

  • Sender MUST 在 HTTP 5xx 上重试,且 MUST 使用退避算法以免压垮服务端。
  • Sender MUST NOT 在 HTTP 2xx、4xx(429 除外) 上重试;MAY 在 429 上重试(若服务端跟不上,可能导致 Sender "falling behind")。这样设计是为:服务端错误时不丢数据、客户端错误时仍能推进。
  • Receiver:写入成功 MUST 返回 HTTP 2xx;写入失败且应重试 MUST 返回 5xx;请求非法、永不可能成功、不应重试 MUST 返回 4xx。

9.3.8 过期标记(Stale Markers)

  • Sender MUST 在某个时间序列不再被追加时发送 stale marker。
  • MUST 用特殊 NaN 值 0x7ff0000000000002 表示;该值 MUST NOT 挪作他用。
  • 常见探测手段:① 服务发现发现目标消失;② 相邻两次抓取间发现目标不再暴露该序列;③ 抓取原暴露该序列的目标失败;④ 跟踪 recording/alerting rule 的配置与求值。

9.3.9 范围之外(Out of Scope)

主题 说明
up 指标 定义与语义不在协议范围,应单独记录
HTTP Path 路径可为任意值,MUST 由 Sender 提供,通常在配置里给完整 URL
持久化 建议 Sender 在 Receiver 故障时持久缓冲样本数据
认证与加密 因走 HTTP,视为传输层问题;收发双方应支持常规方案(Basic auth、TLS 等),可自定义认证;不应假定 Sender/agent 支持自定义认证
Remote Read 是另一套已迭代的接口,使用较少
分片(Sharding) Prometheus 现有的 remote write 并行分片是实现细节、不属于规范;一旦实现并行,MUST 保持每 series 的样本顺序
回填(Backfill) 规范不限制能推多老的序列,但实现可能有约束
限制(Limits) 标签数量/长度、batch size 等不在本文范围,但实现应设合理上限
Push-based Prometheus 应用直接推指标不是本系统设计目标,应另文讨论
Labels 每个 series MAY 含 job 和/或 instance 标签(通常由 Sender 的服务发现添加),非强制

9.3.10 未来计划(非规范内容)

  • 事务性:希望像 scrape 那样"对齐"remote write,例如把单次抓取的 samples、metadata、exemplars 放进单个 remote write 请求;尚未设计。
  • Metadata 与 Exemplars:Prometheus 当前对发送 metadata(type/help)与 exemplars 是实验性支持;未来版本可能强制打包进单请求。
  • 优化:希望通过消除 label 名/值的重复来减小消息体积。

9.3.11 相关与 FAQ

兼容 Sender/Receiver 清单(截至 2023-04):Prometheus(双向)、Avalanche(sender)、Cortex(receiver)、Elastic Agent(receiver)、Grafana Agent(双向)、GreptimeDB(receiver)、InfluxData Telegraf(双向)、M3(receiver)、Mimir(receiver)、Oodle(receiver)、OpenTelemetry Collector(sender,最终也做 receiver)、Thanos(receiver)、Vector(双向)、VictoriaMetrics(receiver)。

FAQ 要点:

  • 为何不用 gRPC:最初确实用过,2016 年难穿过 ELB(prometheus/prometheus#1982),改用 HTTP 上的 protobuf。
  • 为何不做流式 protobuf:持久 HTTP/1.1 连接已接近流式;头需重发但比新建 TCP 便宜。
  • 为何按序发样本:来源于 Prometheus 内部 append-only 的时序编码;未来版本可研究缓冲重排以解除约束。
  • 如何并行:样本只需同 series 内有序;按标签分片到不同队列、队列内串行,从而同 series 有序、不同 series 并行。这是为生态完整性,避免出现"能写 Prometheus、不能写 Cortex/Thanos"的 agent 分叉。

引自:Prometheus Remote-Write 1.0 specification

9.4 Remote Write 2.0 规范 [EXPERIMENTAL]

元信息:Version 2.0-rc.5;Status Experimental;Date May 2024。关键词同按 RFC 2119 解读。

定位:对 1.0 做 protocol 与 semantics 的小幅修改,新增一个 Protobuf Message 承载新特性(更多用例、更广采用、性能与成本更优);弃用 1.0 的 Protobuf Message;新增强制的 X-Prometheus-Remote-Write-*-Written HTTP 响应头以保证可靠性;并给出用基础 content negotiation 请求头实现**向后兼容收发双方(甚至共用单个 endpoint)**的做法。更高级的自动协商可能留给未来 minor 版本。

免责 NOTE:这是 RC,处于实验态;预计不会有大改,但保留基于早期采用者反馈破坏兼容性的权利;反馈应提到 proposals PR #35。

9.4.1 新增/变更术语

  • Remote-Write:本 Prometheus 协议名。
  • Protocol:使 client 与 server 传输 metrics 的通信规范。
  • Protobuf Message:本协议数据结构的 content type 定义;只用 Google Protocol Buffers,schema 定义在 .proto 文件中。
  • Wire Format:数据在网络上的形式;对 Remote-Write 永远是压缩后的二进制 protobuf。
  • Sender / Receiver:发送方 / 接收(写入)方。Written 的含义由 Receiver 决定——通常指存入数据库,也包括仅校验、拆分或增强。Written 指 Receiver 已接收并接受的数据,是否落持久存储/WAL 由 Receiver 决定;与"显式以错误拒绝"相对。
  • Sample:(start timestamp, timestamp, value) 三元组(1.0 是二元组)。
  • Histogram:(start timestamp, timestamp, histogram value) 三元组。
  • Label:(key, value);Series:由唯一标签集标识的 sample(或 histogram)列表。

9.4.2 协议(Protocol)—— 1.0 vs 2.0 消息选择

  • 协议 MUST 由 RPC 构成,请求体 MUST 用 Google Protocol Buffers 序列化后再压缩。
  • 序列化 MUST 使用以下两种消息之一:
    • prometheus.WriteRequest(1.0 引入)——自 2.0 起弃用,SHOULD 仅因兼容而使用;收发双方 MAY NOT 支持它;
    • io.prometheus.write.v2.Request(本规范引入)——收发双方 SHOULD 尽可能使用,且 MUST 支持。
  • Protobuf Message MUST 用二进制 Wire Format,再 MUST 用 Google's Snappy 压缩;MUST 用 block format,MUST NOT 用 framed format。
  • Sender MUST 把序列化压缩后的消息放进 HTTP POST body 发给 Receiver 给出的 URL 路径;Receiver MAY 指定任意路径。
  • Sender MUST 发送以下保留头:Content-Encoding、Content-Type、X-Prometheus-Remote-Write-Version、User-Agent。Sender MAY 允许用户加自定义头,但 MUST NOT 允许配置出这些保留头。

9.4.3 四个头的具体要求

  • Content-Encoding:MUST 遵守 RFC 9110;Sender MUST 用 snappy;Receiver MUST 支持 snappy。新压缩算法可能出现在 2.x 及以后。
  • Content-Type:MUST 遵守 RFC 9110。Sender MUST 仅用 application/x-protobuf 作为媒体类型,MAY 追加 ;proto=<fully qualified name>。因此 Sender MUST 发送以下三种头值之一:
Content-Type: application/x-protobuf
Content-Type: application/x-protobuf;proto=prometheus.WriteRequest
Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request
  • 面向 1.x Receiver 时 SHOULD 用不带 proto= 的 application/x-protobuf 以求向后兼容;否则 SHOULD 用带 proto=io.prometheus.write.v2.Request 的形式。
  • Receiver MUST 依据 content type 判定用哪个 schema;选错 schema 可能导致非确定性行为(如数据损坏)。
  • NOTE:得益于 v2 消息的 reserved 字段,Receiver 若误用 prometheus.WriteRequest schema 解 v2 载荷,会得到空消息——便利但不可依赖,未来消息未必有此特性。
  • X-Prometheus-Remote-Write-Version:面向 1.x Receiver 时 MUST 用 0.1.0;否则 SHOULD 用自身兼容的最新版本,如 2.0.0。
  • User-Agent:MUST 带,且 SHOULD 遵守 RFC 9110 的 User-Agent 格式。

9.4.4 响应语义(Response)

  • 全部数据成功写入的 Receiver MUST 返回 2xx;此时响应体 SHOULD 为空、状态码 SHOULD 为 204 No Content;Sender MUST 忽略响应体。
  • 异步处理(如入消息队列/管道延迟处理)的 Receiver MAY 返回 202 Accepted,表示已受理但未处理完。
  • 若 Receiver 已知的任一部分数据(Samples/Histograms/Exemplars)未同步成功写入(无论 partial write 还是整体拒绝)或未送入异步处理(如队列满),Receiver MUST NOT 返回 2xx;此时 MUST 在响应体给出人类可读错误信息,错误 SHOULD 含被拒样本数量与原因。Sender MUST NOT 解析错误信息,SHOULD 原样记日志。

9.4.5 强制 Written 响应头(2.0 核心新增)

内容协商成功后 Receiver 处理该批次;对每类重要数据(目前 Samples、Histograms、Exemplars)处理完毕(无论成败)后,Receiver MUST 发送对应的 X-Prometheus-Remote-Write-*-Written 响应头,值为成功写入元素的精确数量。

返回 202 的异步 Receiver 若无法同步计数 MAY 省略这些头(校验被推迟);此类 Receiver SHOULD 通过其他渠道把错误传播给客户端。

每个头值 MUST 是单个 64-bit 整数;头名固定为:

X-Prometheus-Remote-Write-Samples-Written    <count of all successfully written Samples>
X-Prometheus-Remote-Write-Histograms-Written <count of all successfully written Histogram samples>
X-Prometheus-Remote-Write-Exemplars-Written  <count of all successfully written Exemplars>
  • 收到 2xx 或 4xx 时,Sender 可假定缺失的头意味着该类元素写入数为 0。
  • 但 Sender MUST NOT 在以下情况如此假定:收到 202(异步可能推迟校验);使用弃用的 prometheus.WriteRequest(可能撞上没此特性的 1.0 Receiver)。
  • 用途:更精细处理 Partial Write(更准确的客户端埋点与错误处理);识别损坏的 1.0 Receiver 实现(用 v2 请求却返回非 202 的 2xx 且无任何 *-Written 头时,SHOULD 假定为 415 Unsupported Media Type——这类 Receiver 不检查 Content-Type,用 1.0 schema 解 v2 载荷得到空结果且不报错);以及检测其他收发实现缺陷。
  • Sender MUST NOT 依据 remote write 响应头去推断 Receiver 实现了哪个规范版本。未来可能有更多(可选)头。

9.4.6 Partial Write / 不支持的内容 / 非法样本 / 重试

  • Partial Write:Sender SHOULD 单请求发多 series 样本,因此 Receiver MAY 只写入部分合法样本(partial write);此时 Receiver MUST 依 Invalid Samples 与 Retries on Partial Writes 返回非 2xx。
  • Unsupported Request Content:Receiver 若不支持给定 content type 或 encoding,MUST 返回 415;Sender 对 1.x Receiver SHOULD 预期是 400(向后兼容)。
  • Invalid Samples:哪些样本非法由 Receiver 决定(有 Receiver 可能拒无 metadata type 或无 start timestamp 的样本,另一些接受)。含非法样本的请求,Receiver MUST 返回 400(除非发生可重试的部分写)。Sender MUST NOT 对 4xx(429 除外)重试;429 MUST 用于表示永不会成功、不应重试;Sender MAY 用不同 content type/encoding 对 415 重试以试探 Receiver 支持什么。
  • Retries & Backoff:
    • Receiver MAY 返回 429(过载)并可带 Retry-After 头指示下次尝试时间;MAY 返回 5xx 表示内部错误;
    • Sender MAY 在 429 上重试;MUST 在 5xx 上重试;MUST 用退避算法;MAY 处理 Retry-After 以估算下次重试时间;
    • 429 与 5xx 的差异原因:Receiver 跟不上或主动限流时,Sender 可选择不重试 429,从而在 Sender 侧错误时仍能推进,同时 5xx(Receiver 侧错误)不丢数据。
  • Retries on Partial Writes:部分写或部分非法样本时,若期望 Sender 重试整个请求,Receiver MAY 返回 5xx 或 429;此时 Receiver MUST 支持幂等性,因为 Sender MAY 用同一请求重试。

9.4.7 前后向兼容

  • 遵循 SemVer 2.0:任意 2.x 兼容 Receiver MUST 能读任意 2.x 兼容 Sender,反之亦然;破坏性变更将产生 3.x。
  • Protobuf Message 自身:删字段 = major bump;加(可选)字段 = minor bump。故 2.x 的未来 minor 版本 MAY 新增可选字段、新压缩、新 Protobuf Message 与协商机制,只要向后兼容(对收发双方均为可选)。
  • 2.x vs 1.x:2.x 通过引入强制的 io.prometheus.write.v2.Request 并弃用 prometheus.WriteRequest,与 1.x 破坏兼容。2.x Sender MAY 通过让用户配置 content type 来支持 1.x Receiver;也 MAY 在收到 415 时自动回退到其他 content type。

9.4.8 Protobuf Message:io.prometheus.write.v2.Request

权威 schema 在 Prometheus 仓库 prompb/io/prometheus/write/v2/types.proto;gogo 依赖与选项可忽略(将被移除),不属规范、不影响序列化格式。简化 schema:

message Request {
  reserved 1 to 3;

  // symbols contains a de-duplicated array of string elements used for various
  // items in a Request message, like labels and metadata items. For the sender's convenience
  // around empty values for optional fields like unit_ref, symbols array MUST start with
  // empty string.
  repeated string symbols = 4;
  repeated TimeSeries timeseries = 5;
}

message TimeSeries {
  reserved 6;
  repeated uint32 labels_refs = 1;
  repeated Sample samples = 2;
  repeated Histogram histograms = 3;
  repeated Exemplar exemplars = 4;
  Metadata metadata = 5;
}

message Exemplar {
  repeated uint32 labels_refs = 1;
  double value = 2;
  int64 timestamp = 3;
}

message Sample {
  double value = 1;
  int64 timestamp = 2;
  int64 start_timestamp = 3;
}

message Metadata {
  enum MetricType {
    METRIC_TYPE_UNSPECIFIED    = 0;
    METRIC_TYPE_COUNTER        = 1;
    METRIC_TYPE_GAUGE          = 2;
    METRIC_TYPE_HISTOGRAM      = 3;
    METRIC_TYPE_GAUGEHISTOGRAM = 4;
    METRIC_TYPE_SUMMARY        = 5;
    METRIC_TYPE_INFO           = 6;
    METRIC_TYPE_STATESET       = 7;
  }
  MetricType type = 1;
  uint32 help_ref = 3;
  uint32 unit_ref = 4;
}

message Histogram { ... }  // 支持稀疏指数分桶/自定义分桶;float 或 integer 直方图

所有时间戳 MUST 为 int64 毫秒(Unix epoch);sample 值 MUST 为 float64。

对每个 TimeSeries:

  • labels_refs MUST 提供;
  • samples、histograms、exemplars 中至少一个元素 MUST 提供;只有 metadata 与 labels_refs 的 TimeSeries 非法;
  • MUST NOT 同时包含 samples 和 histograms;混用 float 与 histogram 的序列 MUST 拆成独立 TimeSeries;
  • metadata 子字段 SHOULD 提供;Receiver MAY 拒绝未指定 Metadata.type 的序列;
  • 系列若有 exemplars,SHOULD 提供。

Symbols(字符串驻留 / 去重):

  • v2 消息对所有字符串做 string interning,在标准压缩之外带来额外的压缩与内存收益。
  • symbols 表 MUST 提供,且 MUST 含 series、exemplar 标签、metadata 字符串所去重后的字符串。
  • symbols 表第一个元素 MUST 是空字符串,用于表示空/未指定值(如 Metadata.unit_ref、Metadata.help_ref 未提供时)。
  • 引用 MUST 指向 symbols 数组中的已存在下标;字符串顺序由 Sender 决定,Receiver 不应假设任何特定编码。

Series Labels:

  • MUST 随每个 Sample/Histogram 发送完整标签集,且标签集:SHOULD 含 __name__;MUST NOT 含重复标签名;MUST 按字典序排列;MUST NOT 含空名或空值。
  • Metric name、label name、label value MUST 为任意 UTF-8 字符序列(相对 1.0 的放宽)。
  • Metric name SHOULD 匹配 [a-zA-Z_:]([a-zA-Z0-9_:])*;label name SHOULD 匹配 [a-zA-Z_]([a-zA-Z0-9_])*。不合规的名字对 PromQL 用户更难用。
  • 以 __ 开头的标签名 RESERVED,SHOULD NOT 使用。Receiver MAY 对标签数量/长度设限。

Samples 与 Histogram Samples(created/start timestamp):

  • Sender MUST 对给定 TimeSeries 按时间戳顺序发送 samples(或 histograms);MAY 对不同 series 并行发多请求。
  • Sample/Histogram 的 start_timestamp SHOULD 为具 counter 语义的类型(如 counter 与 counter histogram)提供;Receiver MAY 拒无 start_timestamp 的此类序列。
  • 因可选性,0 值 MUST 被 Receiver 视为未设置;要表达极罕见的 0 毫秒时间戳,MUST 用 "1" 或 "-1"。
  • 过期标记:
    • Sender SHOULD 在时间序列不再被追加时发 stale marker;若序列停用可被探测到,则 MUST 发送,例如:被拉取(scraped)的序列(除非用了显式时间戳);由 recording rule 求值产生的序列。
    • 一般地,不发送会引来 Receiver 侧非平凡的查询时间对齐问题(staleness)。
    • MUST 用特殊 NaN 0x7ff0000000000002;MUST NOT 挪作他用。
    • 探测手法同 1.0,另加第 5 条:对非抓取来源跟踪指标停用(如 k6 中某个 benchmark 结束后为其 series 发 stale marker)。

Metadata:SHOULD 遵循 Prometheus 官方的 Type 与 Help 指南;MAY 遵循 OpenMetrics 官方的 Unit 指南。

Exemplars:挂在 TimeSeries 上的每个 exemplar:

  • MUST 含 value;
  • MAY 含标签(如引用 trace/request ID);若引用 trace,SHOULD 用 trace_id 标签名作为最佳实践;
  • MUST 含 timestamp(Prometheus/OpenMetrics 暴露格式中 exemplar 时间戳可选,但此处假定抓取时分配;Receiver 需靠它可靠处理如去重);
  • MAY 出现在既无 samples 也无 histograms 的 TimeSeries 中,此时 exemplars 关联到由 labels_refs 标识的 series,而非某个具体 sample/histogram。

9.4.9 范围之外 / 未来计划 / 相关

  • Out of Scope:同 1.0。
  • Future Plans(2.0 已完成 1.0 三项未来计划中的两项):
    • 事务性:2.0 仍未定义事务性(会让可扩展 Sender 实现变难);但 2.0 借 native histogram 的自定义分桶解决了经典直方图桶的事务性问题;Sender 可把经典直方图翻译成 native histogram,但这不属本规范强制;因此 Receiver MAY 忽略某些指标类型(如经典直方图)。
    • 替代 wire format:OpenTelemetry 的 OTLP 已验证 Apache Arrow 等列式格式的可行性;希望实验并纳入基准测试,可能长期同时维护 protobuf 与列式格式并通过内容协商加新消息。
    • 全局 symbols:预定义静态 ref→symbol 字典(如 namespace、le、job、seconds、bytes),Sender 可引用而无需放进请求的 symbols 表;字典可随 minor 版本增长。
  • FAQ:
    • 不用 gRPC:1.0 没用,破坏它会增加采用摩擦。
    • 为何按序:约束来自 Prometheus 为 append-only 优化的时序编码,也是生态众多数据库共有的要求;开启 OOO 特性时允许乱序但有性能代价,故仅应急。规范不允许乱序(Receiver 可支持),未来 2.x 可用 content type 协商乱序写。
    • 如何并行:同 series 内必须有序;不同 series 可并行(Prometheus 按标签分片到独立队列、队列内串行)。
    • 与 OTLP 的区别:OTLP 是从零设计、覆盖 metrics/logs/traces/profiles 的多信号协议(推荐 gRPC+protobuf,也支持 HTTP+protobuf/JSON),metrics 侧含非标识标签、flags、时态聚合类型、resource/scoped metrics、schema URLs 等,并要求语义约定。Remote-Write 为简单、效率与有机增长而设计,2023 年正式发布时已有大量 CNCF 采用者用了多年;2.0 在旧协议上加 metadata、exemplars、start timestamp、native histograms 与字符串驻留;始终无状态、只聚焦 metrics、有主见,目标是比同类更便宜简单的稳定协议。

引自:Prometheus Remote-Write 2.0 specification

9.5 Native Histograms 规范

9.5.1 版本与状态

  • 2022-11 作为实验特性引入;Prometheus server v2.40.0 首次支持,需 --enable-feature=native-histograms。
  • v3.8.0 起为 stable;但仍需显式开启抓取:配置项 scrape_native_histograms。v3.8 中 feature flag 的唯一残留作用是默认把 scrape_native_histograms 设为 true;v3.9 起 feature flag 完全 no-op,必须显式设置该配置。远程写需 send_native_histograms。v4 起上述两项默认 true。
  • 声明按 RFC 2119 使用 MUST/MUST NOT/SHOULD/MAY/OPTIONAL 等关键词。

9.5.2 术语表(Glossary)

  • native histogram:新的复合 sample type,表示完整直方图(下文常简称 histogram)。
  • classic histogram:旧的固定 bucket 直方图类型,在 exposition 格式中以整体存在,摄入时被拆成多个 float sample。
  • Sparse histogram:已废弃的旧称(等同 native histogram);sparse buckets 仍是描述 native histogram bucket 的有效术语。

9.5.3 核心概念与设计目标

  • 把直方图提升为 data model 的一等公民("native" 命名由来)。单个 sample 表示 sum + count + 动态 bucket 集合;PromQL 表达式也可返回该类型。
  • 6 大特性:
    1. 稀疏 bucket 表示,空 bucket(近)零成本;
    2. 覆盖完整 float64 范围;
    3. 埋点无需配置 bucket 边界;
    4. 由简单配置参数决定动态分辨率;
    5. 指数分桶 schema,同 schema 间可合并;
    6. exposition 与存储均高效表示。
  • 早期名 "sparse histograms" 强调稀疏,但未涵盖指数分桶与动态分辨率,故改名。

9.5.4 数据模型(Data model)

总体结构:字段含 count(观测数)与 sum(观测和,任意 float64)。另含:schema、正/负观测各一份的稀疏索引 bucket 表示、zero bucket、custom values 列表(可空)、Exemplars。

Flavors(两个独立维度):

  1. counter vs. gauge:counter-like(bucket 为计数器)或 gauge-like(bucket 在某一时刻表示任意分布)。gauge histogram 概念沿用 OpenMetrics gaugehistogram。
  2. integer vs. float:integer histogram 的 bucket 数、zero bucket、total count 均为 uint64;float histogram 同些值为 float64("weighted/scaled" 场景)。sum 始终为 float64。
  • PromQL 只处理 float(float histogram 通用):TSDB 取出后引擎先转 float histogram;recording rule 写回的也是 float histogram。若其非 sum 字段可精确表示为 uint64,TSDB 实现 MAY 转回 integer histogram 以省空间(v3.00 的 TSDB 未启用)。
  • negative histogram(如乘 -1)仅允许作为中间结果,在 PromQL 内始终视为 gauge;无法用任何交换格式(exposition/remote-write/OTLP)表示,也不能存入 TSDB。
  • 引入 integer/float 区分的主要理由:protobuf varint 对小整数编码更短,配合 integer bucket 的 delta encoding 使多数整数 1–2 字节,多 bucket 时数据量接近 8x 缩减(float 恒 8 字节)。
  • counter histogram 的 total count 与各 bucket 单独表现为 Prometheus counter(仅在 counter reset 时下降);但 sum 可因观测负值而下降。PromQL 实现 MUST 基于整个 histogram 检测 counter reset。

Schema:

  • int8 有符号整数,定义 bucket 边界计算方式。合法值:-53,以及 -4 至 +8(闭区间);-9 至 +52 为保留区。
  • -53 = custom bucket boundaries(custom buckets / NHCB);其余为标准指数 schema(standard schemas)。
  • 标准 schema 相互可合并,RECOMMENDED 一般使用。schema 数字越大分辨率越高;schema n 分辨率是 n+1 的一半,故 n+1 可合并相邻 bucket 转成 n。
  • 标准 schema n 下索引 i 的 bucket 边界(Python 语法):正 bucket 上界(含)(2**2**-n)**i,下界(不含)(2**2**-n)**(i-1);负 bucket 下界(含)-((2**2**-n)**i),上界(不含)-((2**2**-n)**(i-1))。i 可为负整数。
  • MaxFloat64/MinFloat64 与 ±Inf 例外:含 MaxFloat64 的正 bucket 上界(含)取 MaxFloat64(避免溢出);其下一个正 bucket 下界(不含)为 MaxFloat64、上界(含)为 +Inf(正溢出桶)。负向对称(含 MinFloat64 的 bucket 下界取 MinFloat64;下一个负 bucket 上界不含为 MinFloat64、下界含为 -Inf,负溢出桶)。+Inf/-Inf 桶之外 MUST NOT 使用。
  • 取值范围依据:>52 时相邻 bucket 增长因子小于 float64 可表示差;<-9 时增长因子超过 float64 最大值。故 -9..+52 保留给未来标准 schema,MUST NOT 他用。
  • 接收方 MAY 在摄入时降低 schema(合并 bucket);MAY 接受 9..52 的 schema 并在摄入时降到合法值。若仍未知:scrape(含 federation)中任何一个未知 schema 的 histogram 存在则整个 scrape MUST 失败(避免不完整抓取);其他摄入路径(含 WAL/WBL 重放)MAY 忽略并 SHOULD 通知用户。TSDB 从永久存储读取(不含 WAL/WBL 重放):9..52 MAY 转换;否则未知 schema MUST 在检索时报错,触发它的 PromQL 查询 MUST 失败。
  • -53 边界由 custom values 显式设定,可表示 classic histogram 或指数分桶不匹配的分布;不同 custom 边界的 histogram 一般不可相互合并,SHOULD 仅在明确场景审慎使用。

Buckets:

  • 标准 schema 用两个 list(正、负);custom buckets 只用正 bucket list(复用)。
  • 空 bucket MAY 从 list 中省略("sparse buckets" 由来)。
  • float histogram:list 元素为 float64,直接表示 population(一般非负,仅 PromQL 中间结果可为负)。
  • integer histogram:元素为 int64,每个元素是相对前一 bucket 的 delta(首 bucket 为绝对量,等价于相对 0 的 delta)。delta MUST NOT 使绝对 population 变负。
  • Spans:映射 list 中 bucket 到索引。每对为 offset(int32)与 length(uint32)。仅每个 list 的首 span 可负 offset,定义该 bucket list 首 bucket 的索引(NHCB 索引恒为正)。length 定义紧随其后的连续 bucket 数;后续 span 的 offset 定义被排除(未填充)的 bucket 数,length 定义随后连续 bucket 数。
  • 每个 span list 的 length 之和 MUST 等于对应 bucket list 长度。
  • length=0 的空 span 有效且 MAY 使用,但一般无用,SHOULD 通过把其 offset 并入下一 span 消除;非首 span 可 offset=0,SHOULD 通过把其 length 并入上一 span 消除。两者允许以给生产者择优选表示的灵活性。
  • 索引溢出:int32 最大 2147483647;schema 8 中含 MaxFloat64 的 bucket 索引 262144、+Inf 溢出桶索引 262145;仍可用 int32 的最高标准 schema 为 20(相邻增长因子约 1.000000661)。
  • 示例(integer histogram,正 bucket -2→3, -1→5, 0→0, 1→0, 2→1, 3→0, 4→3, 5→2):
    • list [3, 2, -4, 2, -1] + spans [[-2,2],[2,1],[1,2]];
    • 显式表示索引 3 一个空 bucket 后合并第二、三 span:list [3, 2, -4, -1, 3, -1] + spans [[-2,2],[2,4]];
    • 全显式则合并为单 span:list [3, 2, -5, 0, 1, -1, 3, -1] + spans [[-2,8]]。

Zero bucket:

  • 恰好为 0 的观测不入标准 bucket,进 zero bucket;计数用单个 uint64(integer)或 float64(float),一般非负。
  • zero threshold:float64 ≥ 0。为 0 时仅恰好 0 进入;为正时 [-threshold, +threshold] 闭区间内观测全部进入 zero bucket。用于:抑制零附近噪声;或对远离 0 的长尾分布避免大量高分辨率 bucket。
  • threshold SHOULD 与某个常规 bucket 边界重合;若重叠,计在重叠常规 bucket 中的观测 MUST 在 [-threshold, +threshold] 之外。
  • 合并规则:同 threshold 时两 zero bucket 直接相加;不同时取源中最大 threshold,若其落在其他源的已填充 bucket 内则增加 threshold 直到(对每个源)新 threshold 与已填充 bucket 边界重合、或不在任何已填充 bucket 内;随后把各源 zero bucket 及现落入新 threshold 内的 bucket 相加。
  • schema -53 不使用 zero bucket。

Custom values:

  • 标准 schema 下未用;仅 -53 使用。表示 custom bucket 的上界(含),升序排列。custom bucket 存于正 bucket list 与正 span list(边界可为负);其索引即上界在 custom values 中的零基位置。
  • 下界(不含)为前一个 custom value;首个(位置 0)无前值,下界视为 -Inf(含)。故索引 0 的 bucket 统计 [-Inf, 第一个custom value]。仅观测正值时,索引 0 的 bucket SHOULD 上界为 0,以标记是否出现 0 或以下观测(若只有正值,该 bucket 保持未填充、不显式表示)。
  • custom values MUST NOT 为 +Inf;大于最后一个 custom value 的观测进入上界 +Inf 的溢出桶,其索引等于 custom values 长度。故 classic histogram 常见的 +Inf 上界通常不在 custom values 中显式出现。
  • custom values MUST NOT 为 NaN;此类 classic histogram MUST 被拒绝,不能转为 NHCB。

Exemplars:native histogram sample 可有 0/1/多个 exemplar,组织为列表且 MUST 带时间戳。classic histogram 的 exemplar 若带时间戳 MAY 被 native histogram 复用。

特殊观测值:

  • 埋点 SHOULD 避免观测 NaN/±Inf,但 MUST 正确处理。
  • sum 按常规浮点加法(NaN 观测使 sum 为 NaN;+Inf 使 sum 为 +Inf,除非已是 NaN/-Inf 则为 NaN)。
  • NaN 观测不入任何 bucket,但使 count 自增;故 count 可大于所有 bucket 之和,差额即 NaN 观测数。
  • ±Inf 观测使 count 自增并递增:标准 schema 下 +Inf→正溢出桶、-Inf→负溢出桶;schema -53 下 +Inf→索引等于 custom values 长度的 bucket、-Inf→索引 0 的 bucket。

OpenTelemetry 互操作:

  • 标准 schema 可与 OTel exponential histogram 互转:Prom schema = OTel scale(OTel 允许低于 -4/高于 +8);Prom 索引 n 对应 OTel n-1。OTel 为稠密表示(可看作"只有一个 span 的 Prom")。Prom zero bucket = OTel zero count,均含 zero threshold(OTel 未给时隐含 0)。
  • NHCB(及其他未来 schema)无法干净转为 OTel exponential histogram,但可转常规固定 bucket 的 OTel histogram。OTel 的 min/max 字段在 Prom 无对应概念(counter histogram 长期累积、随时可抓取)。

9.5.5 编码格式(Exposition formats)

  • 背景:classic exposition 以字符串为主,故曾弃用 protobuf;native histogram 数值数据量大,故复活 protobuf-based exposition。
  • 经典 Prometheus 文本格式未扩展、也不计划扩展;OpenMetrics 截至 2024-11-03 不支持 native histogram(protobuf 版提案 PR #256 审核中;文本版设计文档在推进)。两者 wire format 相同:proto2(client_model/metrics.proto)与 proto3(prompb/io/prometheus/client/metrics.proto)。

Protobuf 消息结构(proto3 关键部分):

message Histogram {
  uint64 sample_count       = 1;
  double sample_count_float = 4; // > 0 时覆盖 sample_count
  double sample_sum         = 2;
  repeated Bucket bucket    = 3; // classic 用,按 upper_bound 递增,+Inf 桶可选
  google.protobuf.Timestamp start_timestamp = 15;

  sint32 schema           = 5;  // 目前 -4 <= n <= 8
  double zero_threshold   = 6;  // 零桶宽度
  uint64 zero_count       = 7;  // 零桶计数
  double zero_count_float = 8;  // > 0 时覆盖 zero_count

  repeated BucketSpan negative_span = 9;
  repeated sint64     negative_delta = 10; // integer:相对前一桶的增量(首桶相对 0)
  repeated double     negative_count = 11; // float:每桶绝对计数

  repeated BucketSpan positive_span = 12; // 无观测且 zero_threshold=0 时用 no-op span(0,0)
  repeated sint64     positive_delta = 13;
  repeated double     positive_count = 14;

  repeated Exemplar exemplars = 16; // MUST 带时间戳
}
message BucketSpan { sint32 offset = 1; uint32 length = 2; }

要点:

  • native 与 classic 共用同一 Histogram 消息(在旧消息上扩展字段);sum/count/start_timestamp 两者共享且行为一致;扩展时顺带加入 classic float histogram 支持(sample_count_float、cumulative_count_float)。
  • 桶 population:float 用绝对值,integer 用相对前一桶(首桶相对 0)的 delta → 值更小、varint 编码更短(sint64)。
  • 未观测且 classic 无 bucket 的 native histogram 与 classic histogram 的 protobuf 完全相同,故欲被解析为 native 的消息 MUST 在 positive_span 中放 no-op span(offset=0、length=0)。
  • native exemplar MAY 放入重复 Exemplar 字段但每个 MUST 带时间戳;若未提供,parser MAY 用 classic bucket 上带时间戳的 exemplar(每 bucket 最多一个)。exemplar payload SHOULD NOT 远大于消息其余部分,且 SHOULD 分布在不同 bucket、近似均匀覆盖 bucket 范围(优于按观测分布比例放置,后者罕见取得长尾 exemplar)。
  • 无 custom values 的表示:protobuf 不表示 NHCB 的 custom values;NHCB 从不直接暴露,而是以 classic histogram 呈现,摄入时再转回 NHCB(federation 同理)。

9.5.6 埋点库(Instrumentation libraries)

  • 官方支持 native histogram 的库(2026-06-15):Go client_golang、Java client_java、Rust client_rust。
  • 埋点 API 不变;库可同时维护 classic 与 native 版本并并行暴露,由用户配置选择。
  • SHOULD 提供的配置参数(括号为默认值):NativeHistogramBucketFactor(1.1,>1 的 float,决定初始分辨率,库选一个使相邻 bucket 宽度增长不超过该因子的起始 schema);NativeHistogramZeroThreshold(2^-128)。
  • 分辨率用增长因子而非直接给 schema(用户不易理解 schema 数学)。因子 → schema 对照表:
增长因子 schema
65536 -4
256 -3
16 -2
4 -1
2 0
1.5 1
1.2 2
1.1 3
1.05 4
1.03 5
1.02 6
1.01 7
1.005 8
  • 限制 bucket 数(DoS 防护):库 SHOULD 提供 bucket 限制策略。Go 库策略由三参数定义:NativeHistogramMaxBucketNumber(uint,0=不限,其余参数被忽略;典型值 160)、NativeHistogramMinResetDuration(duration)、NativeHistogramMaxZeroThreshold(float)。超限时依次采用:
    1. 距上次 reset ≥ MinResetDuration 则整个 histogram reset(视为正常 counter reset,桶/和/计数/零桶清零);
    2. 否则(或 MinResetDuration=0)不 reset,而是增大 zero threshold 把近零 bucket 并入零桶,上限为 MaxZeroThreshold;
    3. 仍超限则降到下一更低 schema(相邻 bucket 合并、宽度加倍),重复至满足或到达 schema -4。 步骤 2/3 生效后,待 MinResetDuration 过去会执行一次 reset 以恢复初始 zero threshold 与分辨率(含更新 start timestamp)。
  • 全用高分辨率 + 合理 MaxBucketNumber 的策略(OTel 默认做法)在 Prometheus 场景不推荐:会引起埋点程序与 TSDB 的 churn,且典型查询需合并大量 histogram,最终取最低公共分辨率。
  • 按标签分区:native histogram 比 classic 更宽松;分区后各 histogram 通常填充更少 bucket,总填充 bucket 数的增长倍数明显小于分区倍数。
  • NHCB:截至 2024-11-03 埋点库无法直接配置 NHCB;直接需要的场景现做法是用 classic histogram 埋点并让 scraper 摄入时转 NHCB。

9.5.7 抓取配置与兼容性

  • 开启抓取:scrape_native_histograms: true(单 config 或全局);开启会改变内容协商,优先 classic protobuf exposition 而非 OpenMetrics 1.x 文本。
  • scrape_protocols 可全局/单 config 微调协商优先级;若开启 native 且未由 flag/用户设置,其有效值变为 [PrometheusProto, OpenMetricsText1.0.0, OpenMetricsText0.0.1, PrometheusText0.0.4]。只要 classic Prometheus protobuf 是唯一支持 native 的格式,则需同时开启 scrape_native_histograms 且协商 protobuf 方能摄入 native。
  • 注意:文本格式抓取一般比 protobuf 抓取更省资源(tracking issue #14668);v2 下 quantile/le 标签格式化在某些情况下不一致(v3 一致),与 native 无直接关系但因需 protobuf 而可能同时出现。
  • 摄入时限制 bucket 数与分辨率(因用户可能无法改埋点、或故意高分辨率供不同 scraper 选择):
    • native_histogram_bucket_limit(单个 histogram bucket 数上限,标准 schema 超限则反复降 schema 至满足;NHCB 超限或 schema -4 仍超限则 scrape 失败);
    • native_histogram_min_bucket_factor(相邻增长因子下限,仅标准 schema,NHCB 无效;到 schema -4 仍满足不了也成功)。 两者接受 0 表示"不限"。Prometheus 目前最多存 schema 8,但接受 >8 至保留上限 52 并在摄入时降到 8(或按上述设置降更低)。
    • 注意埋点的 bucket factor 是上限、抓取配置的是下限,故导出 schema 略有差异:
埋点 factor → 摄入后 schema 值
1.4 1
1.1 2
1.09 3
1.04 4
1.02 5
1.01 6
1.005 7
1.002 8
  • native_histogram_bucket_limit 设硬成本上限,native_histogram_min_bucket_factor 适合避免无谓资源开销。
  • 同抓 classic 与 native:scrape_native_histograms=false(v3 默认)时完全忽略 native 部分;true(v4+ 默认)时优先 native,classic 仅在无 native 数据时抓取。迁移场景可设 always_scrape_classic_histograms: true(默认 false)同时摄入两版(需 classic 至少一个 bucket 且 native 至少一个 bucket span,可为 no-op)。不冲突:classic 摄入为带后缀的多个 series,native 为单个原名 series(如 rpc_latency_seconds vs ..._sum/..._count/..._bucket{le=...})。
  • classic 转 NHCB:布尔配置 convert_classic_histograms_to_nhcb 可让 Prometheus 把 classic histogram 以 NHCB 摄入。NHCB 能自动协调不同 bucket 布局,但仅保留精确匹配的边界,布局差异大时可能只剩溢出桶一个 bucket。NHCB 存储成本低、追加 bucket 的边际成本低。

9.5.8 TSDB 存储与编码实现

  • integer 与 float histogram 分开存储;TSDB MAY 把 float histogram 按 integer histogram 存(若各 bucket 计数与总 count 都在 int64 范围内可精确表示)。Prometheus TSDB 尚未启用。
  • 两种新 chunk encoding(chunkenc.Encoding):
常量 字符串 数值 用途
chunkenc.EncHistogram histogram 2 integer histogram
chunkenc.EncFloatHistogram floathistogram 3 float histogram
  • 两种 WAL/内存快照 record type(record.Type):
常量 字符串 数值
record.HistogramSamples histogram_samples 9
record.FloatHistogramSamples float_histogram_samples 10

另有兼容旧 WAL 的 legacy 类型 ...Legacy(7、8),为引入 NHCB custom values 之前所用。

  • series 身份只由标签决定,sample type/flavor 不参与;同一 series MAY 混有不同类型/风味。TSDB 实现 MUST 处理类型变化,但 MAY 用较低效方式:遇到无法写入当前 chunk 的 sample 类型即关闭该 chunk、按合适编码开新 chunk。
  • 三种自定义数值编码(细节见 tsdb/docs/format/chunks.md):
    • varbit-int:有符号整数的可变位宽编码,1 bit–9 bytes,越接近 0 用位越少(类似 float chunk 时间戳编码,但位长分桶针对 histogram 取值分布优化);
    • varbit-uint:无符号整数的同族编码;
    • varbit-xor:float 序列的可变位宽编码,基于当前与前一个 float 的 XOR,1 bit–77 bits/float(与 float sample 现用编码完全相同)。
  • histogram chunk 布局:chunk 样本数(uint16)→ 1 字节描述 gauge/counter(并含 counter reset 信息)→ chunk layout(全 chunk 内所有 histogram 共享):零桶 threshold(自定义编码:零或某些 2 的幂 1 字节、任意值 9 字节)、schema(varbit-int)、正 spans(span 数 varbit-uint,随后每个 span 的 length varbit-uint + offset varbit-int,重复)、负 spans 同理,仅 schema -53 额外有 custom values(数量 varbit-uint + 逐个自定义编码)。
  • integer histogram 每样本数据:
    • timestamp:varbit-int,首样本绝对、第 2 样本相对第 1 的 delta、其后"delta of deltas"(同 float chunk 时间戳双 delta,仅 varbit-int 位分桶不同);
    • count:首样本 varbit-uint,其后 varbit-int,同双 delta;
    • zero bucket population:同上(首 varbit-uint,其后 varbit-int,双 delta);
    • sum:首样本 float64,其后 varbit-xor(当前与前一样本 XOR);
    • 正 bucket population:每桶相对前一桶的 delta(首桶绝对),varbit-int,再套双 delta(因值本身已是 delta,故称"triple delta")。负 bucket 同理。
  • float histogram 差异:count 与 zero bucket population 为 float,编码同 sum(首样本 float64、其后 varbit-xor);bucket population 为绝对值(非 delta),首样本全为 float64,其后 varbit-xor(对应 bucket 的当前与前一样本 XOR)。
  • 切割新 chunk 的触发条件:integer↔float 类型变化;gauge↔counter 变化;counter histogram 的 counter reset;schema 变化;zero threshold 变化;custom values 变化;staleness marker 后接常规 sample;超 chunk 尺寸上限。
  • span 差异也会改变 chunk layout,但通过追加显式未填充 bucket 使 chunk 内所有 histogram 共享 span 结构来协调。bucket 消失对 gauge histogram 可行(缺失桶补为未填充);但已填充 bucket 消失对 counter 是 counter reset。更常见的是新追加 histogram 出现此前不存在的 bucket,此时需给此前所有 histogram 补显式未填充 bucket,导致整 chunk 重编码(有优化空间但实现复杂,目前性能影响不突出)。
  • Staleness markers:float series 用特殊的 stale NaN 位模式。为免仅因标记 staleness 就切新 chunk(float 值不能接在 histogram 值后),另有 histogram 版 stale marker:将 sum 字段设为 special stale NaN,其余字段忽略(可设为利于存储的值)。float/integer/float-histogram 三种 stale marker MUST 被 TSDB 视为等价。
  • Chunk 尺寸限制:float chunk 限 1024 字节,histogram 一般沿用;但单个 histogram 可能很大,故需先达到最少 10 个 histogram/chunk 才启用 1024 字节限制,因此 histogram chunk 可远大于 1024 字节。
  • Counter reset 检测(较复杂,宜在摄入时做一次并持久化结果):
    • gauge 与 counter histogram 显式区分(不同于 float sample 摄入后不再区分 gauge/counter);gauge histogram 不适用 counter reset。gauge 后接 counter 视为发生 counter reset(等价于 gauge 删除、counter 从 0 新建)。
    • counter 接 counter:若都用标准 schema 但 schema 或 zero bucket 宽度不同,可能是"兼容的分辨率降低",须满足:schema 数字减小、且旧 histogram 的每个已填充常规 bucket 要么完全落入新 zero bucket、要么完全不落入(无部分重叠)。不满足则视为 counter reset 并结束;满足则先按前述方式转换第一个 histogram(合并相邻 bucket 降 schema、把常规 bucket 并入 zero bucket 增宽)。
    • 两个 NHCB 的 custom values 差异按"histogram 间兼容性"规则协调。
    • 此时两者 schema 与 zero bucket 宽度相同(NHCB 不用 zero bucket,其宽度与 population 视为相等)。构成 counter reset 的情形:count 下降(但 sum 下降不算);任一 bucket(含 zero bucket)population 下降——含已填充 bucket 消失(未表示 bucket 等价于 population 0)。
    • counter reset 后切新 chunk 可提升压缩率(reset 后 bucket 清零,chunk 需表示所有 histogram bucket 的并集)。故 chunk 中首样本后不会再有 counter reset,只需持久化首样本的 counter reset 信息。
    • histogram flags:chunk 样本数后一个字节,前两 bit 存 counter reset 信息,对应 chunkenc 包 CounterResetHeader 常量:
常量 位模式 含义
GaugeType 11 gauge histogram
CounterReset 10 发生 counter reset
NotCounterReset 01 未发生 counter reset
UnknownCounterReset 00 未知(总是安全,只要求重新检测)
  • 查询时以 CounterResetHint(Go 类型 Histogram/FloatHistogram 字段,常量同名)传给调用方。gauge histogram 的 hint 恒为 GaugeType;其他值即 counter histogram。chunk 内第 2 个及以后离序返回的 counter histogram hint 为 NotCounterReset。返回 counter chunk 首样本时 hint MUST 设为 UnknownCounterReset,除非实现能确保上一个返回的 histogram 正是摄入时用于检测 reset 的相邻 histogram(唯此可 MAY 直接用 chunk 信息)——因 tombstone 删除/回填插入会破坏原检测。目前 Prometheus 对所有 counter chunk 首样本都返回 UnknownCounterReset(issue #15346)。hint 为 UnknownCounterReset 时 querier MUST 重新执行检测。
  • 重叠 block/乱序样本需特别小心,会欠检测或过检测。凡 TSDB 无法确定摄入时已做检测,则须再检测一次或对第二个样本返回 UnknownCounterReset。
  • 存在检测不到的 reset:若 reset 后计数增长足够快,使得 reset 后首个样本无任何计数下降(float counter 更常见)。可用 start timestamp 更安全地标记。

9.5.9 Start timestamp / Exemplars

  • OpenMetrics 的 created timestamp 后改名 "start timestamp",表示 metric 最近创建或重置时间。counter histogram 也插入 synthetic zero sample(无已填充 bucket、sum/count/zero bucket 全 0);其 schema、zero 宽度、custom values 与 float/integer flavor SHOULD 与紧随其后的样本一致(避免误判 spurious counter reset);synthetic zero sample 的 counter reset 信息恒为 CounterReset。
  • Exemplar 附着在整个 histogram sample(非单个 bucket),故一个样本常带多 exemplar。scraper SHOULD 检测未变 exemplar 以省存储。检测流程(假定新 exemplar 时间戳更近):
    1. 新摄入 histogram 的 exemplar 按 timestamp→value→labels 排序;
    2. 按序追加到 exemplar 存储;
    3. 若某 exemplar 排序在最后一个成功追加者之前或相等则追加失败(可能来自上轮抓取);
    4. 排序在之后则成功。 仅当全部 exemplar 都排在最后成功追加者之前才计为乱序(混合新 exemplar 的乱序检测不到,可接受)。

9.5.10 PromQL 支持

  • 只处理 float histogram;integer histogram 取出时自动转 float。
  • Annotations:warn 级表示很可能需用户处理的问题,info 级表示可能故意但仍不寻常。
  • 兼容性(算子/函数作用于多个 histogram 时需同 schema、同 zero 宽度、同 custom values):NHCB 之间才兼容,不同 custom values 需协调(取所有原始 NHCB 共享的 custom values 为新集合,各 NHCB 合并入统一 bucket 集;可能无共享值→只剩溢出桶;此类查询加 info 级 annotation)。标准 schema 总可降到最低公共 schema(合并相邻 bucket)。不同 zero 宽度靠扩大较小的 zero bucket 并把落入的常规 bucket 并入;若最大公共宽度落在某已填充 bucket 中间则扩到与该 bucket 边界重合。不兼容阻止操作则加 warn 级 annotation。
  • Counter reset:hint MAY 被采用以避免显式检测、并正确处理常规检测不到的 reset(best effort)。与 classic 不同:sum 下降不构成 counter reset。子查询返回的 counter histogram 的 hint MUST NOT 被采用(除非引擎能确定子查询连续 histogram 在 TSDB 中也连续)。
  • Gauge vs. counter:通过 hint 可知 gauge/counter。作用于 counter 的函数仍会处理 gauge(反之亦然),但返回 warn 级 annotation,且此时须把 gauge 当 counter 显式检测 reset。
  • 桶内插值:classic 用线性插值(假设桶内均匀);实际可能偏差大,最大误差为一个桶宽。NHCB 用与 classic 相同的线性插值(主用途是 drop-in 替换 classic),特例:custom values 全正 → 最低桶下界视为 0(建议含 0 值以免此启发式);至少一个 custom value ≤ 0 → 最低桶下界视为 -Inf,但插值时最低桶所有观测视为等于该桶上界;溢出桶观测视为等于最后一个 custom value;无 custom values 的 NHCB 所有观测视为 0。标准指数 schema 用指数外推(假定桶二分后两半 population 平均相近),更契合密度随值增大而降低的分布;zero bucket 内仍用线性插值,且若所有已填充常规桶为正则假设 zero bucket 内观测也为正(在 0 与 zero bucket 上界间插值),全负则镜像。
  • Mixed series:counter/gauge 混用不阻止操作但加 warn;float 与 histogram 混用较多问题,许多 range vector 函数会移除混合元素并加 warn。
  • 一元负号:对 native histogram 取反(bucket、count、sum 全部反号),counter reset hint 恒设 GaugeType;负 histogram 仅作中间结果、恒视为 gauge、不能作 recording rule 结果(否则报错)、不能用于任何交换格式。
  • 二元算子:多数不支持(histogram↔histogram、histogram↔float、histogram↔scalar 的不可能组合会移除元素并加 info 级 annotation——类似标签匹配,sample type 作用类比标签)。
算子 行为
+ / - 两个兼容 histogram,逐桶及 count/sum 相加;缺失桶视为空;结果 flavor/hint 规则:同 hint 则保留、否则 UnknownCounterReset;减法结果恒标 gauge;hint 直接矛盾 CounterReset 与 NotCounterReset 触发 warn
* float/scalar × histogram,乘所有桶及 count/sum;乘负值结果恒 gauge
/ histogram ÷ float/scalar,等价乘倒数;除以 0 得到无常规桶、zero population 与 count/sum 依输入正/负/零而设为 +Inf/-Inf/NaN 的 histogram
== / != 比较 schema、custom values、zero threshold、所有 bucket、sum/count;flavor 无关
逻辑/集合算子(and/or/unless) 正常
trim 算子 </(trim upper)、>/(trim lower) 专为 native 引入,左侧须 histogram、右侧 float/scalar,移除大于/小于阈值的观测
  • trim 算子细节:移除仅在阈值与桶边界重合时精确,否则用插值(NHCB 有特例:最低桶下界 -Inf→桶内视为 -Inf;最高桶上界 +Inf→视为 +Inf;单桶 [-Inf,+Inf] 时仍视为 0)。移除后 sum 由剩余桶估计:NHCB 最低桶上界若为负或 0 取该上界、溢出桶取下界、标准 schema 负溢出桶 -Inf、正溢出桶 +Inf、其余用算术平均(NHCB 与标准 schema zero bucket,注意前述特例),标准指数桶其余用几何平均;被部分移除的桶用算子阈值替换相应边界。此估计不精确(即使阈值与边界重合),仅宜用于分位数估计/过滤(sum 无关);trim 算子是 histogram_fraction 的可行替代(如需计数而非分数),如 histogram_count(h >/ 0.2 </ 0.5) 优于 histogram_fraction(0.2,0.5,h)*histogram_count(h),且空 histogram 返回 0 而非 NaN。trim 保持 counter/gauge flavor。
  • 聚合算子:group、count、count_values(用 Go FloatHistogram.String 文本)、limitk、limit_ratio 同 float/histogram 一致工作;sum(同 +)、avg(同 + 后除以个数,同 /),二者会移除需混合 float 与 histogram 聚合的元素并加 warn;sum/avg 应仅用于 gauge(允许 counter 但需谨慎,hint 规则:全同 hint 则保留、含 gauge 则为 gauge、否则 UnknownCounterReset,直接矛盾加 warn);其他聚合算子不支持,忽略 histogram 并加 info 级 annotation。
  • 函数:
    • range vector 函数 delta()(gauge)、increase()(counter)、rate()(counter)、idelta()(gauge)、irate()(counter)——逐桶(含 zero bucket)及 sum/count 应用对应 float 操作生成新 histogram;flavor 不符则加 warn;delta/increase/rate 对 range 内混 float 与 histogram 的 series 无结果,idelta/irate 对最后两个样本混合的 series 无结果(均加 warn);均返回 gauge。counter 类函数在第 1、2 样本间有 counter reset 时不对第 1 样本做 schema 协调(静默忽略)。为避免外推低于 0,用与 float counter 相同启发式但仅基于 count,故个别桶仍可能外推低于 0。classic histogram 对每桶及 count/sum 独立应用启发式(因是分开的 series),会造成不一致;NHCB 不重现此问题,故 classic 与其等价 NHCB 的 rate()/increase() 结果可能略有差异。
    • avg_over_time()/sum_over_time() 对应聚合算子(range 内混 float/histogram 则整条结果从输出向量移除并加 warn)。changes()/resets() 与 float 相同(float↔histogram 算一次 change/reset;counter↔gauge flavor 变化对 changes() 不算 change、对 resets() 算 reset)。
    • histogram_quantile() 是唯一特殊对待 le 标签的函数;对 native histogram 类似工作但无 le 特殊作用。示例:classic histogram_quantile(0.9, sum by (job, le) (rate(http_request_duration_seconds_bucket[10m])));native histogram_quantile(0.9, sum by (job) (rate(http_request_duration_seconds[10m])))。首参用 1/0 可估 max/min 观测,标准 schema 因全 float64 范围保持同分辨率而结果更有用(classic 常落在 +Inf 桶)。NaN 观测被 histogram_quantile 视为 +Inf(结果落在既有桶内则按 +Inf 计算并加 info annotation;落在所有桶之上则返回 NaN 并加另一 info annotation)。
    • 专为 native 引入的函数:histogram_avg()、histogram_count()、histogram_sum()、histogram_fraction()、histogram_stddev()、histogram_stdvar();均静默忽略 float 输入、返回 float 向量。histogram_count/histogram_sum 不可用于 range selector,推荐先 rate histogram 再取其 count/sum(如 histogram_count(rate(http_request_duration_seconds[10m])));对 histogram_sum() 结果做子查询时特殊 counter reset 检测不生效,负观测可能致假 reset。histogram_avg() 返回观测算术均值(区别于对多个 histogram 用 avg 聚合——后者返回"平均后的 histogram")。histogram_stddev()/histogram_stdvar() 估计标准差/方差,桶内观测取桶边界均值(zero bucket 与 custom bucket 用算术均值、标准指数桶用几何均值)。histogram_fraction(lower, upper, histogram) 返回两边界间观测分数,误差取决于分辨率与边界对齐度;±Inf 为合法边界;NaN 观测恒视为边界之外;边界含/不含仅在与桶边界精确对齐时有意义。
    • 不直接处理样本值的函数对 native 与 float 相同:absent()、absent_over_time()、count_over_time()、info()、label_join()、label_replace()、last_over_time()、present_over_time()、sort_by_label()、sort_by_label_desc()、timestamp()。其余未提及函数不支持(静默忽略 histogram);deriv()、double_exponential_smoothing()、predict_linear() 及未提及的 <aggregation>_over_time() 会从 range vector 移除 histogram sample(range 内混用则加 info annotation)。
  • Recording rules MAY 产出 native histogram 值,按正常摄入写回(含 gauge/counter 与 hint;否则新发起检测);TSDB MAY 把 recording rule 产生的 float histogram 转为 integer(若可精确表示)。Alerting rules 正常但 RECOMMENDED 避免把 native histogram 作为告警输出值(模板中渲染为 FloatHistogram.String 简易文本,难读)。
  • Testing framework:promtool 单测与规则单测支持 native histogram;load_with_nhcb 把 classic 转 NHCB 并同时加载 classic float 与 NHCB series;expect 关键字可断言 info/warn annotation。
  • Optimizations:解码与深拷贝 histogram 昂贵;Prometheus 现尽量免复制(TODO:仍需正规 CoW)并跳过仅需 sum/count 场景的 bucket 解码。

9.5.11 Query API / UI / 模板 / 远程读写 / OTLP

  • Instant/Range query:vector 结果新增与 value 同级的 histogram key(互斥;值为二元数组,第二元素为 histogram 对象)。matrix 新增与 values 同级的 histograms key(不互斥;同一时间戳只能有一个样本—float 或 histogram)。命名历史原因(本应叫 float/histogram、floats/histograms)。histogram 对象:{"count":"...","sum":"...","buckets":[[<boundary_rule>,"<left>","<right>","<count>"],...]};每个 bucket(含 zero bucket)显式表示边界与计数,不含 spans 与 schema,结构不依赖 schema。<boundary_rule>:0=open left(左开右闭)、1=open right(左闭右开)、2=open both、3=closed both。标准 schema:正 bucket open left、负 bucket open right、zero bucket(左负右正)closed both;NHCB 全为 open left(镜像 classic)。
  • Metadata:series endpoint 同 float 处理(不告知 sample type;native 摄入为原名 request_duration_seconds,classic 为 ..._sum/..._count/..._bucket;两者都摄入则全部返回)。targets/metadata 与 metadata 作用于原始名字,classic 与 native 都表示为 request_duration_seconds(type 均为 histogram),无法仅凭 metadata 区分 native/classic,需再查 series。
  • UI:Table view 用柱状图 + 文本(每柱一个 bucket,x 轴由上下界定,柱面积 ∝ population)。可切换指数/线性 x 轴(默认指数,适配标准 schema);指数轴上常规桶等宽(y 轴可显示真实 population,zero bucket 例外——技术宽度无限但按常规桶等宽渲染)。线性轴下 y 显示 population/宽度。Graph view 计划用 heatmap(TODO:尚未实现,现只画观测之和;issue #11268)。
  • Template expansion:native histogram 可渲染为受开/闭区间数学记法启发的文本(Go FloatHistogram.String);桶多、边界小数位多时难读,慎用。示例:{count:3493.3, sum:2.349209324e+06, [-22.62741699796952,-16):1000, ...}。
  • Remote-write/read:protobuf 扩展,不支持 native 的接收方会忽略新字段;发送需 send_native_histograms=true。remote-write v2 中 native histogram 是 stable。SHOULD 在抓取阶段把 classic 转 NHCB,避免 remote-write 传输 classic 的一致性问题;OTel 显式 histogram 也 SHOULD 在 OTLP 摄入时转 NHCB。TODO:多个 exemplar 分属不同 remote-write 请求的处理。
  • Federation:native 联邦需 protobuf;OpenMetrics 文本待支持后用文本亦可但 protobuf 更高效。NHCB 经联邦端暴露时渲染为 classic float histogram,scraper 可选转回 NHCB 或作为 classic 摄入(后者可能命名冲突)。注意 OpenMetrics v1 不支持 classic float histogram,但 Prometheus 联邦不用 OM v1。
  • OTLP:内置 receiver 用前述兼容性把 OTel exponential histogram 转 native(scale>8 降到 8;< -4 则摄入失败)。显式 OTel histogram 等价 classic,默认转 classic,可选直接转 NHCB。
  • Pushgateway:逐步支持,v1.9 完整支持;基于 classic protobuf 内部模型,改动主要是 UI。混合 histogram(classic + native bucket)可推送并原样暴露;查询 API 只能返回一种 bucket,优先 native。
  • promtool:query ... 支持(含新增 query analyze 分析 classic/native 使用模式);test rules 支持;tsdb analyze/tsdb list 正常(前者 --extended 有 histogram chunk 专节);tsdb dump 用 FloatHistogram.String 文本;tsdb create-blocks-from rules 支持;promql ... 全支持;tsdb bench write 暂不计划支持;依赖 OpenMetrics 文本格式的命令不支持 native:check metrics、push metrics、tsdb dump-openmetrics、tsdb create-blocks-from openmetrics。
  • prom2json:v1.4 起支持;若 exposition 中 histogram 至少含一个 bucket span,则用 native bucket 替换 JSON 中常规 classic bucket(格式借鉴 query API)。

9.5.12 迁移注意事项(Migration considerations)

三大问题来源:

  1. query 方式不同(多数改动小,但有棘手边界,难以可靠自动转换);
  2. classic 与 native 不能相互聚合(过渡点导致 dashboard 难做、跨过渡点的 range vector 必然不完整);
  3. classic 可把桶边界精确放在关注点,native 标准 schema 虽分辨率高但不能任意设边界(此场景用户体验可能更差)。
  • 应对 (3):可不迁移该 histogram;或保持埋点不变、摄入时把 classic 转 NHCB(利用存储优势,但仍需等同处理 (1)(2))。
  • 保守做法:允许较长过渡期(并行采集存储 classic 与 native)。步骤:
    1. 先让埋点并行暴露 classic 与 native(若计划仅靠 NHCB 转换可跳过);
    2. 配置同时抓取(必要时启用 classic→NHCB 转换);
    3. 现有 classic 查询继续可用,逐步迁移 dashboard/alert/recording rule(注意长 range vector 如 histogram_quantile(0.9, rate(rpc_duration_seconds[1d])),需已采集 ≥1d 才用;并行期足够长可免写复杂切换);
    4. 确认迁移完毕后再改为只抓 native(也可用 relabel 增量移除 classic);
    5. 最后从埋点移除 classic。
  • Grafana Mimir 文档有同理念的详细迁移指南。

引自:Native Histograms

9.6 OpenMetrics 2.0 迁移指南(Client Libraries)

定位:讲从 OpenMetrics(OM)1.0 到 2.0 中,影响 client library(即 exposer)实现的变更;章节按可增量实施排序(从版本协商开始,再到指标类型、语法、元数据)。

免责:本文可能有错;任何与规范冲突处,以规范文档为准。NOTE:OM 2.0 目前实验态(RC),细节可能变;进度见 OpenMetrics issue #276。

判定口径:Breaking = 1.0 中合法的一行在 2.0 中变为非法;Non-breaking = 新增语法或放宽限制,不使既有合法行失效。

9.6.1 Quick Reference 变更表(1.0 → 2.0 → 是否 Breaking)

类别 1.0 → 2.0 Breaking
Negotiation Content-Type 版本头 version=1.0.0 → version=2.0.0 No
Negotiation 协商默认仍为最旧版本(默认 1.0) No
Naming MetricFamily 名必须与 Metric 名匹配(1.0 隐式去后缀 → 2.0 精确匹配) Yes
Naming Counter _total 后缀 MUST → SHOULD No
Naming 保留后缀(1.0 未规定 → 2.0 _count/_sum/_bucket 等 SHOULD NOT) No
UTF-8 Names metric/label 名 [a-zA-Z0-9_:] only → 允许 UTF-8、必要时加引号 No
Start Timestamps 独立 _created 样本 → 样本行内联 st@ Yes
CompositeValues Summary/Histogram/GaugeHistogram 的展开 _bucket/_count/_sum 多行 → 单个 {key:value} CompositeValue Yes
CompositeValues Sum 与 Count 由可选变为 CompositeValue 中必需 Yes
Native Histograms 1.0 N/A → 2.0 通过 schema/spans/buckets 等指数分桶 No
Exemplars W3C trace context 键(trace_id、span_id)推荐 No
Exemplars 时间戳由可选/最多 1 个 → 强制时间戳 + 允许多个 Mixed
Exemplars 大小限制从 128 字符硬限、bucket 级放置 → 软限制、W3C 键、sample 级放置 Mixed
Unknown Type Number only → 可用 Number 或 CompositeValue No

9.6.2 版本协商与 Content-Type

  • Non-breaking:OM 1.0 的 Content-Type 仍合法。
  • OM 1.0:application/openmetrics-text; version=1.0.0; charset=utf-8;OM 2.0:把 version=2.0.0。
  • 协商默认:无指定版本时必须默认最旧版本(1.0.0),用标准 HTTP content-type 协商;若 scraper 未请求 2.0.0 就回 1.0.0。即 exposer 默认继续提供 1.0,仅在 consumer 要求时切 2.0。
  • Protobuf format removed:OM 2.0 不再规定官方 protobuf 格式;1.0 的 protobuf 可继续支持,但 2.0 不含新的/更新的 protobuf 格式。Prometheus protobuf wire format 仍重要且维护(见 exposition formats 文档)。

9.6.3 命名变更(Naming Changes)

  • OM 2.0 收紧 MetricFamily 名与 Metric 名的关系,并改 counter/info 的后缀规则。1.0 的 parser 会隐式剥离已知后缀以把样本名映射回 MetricFamily;2.0 取消该隐式剥离,MetricFamily 名必须与每个 Metric 的 Name 精确一致。总体上强制的 unit 与 _total 后缀现为推荐但非必需。
  • 术语变更(Non-breaking,不影响暴露格式):数据模型层级由 MetricSet → MetricFamily → Metric → MetricPoint → Sample(1.0)改为 MetricSet → MetricFamily → Metric → Sample(2.0,Sample 携带 Number 或 CompositeValue):MetricPoint 被移除、Sample 升为一等对象;复杂类型(除 StateSet)改用 CompositeValue 而非多个 MetricPoint。StateSet 下:原 Metric → 现 MetricGroup;原 MetricPoint → 现 Metric。
  • MetricFamily 名必须匹配 Metric 名(Breaking):TYPE/HELP/UNIT 注释里的 MetricFamily 名必须与 Metric 行上的名一致。
# OM 1.0:注释用基名、样本带 _total
# TYPE http_requests counter
# HELP http_requests Total HTTP requests.
http_requests_total 1027

# OM 2.0:注释名也带 _total
# TYPE http_requests_total counter
# HELP http_requests_total Total HTTP requests.
http_requests_total 1027

这是最常见需要改的点:把 TYPE/HELP 元数据名补上原先只在样本上的后缀。

  • Counter _total(Non-breaking):1.0 counter 名必须以 _total 结尾;2.0 变为应该(不再强制)。Tip:改为 should 主要为兼容 OpenTelemetry(其对 counter 名格式无要求)。
# 1.0
# TYPE http_requests counter
http_requests_total 1027

# 合法 2.0
# TYPE http_requests counter
http_requests 1027
  • 保留后缀(Non-breaking):MetricFamily 名 should not 以 _count、_sum、_bucket、_gcount、_gsum 结尾。原因:较老的 ingester 会把 histogram/summary 展开为带这些后缀的经典样本,若你另有 foo_bucket 的 gauge 会与 foo 直方图展开的 foo_bucket 冲突。因为是 should not(非 must not),既有指标仍能解析,但改名可避免碰撞 bug。
  • 实践中命名(完整 OM 2.0 暴露示例):
# TYPE http_requests_total counter
# HELP http_requests_total Total HTTP requests received.
http_requests_total{method="GET",code="200"} 1027
http_requests_total{method="POST",code="201"} 53
# TYPE http_request_duration_seconds histogram
# HELP http_request_duration_seconds Duration of HTTP requests in seconds.
http_request_duration_seconds{method="GET"} {count:1027,sum:172.5,bucket:[0.1:800,0.5:950,+Inf:1027]}
# TYPE build_info info
# HELP build_info Build metadata.
build_info{version="1.4.2",branch="main",goversion="go1.22"} 1
# EOF
  • http_requests_total(counter):TYPE 名与样本名精确一致,保留 _total 提升可读性与向后兼容;
  • http_request_duration_seconds(histogram):MetricFamily 名避开保留后缀;用 CompositeValue 单行表达全部桶;
  • build_info(info):TYPE 名与样本名一致,_info 后缀按要求出现在 MetricFamily 名上。

9.6.4 UTF-8 名称

  • Non-breaking:1.0 传统名语法仍合法。2.0 允许 metric/label 名含超出传统 [a-zA-Z0-9_:] 的 UTF-8 字符,主要为 OpenTelemetry bridge 场景(如 process.cpu.seconds 点分命名),与放宽的 _total 规则配合更好。注意:并非所有 Prometheus 生态工具都支持 UTF-8 指标名。
  • Metric name 加引号:不匹配 ^[a-zA-Z_:][a-zA-Z0-9_:]*$ 的 metric 名必须用双引号包裹(任何名都可加引号,但仅超出传统字符集时必需)。引号内用 \\ 表示反斜杠、\" 表示双引号、\n 表示换行。
# TYPE "process.cpu.seconds" counter
# HELP "process.cpu.seconds" Total user and system CPU time spent in seconds.
{"process.cpu.seconds"} 4.20072246e+06
  • Label name 加引号:不匹配 ^[a-zA-Z_][a-zA-Z0-9_]*$ 的 label 名必须用双引号;规则同上。
{"process.cpu.seconds","node.name"="my_node"} 4.20072246e+06
  • 替代花括号语法:需要加引号的 metric 名要移到花括号内作为第一个元素(即 metric 名必须在花括号内,不能在外);ABNF 中为 name-and-labels-in-braces 产生式。完整示例:
# TYPE "process.cpu.seconds" counter
# UNIT "process.cpu.seconds" seconds
# HELP "process.cpu.seconds" Total user and system CPU time spent in seconds.
{"process.cpu.seconds","node.name"="my_node"} 4.20072246e+06

9.6.5 起始时间戳(Start Timestamps)

  • Breaking:_created 样本不再用于提供 Start Time。
  • OM 2.0 用样本行上的**内联 Start Timestamp(st@)**取代独立的 _created 样本,减小暴露体积并避免 _created 样本与值样本之间的竞态。Counter、histogram、summary 具有创建语义并支持 start timestamp;gauge 不支持。
  • OM 2.0 样本行字段顺序:
metric_name value [timestamp] [st@start_timestamp] [# exemplar...]
  • Counter 对比:1.0 用 http_requests_total 1027 + http_requests_created 1000000000 两行;2.0 用 # TYPE http_requests_total counter + http_requests_total 1027 st@1000000000.000(_created 样本变为内联 st@)。
  • Histogram 对比:1.0 是 _bucket{le=...} 多行 + _sum/_count/_created;2.0 为 http_request_duration_seconds {count:1027,sum:172.5,bucket:[0.1:800,0.5:950,+Inf:1027]} st@1000000000.000(_created 样本消失,st@ 出现在该 CompositeValue 行的值之后)。
  • 实践示例(含标签、时间戳、start timestamp、exemplar 的完整字段顺序):
# TYPE http_requests_total counter
http_requests_total{method="GET",code="200"} 1027 1710000000.123 st@1000000000.000 # {trace_id="abc123",span_id="def456"} 1.0 1709999999.456
http_requests_total{method="POST",code="201"} 53 1710000000.123 st@1000000000.000
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds{method="GET"} {count:1027,sum:172.5,bucket:[0.1:800,0.5:950,+Inf:1027]} 1710000000.123 st@1000000000.000
# EOF

9.6.6 CompositeValues

  • Breaking:传统多行复杂类型不再允许(StateSet 除外)。
  • OM 1.0 中复杂类型(histogram、summary、gaugehistogram)表示为多条展开样本行(每桶一行、count 一行、sum 一行);OM 2.0 合并为单行,其值为 CompositeValue —— 一个结构化 {key:value,...} 块。三种类型都必须用 CompositeValue 语法。
  • 语法概要:花括号包裹、逗号分隔 key:value、内部无空白;key 为固定字面名,value 依字段为 number/integer/方括号列表。

完整 key 表:

key 格式 适用类型 含义
count number histogram、summary 观察计数
sum number histogram、summary 观察值之和
gcount number gaugehistogram gauge 观察计数
gsum number gaugehistogram gauge 观察值之和
quantile [q:v,...] summary 按分位数排序的 quantile/value 对
bucket [le:v,...,+Inf:v] histogram、gaugehistogram 经典 经典桶上界/计数对,+Inf 必需
schema integer native hist 原生直方图分辨 schema
zero_threshold realnumber native 零桶宽度
zero_count number native 零桶内观察数
negative_spans [off:len,...] native 负桶 span 定义
negative_buckets [v,...] native 负桶计数
positive_spans [off:len,...] native 正桶 span 定义
positive_buckets [v,...] native 正桶计数

各类型必需 key:

类型 必需 key
Summary count,sum,quantile
经典 histogram count,sum,bucket
经典 gaugehistogram gcount,gsum,bucket
native histogram count,sum,schema,zero_threshold,zero_count 加可选正/负 spans+buckets
native gaugehistogram gcount,gsum,schema,zero_threshold,zero_count 加可选
combined(经典+native) count/gcount、sum/gsum、native 字段、最后 bucket

含全部 key 的抽象示例(combined):

{count:<n>,sum:<n>,schema:<i>,zero_threshold:<r>,zero_count:<n>,negative_spans:[<off>:<len>,...],negative_buckets:[<n>,...],positive_spans:[<off>:<len>,...],positive_buckets:[<n>,...],bucket:[<le>:<n>,...,+Inf:<n>]}

其中 <n> number、<i> integer、<r> real、<off> 整数偏移、<len> 正整数长度、<le> 实数桶边界;字段必须按所示顺序出现,花括号内不得有任何空白。

  • Summary:2.0 中 Summary Sample 必须含 Count、Sum 与一组 quantile(1.0 中三者均为 may);此前省略 count/sum 的 exposer 必须补上。1.0 五行 → 2.0 一行:http_request_duration_seconds_summary {count:1027,sum:172.5,quantile:[0.5:0.013,0.9:0.025,0.99:0.10]};quantile 映射移入块内;无观察时用 quantile:[]。
  • Histogram:所有 _bucket/_sum/_count 行并为单个 CompositeValue:http_request_duration_seconds {count:1027,sum:172.5,bucket:[0.1:800,0.5:950,+Inf:1027]};bucket 列表用 threshold:count 对并含 +Inf;字段顺序必须为 count,sum,bucket;_sum/_count 后缀彻底消失。
  • GaugeHistogram:度量当前分布(非 reset 型),如队列深度、在途请求大小;值具 gauge 语义,可升可降无需 reset。1.0 展开格式用 _gcount/_gsum 后缀;2.0 用 CompositeValue 的 gcount/gsum 字段(保留 gauge 语义前缀)。示例:queue_depth_bytes {gcount:23,gsum:1048576,bucket:[1024:5,65536:18,+Inf:23]};字段顺序必须为 gcount,gsum,bucket;GaugeHistogram 没有 Start Timestamp(无创建语义)。
  • 实践示例(全类型同现):
# TYPE http_requests_total counter
http_requests_total{method="GET",code="200"} 1027 1710000000 st@1000000000
# TYPE http_request_duration_seconds_summary summary
http_request_duration_seconds_summary{path="/api/v1"} {count:1027,sum:172.5,quantile:[0.5:0.013,0.9:0.025,0.99:0.10]} 1710000000 st@1000000000
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds{path="/api/v1"} {count:1027,sum:172.5,bucket:[0.1:800,0.5:950,+Inf:1027]} 1710000000 st@1000000000
# TYPE queue_depth_bytes gaugehistogram
queue_depth_bytes{queue="work"} {gcount:23,gsum:1048576,bucket:[1024:5,65536:18,+Inf:23]} 1710000000
# EOF
  • counter 行用普通 Number(非 CompositeValue);summary 与 histogram 用 CompositeValue;gaugehistogram 无 Start Timestamp。
  • Sum 与 Count 必需(Breaking):2.0 中 histogram 与 gaugehistogram 的 Sum、Count 不再允许省略(1.0 可选)。histogram 例:1.0 只有 bucket 行 + _created → 2.0 http_request_duration_seconds {count:1027,sum:172.5,bucket:[...]} st@1000000000;gaugehistogram 例:1.0 只有 bucket 行 → 2.0 queue_depth_bytes {gcount:23,gsum:1048576,bucket:[1024:5,65536:18,+Inf:23]}。

9.6.7 Native Histograms(迁移视角)

  • Non-breaking:OM 1.0 完全不支持 native histogram。2.0 中取代"插桩时选定的固定桶边界",用指数分桶 schema在全部取值域自动提供分辨率,无需配置桶;schema 字段控制桶宽粒度,值越大桶越窄(越细)。字段沿用 CompositeValue 的 {key:value,...} 格式。
  • Native-only histogram:CompositeValue 按顺序含 count(观察总数)、sum(观察值之和)、schema(整数 -4 到 8,控制指数桶宽,越大越细)、zero_threshold(非负浮点,定义零桶边界 [-threshold, +threshold])、zero_count(零桶观察数)、positive_spans、positive_buckets。
  • Spans 是 offset:length 对,描述哪些指数桶被填充:首个 span 的 offset 是起始桶下标(可为负),length 是从该下标起连续填充的桶数;其后每个 span 的 offset 是跳过的空桶数;所有 span length 之和等于 bucket 列表中的值总数。负向观察用 negative_spans/negative_buckets,语法相同。若无任何桶被填充,spans 与 buckets 字段可整体省略。
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds {count:59,sum:120.0,schema:3,zero_threshold:1e-4,zero_count:2,positive_spans:[0:3,2:2],positive_buckets:[10,15,12,8,12]} 1710000000 st@1000000000

两个 span:首个从桶下标 0 起 3 个连续桶,跳过 2 个空桶,再 2 个连续桶;桶总数 3+2=5,与 positive_buckets 的 5 个值吻合。

  • Classic 与 Native 合并:部分 consumer 尚不支持 native 桶;combined CompositeValue 在单行同时给出两种表示以向后兼容。字段顺序为 count、sum、全部 native 字段(schema 到 positive_buckets)、最后经典 bucket —— 这样偏好 native 的 parser 可在经典桶列表前停止读取。
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds {count:59,sum:120.0,schema:3,zero_threshold:1e-4,zero_count:2,positive_spans:[0:3,2:2],positive_buckets:[10,15,12,8,12],bucket:[0.01:5,0.1:25,1.0:47,10.0:57,+Inf:59]} st@1000000000
  • GaugeHistogram with Native Buckets:语法同 histogram,但用 gcount/gsum 取代 count/sum;TYPE 行也作区分。
  • 实践示例(native-only 与 combined 同现):
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds{method="GET"} {count:59,sum:120.0,schema:3,zero_threshold:1e-4,zero_count:2,positive_spans:[0:3,2:2],positive_buckets:[10,15,12,8,12]} 1710000000 st@1000000000
http_request_duration_seconds{method="POST"} {count:34,sum:68.5,schema:3,zero_threshold:1e-4,zero_count:1,positive_spans:[0:2,3:1],positive_buckets:[8,12,13],bucket:[0.01:3,0.1:14,1.0:28,10.0:33,+Inf:34]} 1710000000 st@1000000000
# EOF
  • GET 行用 native-only 字段;POST 行同时含 native 与经典桶字段,兼顾尚未支持 native 的 consumer。

9.6.8 Exemplars(2.0 变更)

  • 强制时间戳(Breaking):1.0 exemplar 时间戳可选(may);2.0 每个 exemplar 必须含时间戳——这对不预期值后有时间戳的 1.0 parser 即为破坏性变更。
http_requests_total 1027 1710000000 # {trace_id="abc123",span_id="def456"} 1.0 1709999999

(# 之后为标签、值 1.0、强制时间戳 1709999999。)

  • 多个 Exemplars(Non-breaking,单个仍允许):1.0 每 sample 最多 1 个 exemplar;2.0 允许单 sample 多个,每个以 # 起,各自带标签集、值与时间戳。
http_requests_total 1027 1710000000 # {trace_id="abc123",span_id="def456"} 1.0 1709999999 # {trace_id="xyz789",span_id="uvw012"} 1.0 1709999800
  • W3C Trace Context Keys:2.0 推荐用 trace_id 与 span_id 作为 exemplar 标签键(遵循 W3C Trace Context);键名一致可让 consumer 无需逐 exporter 配置即可把指标与分布式 trace 关联。
  • Size Limit Relaxation:1.0 中 exemplar 标签集有 128 字符硬上限;2.0 移除硬上限,改为软性指导(exposer 不应发出过大的 exemplar 标签集),把限制负担转移给 scraper。
  • Histogram Exemplar Placement:1.0 中 histogram exemplar 出现在各 _bucket 行上;2.0 用 CompositeValue 后,exemplar 挂在 CompositeValue 之后的单一样本行上。无标签的 exemplar 必须用空 LabelSet {};在同一 Sample 上挂多个 exemplar 时,各 exemplar 应使用一致的标签名。

9.6.9 Unknown Type

Non-breaking:1.0 中 Unknown 类型 Metric 的 Sample 只能是 Number 值;2.0 中也可为 CompositeValue。因 Unknown 一般不该用(为类型不确定的第三方指标而存在),这是次要变更,主要影响需处理任意 Unknown 数据的 ingester 与库。

9.6.10 需修改的代码点(归纳)

  1. Content-Type 版本:支持 version=2.0.0,且在未请求 2.0 时默认回 1.0.0(标准 content negotiation);
  2. TYPE/HELP/UNIT 元数据名:补上原先只在样本上的后缀(如 _total),使 MetricFamily 名与 Metric 名精确一致(最常见改动);
  3. 停止 _created 样本:改为在样本行内联 st@;gauge/gaugehistogram 不加 start timestamp;
  4. 复杂类型输出重写:summary/histogram/gaugehistogram 从多行展开改为单行 CompositeValue,并必须含 count/sum(gaugehistogram 为 gcount/gsum),字段顺序严格;无空白;
  5. Exemplar 输出:所有 exemplar 必须带时间戳;支持单 sample 多个 exemplar;推荐 trace_id/span_id;histogram exemplar 移到 sample 行;无标签时用 {};
  6. UTF-8 名支持:不匹配传统正则的 metric/label 名必须用双引号,且加引号的 metric 名要放进花括号首位;
  7. 命名清理:避免(should not)以 _count/_sum/_bucket/_gcount/_gsum 结尾的 MetricFamily 名;counter 可去 _total(可选);
  8. Unknown 类型:允许其 Sample 使用 CompositeValue。

引自:OpenMetrics 2.0 Migration Guide for Client Libraries

第 10 章 参与社区

本章面向"想给 Prometheus 本身(而非自己的应用)写代码/写文档"的读者。制度来源为官方 Contributing 指南;Prometheus 社区其他仓库可能有各自指南与规则,仓库特定信息见各仓库的 README.md 与 CONTRIBUTING.md。

阅读路径建议:新人先看"如何挑选要做的任务";有经验的贡献者看"如何让 PR 被合并";已有较复杂具体计划的看"proposal process"。

10.1 贡献流程

10.1.1 PR 合并流程(面向有经验的贡献者)

  1. 本地用 make test 测试改动并尽量补充测试;
  2. 为避免 CI lint 失败,运行 make common-format 与 make lint 并修复问题;
  3. 用 git commit -s 提交以签署 DCO;
  4. 一般对默认分支提 PR(通常是 main,有时 master),评审者会协助处理例外;
  5. 评审者应被自动分配,团队力求及时响应但可能因其他优先事项延迟;
  6. 检查失败的 CI job,全部应通过;
  7. 评审者可能要求改动,快速处理可缩短 PR 周期。

更多 GitHub 流程见 "GitHub guidelines"(见 10.4 节)。

10.1.2 沟通渠道

面向用户的常规社区渠道用于讨论 Prometheus 使用(含用 Prometheus 代码做非 Prometheus 开发,如用插桩库埋点);Prometheus 组件本身的开发走以下渠道:

渠道 定位与注意事项
GitHub 贡献在 GitHub pull request 中评审;GitHub issue 常用于讨论具体 bug 与 feature request;非正式或全局性讨论更适合其他渠道
CNCF Slack 大量非正式聊天发生在 CNCF Slack,主开发频道为 #prometheus-dev,还有大量专门频道(如 #prometheus-...-dev、#prometheus-protobuf-dev)。注意 Slack 是信息孤岛:内容不被外部搜索引擎索引、难以导出归档、只读也需登录,因此视为临时且公众不可达,内容不会永久保留。重要信息(如讨论结果)应通过其他渠道(GitHub 或邮件列表)发布;避免在别处直接链接 Slack 消息而不摘要其内容
开发者邮件列表 prometheus-developers(Google Groups,另有 mail-archive 镜像),适合公告与更正式的全局话题讨论;其归档被搜索引擎索引,便于查找过往讨论
开发者峰会(Developer summits) 讨论更复杂开发话题的公开会议;日程、会议记录等见 "Developer summit details"
工作组(Work groups) 开发者为某主题定期开线上会则成立工作组,会议公开并经 Prometheus 日历发布

10.1.3 如何挑选任务

对你重要、你已是用户或专家、或你想成为专家的方向最佳。社区两种标识方式:

  • good-first-issue label:适合新贡献者的起点;
  • triage/accepted label:更复杂问题(已分诊、信息齐全、可开工);
  • triage/needs-triage 与 triage/needs-information:表示尚无人看或信息不足——不要做这两个 label 的 issue。

10.1.4 提案流程(Proposal process)

较大的改动与想法需要在 Proposal 仓库(github.com/prometheus/proposals)做正式提案与评审,详见已接受的 "proposal proposal"(proposals/0001-proposal-process.md)。

10.1.5 GitHub guidelines

  • commit message 应描述本次提交的改动;对提交粒度无强意见,按合理逻辑组织,好 commit 便于评审从而更快合并(例如一个 commit 加测试暴露 bug、另一个 commit 修复)。
  • commit message 必须包含 Signed-off-by: <author identity> 行,作者以此同意 https://developercertificate.org/ 的条款(针对该次贡献)。
  • 有了想提的改动后,推到个人 fork 并对默认分支开 PR;默认分支通常是 main,个别仓库为 master。某些情况需对 release 分支提 PR(如 release-3.5),最常见是新版本 release candidate 的修复或 LTS 版本修复;不确定就问。
  • 所需评审者会自动添加;其他应评审或想评审的人可在评论中用用户名提及,但请勿随意 @ 社区成员。每个 PR 都跑检查,必须全部通过才能合并,失败检查由 PR 作者排查解决。
  • 评审中常追加 fixup commit;评审者可 squash 成连贯提交,但须确保最终 commit message 有意义、非自动生成;也可要求作者合并前整理提交。一般允许 rebase 分支;一旦他人基于尚未进入 main 的提交做了改动,作者就不应再改写这些提交。PR 过大或混入不相关关注点(如重构与逻辑改动)时应拆分为多个 PR。

10.1.6 AI 生成贡献

Prometheus 作者不反对用 AI 工具生成代码,但要求:

  • 每个 commit 有 DCO,作者据此证明贡献(整体或部分)由本人创作且有权提交;若基于既有工作,则须处于合适开源许可下且作者有权在修改后提交(详见 linuxfoundation.org/legal/generative-ai)。
  • 人类作者必须完全理解所提交代码,提交前应结合 DCO 仔细审查 AI 生成代码。
  • 鼓励显式披露 AI 使用,例如在相应 commit 加 Assisted-by: <name of AI tool used>。
  • 关于 issue 与 PR 的讨论,强烈倾向人与人对话。

引自:Contributing

10.2 DCO(Developer Certificate of Origin)

  • 提交方式:git commit -s,会在 commit message 末尾自动追加 Signed-off-by: <author identity>。
  • 语义:作者以此同意 https://developercertificate.org/ 的条款(针对该次贡献)。
  • 每个 commit 必须有该行;AI 生成代码同样适用——DCO 是"我对这段代码负责"的签署,不理解代码就无法履行该责任。
  • 建议配合 Assisted-by: 行显式披露 AI 工具使用(见 10.1.6)。

💡 补充:DCO 签署的常见踩坑是"忘了 -s 而 PR 被 CI 拦下"。补救方式是 git commit --amend -s(改最后一个 commit)或 git rebase --signoff <base>(批量补签),然后 git push --force-with-lease 更新 PR——注意不要在他人已基于你的提交工作后再改写历史(见 10.1.5)。

10.3 开发环境搭建

Prometheus 主体用 Go 编写,开发环境的核心是"能跑 make 目标"。

10.3.1 必备命令

命令 用途
make test 运行测试,本地验证改动
make common-format 通用格式化,避免 CI lint 失败
make lint 运行 linter 并修复问题
make format 格式化
make style 风格检查/修正

项目有大量 linter,务必用上述目标正确格式化,不要手工调整风格。

10.3.2 编码风格

  • 使用该语言惯用代码;遵循周边既有风格,不把风格改进与代码改动混在一起;
  • 使用正确英语语法与标点,避免不必要缩写:
    • BAD:// batchQueue full, try again later
    • GOOD:// The batchQueue is full, so we need to try again later.
  • Markdown 中除行尾为 URL 外,行宽限制 80 字符,便于评审评论。

10.3.3 Go 风格指南

Go 是 Prometheus 及生态的主要语言,参考 https://go.dev/wiki/CodeReviewComments。Prometheus 额外规则:

  • 命名导入常放单独 block,并按 stdlib / 其他仓库 / 本仓库 分组;
  • 导出类型的 doc comment 不被 linter 强制(误报太多)但团队在意,应在合理处使用;
  • 长函数签名按下述方式折行——首行以左括号结尾,参数按需分多行,最后以单独一行右括号开头结束:
func (s *shards) sendSamples(
	ctx context.Context, samples []prompb.TimeSeries,
	sampleCount, exemplarCount, histogramCount int,
	pBuf *proto.Buffer, buf compression.EncodeBuffer, compr compression.Type,
) error {
  • 反例(错误写法):参数首行不以左括号结尾、或在最后一参行末尾写右括号。

10.3.4 开发者峰会细节

  • 通常每月最后一个周四线上举行;另有在开发者集中某地(如 PromCon、Kubecon EU)时的全天线下峰会。
  • 线上会面向所有人开放,线下会可能因后勤原因有限制,不确定就问;线下会尽力以 best-effort 方式让线上参与者接入。
  • 议题由团队根据近期讨论拟定,可在会议记录顶部提出,或在峰会前至少 24 小时发到开发者邮件列表。
  • 会议记录:维护滚动记录文档(当前版本自 2024-09-13 起);历史记录含 2017、2018、2019(含第二次)、2020 虚拟峰会 1–5、2021 虚拟峰会 1、2021-2024 滚动记录等。
  • Facilitator:为帮助团队有效运行峰会而设的轮值角色(每次会议轮换),职责分为会前、会中、会后:
阶段 职责与建议
会前 帮助确定议程与议题,确保高票议题相关方能参会。建议:会前两三天在公开社区渠道提醒大家添加 Agenda Topics,并请 Prometheus 团队成员与 maintainer 对议题投票;会前一天联系得票最多的 "Topic owners" 确认能到场
会中 确保会议顺利进行并在需要时达成共识。建议:准时开始,使用 @prometheus.io 账号获取 admin 会议权限;开始录制并说明适用 Code of Conduct;根据投票与在场人员选择讨论议题;记笔记或找志愿者在共享文档记笔记;讨论停滞或偏离议题时策略性介入;需要时发起共识征询
会后 最后一项任务是为下届峰会找新 Facilitator,方式是在 Prometheus Team 邮件列表发邮件

10.4 代码审查与文档贡献

10.4.1 代码审查

  • 评审者由系统自动分配;如认为某人应评审,可在 PR 评论中提及,但请勿随意 @ 社区成员。
  • CI 检查必须全部通过才能合并;失败由 PR 作者修复。
  • 评审中常出现 fixup commit:
    • 评审者可 squash 成连贯提交,但最终 commit message 必须有意义(非自动生成);
    • 也可要求作者合并前自行整理提交;
    • 一般允许 rebase;但他人已基于尚未进入 main 的提交工作后,不应再改写。
  • PR 过大或混入不相关关注点时应拆分。

10.4.2 文档贡献

  • 本手册大量引用的官网文档,其源文件位于 prometheus/docs 仓库(docs/instrumenting/exporters.md 等)。
  • 把新导出器加入"可用导出器列表"的正确做法:给邮件列表发邮件宣布,并提交一个 PR 编辑 https://github.com/prometheus/docs/blob/main/docs/instrumenting/exporters.md(见 6.1.13)。
  • Markdown 行宽限制 80 字符(除行尾 URL),便于评审逐行评论。
  • 文档与代码同源:docs/ 下的规范文件(如 docs/specs/om/*.md、docs/specs/prw/*.md)就是本手册第 9 章的原始权威来源,任何与规范冲突的描述以规范文档为准。

10.4.3 公告节奏小结(以导出器为例)

步骤 动作
1 在开发过程中申请端口号(Default port allocations wiki,宣布前先占)
2 完成导出器并自测(遵循 6.3 节规范)
3 给 development mailing list 发邮件公告
4 提 PR 把它加入 /docs/instrumenting/exporters.md
5 接受社区反馈并持续维护

引自:Contributing


全篇覆盖清单(第 4 篇 · 完整 10 章)

下表列出 第 4 篇覆盖的全部官网页面 URL 及其归属章节。第 1–4 章(Alertmanager)由同篇上半部分撰写,此处一并列出以保证第 4 篇的完整性可核对;第 5–10 章为本下半部分撰写内容。

# 官网页面 URL 归属章节 状态
1 https://prometheus.io/docs/alerting/latest/overview/ 第1章 AM 架构与概念 ✅
2 https://prometheus.io/docs/alerting/latest/alertmanager/ 第1章 AM 架构与概念 ✅
3 https://prometheus.io/docs/alerting/ 第1章(导航页) ✅
4 https://prometheus.io/docs/alerting/latest/configuration/ 第2章 AM 配置全解 ✅
5 https://prometheus.io/docs/alerting/latest/notifications/ 第3章 通知路由与模板 ✅
6 https://prometheus.io/docs/alerting/latest/notification_examples/ 第3章 通知路由与模板 ✅
7 https://prometheus.io/docs/alerting/latest/integrations/ 第3章 通知路由与模板 ✅
8 https://prometheus.io/docs/alerting/latest/high_availability/ 第4章 高可用与 API ✅
9 https://prometheus.io/docs/alerting/latest/https/ 第4章 高可用与 API ✅
10 https://prometheus.io/docs/alerting/latest/alerts_api/ 第4章 高可用与 API ✅
11 https://prometheus.io/docs/alerting/latest/management_api/ 第4章 高可用与 API ✅
12 https://prometheus.io/docs/instrumenting/clientlibs/ 第5章 插桩与客户端库 ✅
13 https://prometheus.io/docs/instrumenting/exposition_formats/ 第5章 插桩与客户端库 ✅
14 https://prometheus.io/docs/instrumenting/content_negotiation/ 第5章 插桩与客户端库 ✅
15 https://prometheus.io/docs/instrumenting/escaping_schemes/ 第5章 插桩与客户端库 ✅
16 https://prometheus.io/docs/instrumenting/pushing/ 第5章 插桩与客户端库 ✅
17 https://prometheus.io/docs/instrumenting/ 第5章(导航页) ✅
18 https://prometheus.io/docs/practices/pushing/ 第5章·Pushgateway 适用性 ✅
19 https://prometheus.io/docs/instrumenting/exporters/ 第6章 导出器与集成生态 ✅
20 https://prometheus.io/docs/instrumenting/writing_exporters/ 第6章 导出器与集成生态 ✅
21 https://prometheus.io/docs/instrumenting/writing_clientlibs/ 第6章 导出器与集成生态 ✅
22 https://prometheus.io/docs/operating/integrations/ 第6章 导出器与集成生态 ✅
23 https://prometheus.io/docs/operating/ 第6章(导航页) ✅
24 https://prometheus.io/docs/guides/node-exporter/ 第6章·node_exporter 部署实践 ✅
25 https://prometheus.io/docs/guides/cadvisor/ 第6章·cAdvisor 实践 ✅
26 https://prometheus.io/docs/guides/multi-target-exporter/ 第6章·multi-target exporter 模式 ✅
27 https://prometheus.io/docs/guides/go-application/ 第7章 应用集成实战 ✅
28 https://prometheus.io/docs/tutorials/instrumenting_http_server_in_go/ 第7章 应用集成实战 ✅
29 https://prometheus.io/docs/guides/opentelemetry/ 第7章 应用集成实战 ✅
30 https://prometheus.io/docs/visualization/browser/ 第8章 可视化 ✅
31 https://prometheus.io/docs/visualization/grafana/ 第8章 可视化 ✅
32 https://prometheus.io/docs/visualization/perses/ 第8章 可视化 ✅
33 https://prometheus.io/docs/visualization/consoles/ 第8章 可视化 ✅
34 https://prometheus.io/docs/visualization/ 第8章(导航页) ✅
35 https://prometheus.io/docs/practices/consoles/ 第8章·console 实践 ✅
36 https://prometheus.io/docs/tutorials/visualizing_metrics_using_grafana/ 第8章·可视化实战 ✅
37 https://prometheus.io/docs/specs/native_histograms/ 第9章 规范·Native Histograms ✅
38 https://prometheus.io/docs/specs/om/open_metrics_spec/ 第9章 规范·OpenMetrics 1.0 ✅
39 https://prometheus.io/docs/specs/om/open_metrics_spec_2_0/ 第9章 规范·OpenMetrics 2.0 ✅
40 https://prometheus.io/docs/specs/prw/remote_write_spec/ 第9章 规范·Remote Write 1.0 ✅
41 https://prometheus.io/docs/specs/prw/remote_write_spec_2_0/ 第9章 规范·Remote Write 2.0 ✅
42 https://prometheus.io/docs/guides/open_metrics_2_0_migration/ 第9章 规范·迁移指南 ✅
43 https://prometheus.io/docs/specs/ 第9章 规范(导航页) ✅
44 https://prometheus.io/docs/specs/om/ 第9章(导航页) ✅
45 https://prometheus.io/docs/specs/prw/ 第9章(导航页) ✅
46 https://prometheus.io/docs/guides/contributing/ 第10章 参与社区 ✅
47 https://prometheus.io/docs/guides/ 附录·站点结构(导航页) ✅
48 https://prometheus.io/docs/prometheus/ 附录·站点结构(导航页) ✅

覆盖统计:第 4 篇共覆盖 48 个官网页面,其中:

  • 第 1–4 章(Alertmanager):11 个;
  • 第 5 章(插桩与客户端库):7 个(含 1 个导航页,另含 practices/pushing 补充);
  • 第 6 章(导出器与集成生态):8 个(含 1 个导航页);
  • 第 7 章(应用集成实战):3 个;
  • 第 8 章(可视化):7 个(含 1 个导航页);
  • 第 9 章(规范):9 个(含 3 个导航页);
  • 第 10 章(参与社区):1 个;
  • 附录·站点结构:2 个导航页。

导航页说明:标注"(导航页)"的 URL 为官网纯目录/索引页,正文为空(仅含 frontmatter),已在对应章节中作结构说明收录,不计入实质知识条目。