本篇是《Prometheus 从入门到生产实践》第 21 / 24 篇。配置以文中版本与环境前提为准,实验和故障均为教学场景。完整程序、配置及校验范围见配套实验包。

高可用要先回答“哪种故障发生后,哪项能力还能继续”。两个进程都在运行,不代表历史查询、规则求值、通知与数据备份都实现了高可用。本篇把 Prometheus 双副本和 Alertmanager 集群分开设计,再连接起来验证。
1. 双副本不是同一个 TSDB 的两个写入进程
常见模式是两台 Prometheus 独立抓取同一批目标、各自存储和求值。任意一台失效,另一台仍可继续采集和发出告警;代价是目标收到两次抓取,存储和计算资源也接近两份。
两台之间并不自动复制本地历史,也没有内建 leader 让另一台只待机。若它们看到的网络、目标或采样时刻不同,短窗口查询也可能出现合理差异。将一个查询负载均衡放在前面,可以改善入口可用性,但不能自动合并两份缺失的历史。
因此,每个副本要有独立数据目录,尽量分布在不同主机或故障域。两份程序放在同一台机器上,只适合验证进程级故障,不足以证明宿主机级高可用。
2. 外部标签标明数据来自哪一组副本
下面是副本 A 的独立配置,B 的文件只把 replica 改为 b;配套文件为 c21-prom-a.yml、c21-prom-b.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
monitor_cluster: lab
replica: a
rule_files:
- c21-ha.rules.yml
alerting:
alert_relabel_configs:
- action: labeldrop
regex: replica
alertmanagers:
- static_configs:
- targets: ['127.0.0.1:19093', '127.0.0.1:29093']
scrape_configs:
- job_name: demo
static_configs:
- targets: ['127.0.0.1:19100']
global.external_labels 常用于向外部系统传输时标识监控来源;它们不是把所有本地样本都原地增加相同标签的机制。本机查询中没有看到 replica,并不能据此认定配置失效。
monitor_cluster 在这里区分不同监控集群,replica 区分同一组里的副本。外部标签与 relabel 的阶段见 Prometheus 配置文档。应用自己暴露的同名标签也可能影响最终身份,因此需要建立统一命名约定。
3. 发送给 Alertmanager 的告警身份必须一致
两份相同业务告警若分别带 replica=a、replica=b,完整标签集合就不同,Alertmanager 会将它们视为不同告警实例。示例使用 alert_relabel_configs 在发送阶段移除 replica,但保留 monitor_cluster 和业务维度。
不能为了去重把 cluster、environment、instance 一股脑删掉,否则不同环境或不同故障对象也可能合并。副本还需要一致的规则、目标标签和相关配置,仅移除 replica 无法修复其他标签差异。
配套规则如下,故障持续一分钟用于缩短实验,不是建议所有业务采用这一时长:
groups:
- name: ha-lab
rules:
- alert: DemoTargetUnavailable
expr: up{job="demo"} == 0
for: 1m
labels:
severity: warning
annotations:
summary: 'demo 抓取失败:{{ $labels.instance }}'
告警内容不同但标签相同,也可能造成难以理解的覆盖和通知行为。把变化的测量值写进 annotations,将识别对象的稳定字段留在 labels。
4. Prometheus 应把告警发给所有 Alertmanager 成员
Alertmanager 集群成员通过 gossip 协调静默和通知日志等状态,但不是靠一个共享数据库同步所有内存告警。每个成员应独立收到告警,所以示例明确列出两个地址,而不是只通过一个随机转发的负载均衡地址发送。
成员之间的集群通信端口与对外 Web/API 端口也不同。只放通 Web 端口,可能出现“两个界面都能打开,但集群实际没有连通”的情况。
这套机制优先避免漏报。网络分区时可能出现重复通知,不能对使用者承诺 exactly-once;接收端仍要能关联事件并妥善处理重复。机制见 Alertmanager High Availability。
5. 在同机隔离端口做机制实验
先把 c21-alertmanager.yml 中的 example.test 地址替换为自己授权的测试接收器,按接收器要求补充认证。原占位地址不会发送成功:
route:
receiver: lab-webhook
group_by: ['alertname', 'monitor_cluster', 'job']
group_wait: 15s
group_interval: 1m
repeat_interval: 1h
receivers:
- name: lab-webhook
webhook_configs:
- url: https://hooks.example.test/alerts
send_resolved: true
在不同终端启动两个 Alertmanager,各用独立存储路径:
alertmanager --config.file=c21-alertmanager.yml \
--storage.path=./data/am-a \
--web.listen-address=127.0.0.1:19093 \
--cluster.listen-address=127.0.0.1:19094 \
--cluster.peer=127.0.0.1:29094
alertmanager --config.file=c21-alertmanager.yml \
--storage.path=./data/am-b \
--web.listen-address=127.0.0.1:29093 \
--cluster.listen-address=127.0.0.1:29094 \
--cluster.peer=127.0.0.1:19094
再启动两份 Prometheus,分别指定 c21-prom-a.yml、c21-prom-b.yml,存储目录分别为 data/prom-a、data/prom-b,Web 端口分别为 19090、29090。启动前检查端口冲突,不要与前面尚在运行的实验实例共用目录。
6. 按故障矩阵验收,而不是只看绿色图标
先保持 exporter 正常,确认两边 up=1。停掉专用实验 exporter,记录两边 pending、firing 和接收端事件;再依次停止一个 Prometheus、一个 Alertmanager,观察剩余链路能否继续提供预期能力。
恢复组件后,检查目标、规则加载和集群成员状态。再在受控环境模拟成员通信中断,明确记录可能重复通知的边界;不要通过破坏真实网络策略来做随手演练。
如果把两份样本写入远端存储,保留或移除 replica 又是另一项设计。通常需要按后端支持的 HA tracker 或查询去重配置处理,不能直接删掉标签让两份写入撞成同一序列。
7. 两条看起来相同的告警,怎样比较身份
重复通知排查时,先导出两边发送后的最终标签集合,逐项比较 alertname、monitor_cluster、job、instance、environment 和业务对象标识。不要只看 summary,因为两份摘要相同并不意味着标签相同。反过来,不同描述若共享同一完整身份,也可能被当成同一事件处理。
常见差异包括两个副本抓取了不同的地址格式,一边是主机名、另一边是 IP;一份服务发现额外带入 pod UID;某条规则把当前数值放入 label,每次求值就产生新身份。它们需要分别修正发现、规则或标签约定,而不该继续扩大 labeldrop 范围。
随后检查接收器本身:通知请求成功是否代表真正展示给人,超时后是否可能已经被接收再重试,事件平台如何识别重复消息。即使监控端尽量去重,接收端仍可能在网络边界遭遇不确定结果。生产验收要把允许的重复、人工处理方式和升级路径写清楚。
最后区分独立故障域与共同依赖。如果两台 Prometheus 共用一个 DNS、一个电源域或一份被误发布的配置,它们仍可能同时失效。高可用演练除了逐个停进程,还要审查共同依赖,并用独立的监控链路观察这组监控系统。分区实验中收到重复通知是需要解释的结果,不应为了追求零重复而牺牲关键告警的送达机会。
最终应形成一张能力表:单 Prometheus 故障、单通知节点故障、主机故障、网络分区时,采集、查询、告警、通知各剩下什么。这样高可用才是经过解释和演练的承诺。
上一篇:高基数与性能治理:限制、查询成本和容量实验 · 系列目录 · 下一篇:Remote Write 与长期存储:队列、兼容与恢复预算