在上篇的Prometheus远程存储方案中,我们解决了数据持久化和大数据量存储/查询的需求。但是,在大规模的监控环境中,除了存储方面的问题外,Prometheus实例在处理大量的监控任务时,也可能会成为性能瓶颈。另外,还有高可用的问题,应该如何来保证监控的可靠性。

本篇我们将就这些问题进行探讨研究。

一. Prometheus高可用

在Prometheus的官方推荐方案中,对于高可用的处理是通过部署两套Prometheus,配置同样的任务实例来实现的。在这个方案中,两套Prometheus会获取相同的监控指标,并且触发同样的告警规则,而对于警报的去重工作则由Alertmanager来负责。


Prometheus监控运维实战十七: 高可用与扩展性_devops


但此方案也存在着缺点,比如当某个Prometheus出现故障或中断时,那么该节点将会出现数据丢失的情况,并与另一个节点存在数据差异。当在该节点上进行查询操作时,就会遇到这个问题。

对此,我们可以与远程存储方案结合起来,将Prometheus的读写放到远程存储端,通过高可用 +远程存储的方式来解决上面的问题。

Prometheus监控运维实战十七: 高可用与扩展性_prometheus_02

此优化方案在解决了Promthues服务可用性的基础上,同时确保了数据的持久化,当某个Promthues 节点发生宕机的情况时,由于还有另一个节点在获取数据,这样可以保证在查询时不会遇到数据丢失的问题。 

该方案适用于用户监控规模不大,但是希望能够将监控数据持久化,同时确保Promthues服务高可用性的场景。


二. 联邦模式

在大规模的监控环境中,当单个Prometheus无法处理大量的监控采集任务时,我们可以基于联邦的模式将采集任务划分到不同的Prometheus实例中,再由顶层的Prometheus进行数据的统一管理。


Prometheus监控运维实战十七: 高可用与扩展性_prometheus_03


在此方案中,工作节点的Prometheus根据拆分原则,负责指定实例的数据采集及规则告警工作,而主节点则通过/federate接口从工作节点获取数据指标,并写入到远程存储中,同时对接Grafana实现监控展示。

1. 任务拆分

在任务的拆分上,我们可以从两个维度来考虑,一种是按功能进行拆分,一种则是水平的拆分。


功能拆分

功能拆分可按资源类型或区域等维度,将不同的任务划分到不同的Prometheus节点上,如下图所示

Prometheus监控运维实战十七: 高可用与扩展性_云原生_04


这种方式简单且易于理解,在此架构中每个Prometheus都有明确的目标实例且配置独立,当监控的实例指标出现故障时,用户可以清楚定位到相应的Prometheus节点。通过功能拆分的方式,可以有效分散Prometheus的负载压力,提升监控系统的整体容量规模。

不过在某些场景下,通常是大规模的部署环境中,功能拆分依然会出现瓶颈。例如:在一个存在着上万台主机的环境中,要是通过单个Prometheus去监控主机参数的话,无疑是难以支持的。而如果要靠人为来划分多个Prometheus监控对应的主机节点,则会给运维人员增加很多工作量和复杂性。在这种场景下,我们可以考虑水平拆分的方式。


水平拆分

在水平拆分的场景下,多个Prometheus节点配置相同的实例信息,Prometheus通过HASH规则的方式,计算自身负责的目标实例,从而实现横向的扩展。

Prometheus监控运维实战十七: 高可用与扩展性_云原生_05

配置示例:


scrape_configs:
- job_name: 'node'
file_sd_configs:
- files:
- /etc/prometheus/node/*.yml
relabel_configs:
- source_labels: [__address__]
modulus: 2
target_label: __tmp_hash
action: hashmod
- source_labels: [__tmp_hash]
regex: ^0$
action: keep


注释:本示例中我们负责抓取监控指标的工作节点为两台,所以在modulus处标记数量为2,并根据每个实例的[__address__]标签值 ,生成一个临时标签[__tmp_hash]及对应的值,该值内容包含0或1。然后根据正则匹配机制,保留符合正则表达则的实例。在两个工作节点中,regex处分别配置为^0$和^1$。如果后续工作节点的数量增加,则需要修改modulus值为新的节点数量 ,并在新的节点中配置对应的regex。


在每个Worker节点的配置中,可以插入一个worker:<value>的标签,这样方便在主节点处可以知道相关指标来源于哪个worker。


global:
external_labels:
worker: 0



2. 主节点配置

在worker配置完成正常运行后,我们可以开始配置主节点的任务,用于抓取工作节点的时间序列。集群中的工作节点都是我们Targets的目标地址,本示例为两个worker节点,相关配置如下 :


- scrape_config:
- job_name: node-workers
honor_labels: true
metrics_path: /federate
params:
match[]:
- '{job="node"}'
static_configs:
- targets:
- worker-0:9090
- worker-1:9090



注释 :honor_labels需配置为true,这样可保证主节点不会覆盖工作节点的序列标签;metrics_path指定federate API路径;params指定match[]参数,匹配我们想要获取的特定时间序列,可以指定多个条件,Prometheus将返回所有条件的并集;targets指定工作节点的目标地址。


如下所示,在任务正常启动后,我们就可以在主节点的上查询到Worker节点收集的监控指标。

Prometheus监控运维实战十七: 高可用与扩展性_监控_06


Prometheus监控运维实战十七: 高可用与扩展性_监控_07



结语:

目前Prometheus在集群与扩展性方面的功能并不算强大,通过分级联邦的方式虽然可以解决扩展性的问题,但依然存着的一些不足之处 。例如:多层结构使得Prometheus之间的网络变得复杂,我们不止要关注工作节点和目标之间的连接,也要关注主节点与工作节点的连接;工作节点根据设定的间隔获取目标指标,而主节点对于工作节点数据的抓取也存在着时间间隔,这可能导致主节点出现数据延迟的情况;最后,当所有的指标汇总到主节点时,可能会对其造成较大的压力,在资源的调配上需做好分配,以免引起主节点的崩溃。

基于以上的短板,在采用分级联邦时,建议尽量遵循下列的规范:

1. 将告警的规则配置在worker节点,而不是主节点。

2. 在主节点抓取worker节点指标时,通过match参数筛选过滤指标。

3. 整体分层架构最多不要超过三层。

4. 将分级联邦模式做为最后的选择,只有在大规模的环境中才考虑使用。