一、概述
Prometheus 几乎已成为监控领域的事实标准,它自带高效的时序数据库存储TSDB,可以让单台 Prometheus 能够高效的处理大量的数据,还有友好并且强大的 PromQL 语法,可以用来灵活的查询各种监控数据以及配置告警规则,同时它的 pull 模型指标采集方式被广泛采纳,非常多的应用都实现了 Prometheus 的 metrics 接口以暴露自身各项数据指标让 Prometheus 去采集,很多没有适配的应用也会有第三方 exporter 帮它去适配 Prometheus,所以监控系统我们通常首选用 Prometheus,所以我们的监控系统的方案也都围绕promtheus展开。
二、Prometheus痛点及解决方案
Prometheus存在单点故障,具体痛点可以这么描述
Prometheus本身只支持单机部署,也不支持高可用及水平扩容。其单机存储和抓取能力都有上限,容易单点故障。
虽然有一种方式是通过部署N个Prometheus分别抓取不同的target来分摊压力的,但是grafana就要为不同的图表配置不同的Prometheus地址(数据源),而且不同数据源之间不能聚合查询,监控页面也看不到全局的视图,造成查询混乱,复杂程度比较棘手。
Thanos+Prometheus可以解决这个问题,它提供了一个核心能力
1、thanos query组件可以反向代理到N个Prometheus,然后grafana直接指向thanos query即可,thanos query会从N个Prometheus同时查询数据,返回满足PromQL的数据结果。这样Prometheus挂了一台也不影响服务,数据在其他Prometheus上还有,很好的解决了单点故障问题。
2、如果N个Prometheus抓取的数据存在重复的,那么thanos query会根据label自动去重,确保返回给grafana的数据不会重复,这个特性很关键。
三、具体架构实现
1、thanos sidecar + thanos query
实际上,thanos query不能直接反向代理prometheus,而是需要在prometheus的POD内部署一个thanos sidecar组件,thanos query反向代理至sidecar,再由sidecar查询POD内的prometheus。
query和sidecar之间走GRPC协议,POD内的sidecar和prometheus则走原生HTTP接口拿数据。
为了高可用,我们可以部署多个prometheus同时抓取。
为了打散采集/存储压力,我们可以部署多个prometheus各自抓取部分数据。
无论出于上述哪种目标或者混合目标,最终依靠Query的正确配置都可以提供统一的PromQL查询入口给Grafana,这一点是毋庸置疑的。
核心组件
- Store Gateway:将云存储中的数据内容暴露出来,提供云存储到Query中转接口
- Query:实现了Prometheus API,汇聚底层组件的数据(如sidecar、store),并实现与监控面板对接(如grafana)
- Compactor:将云存储中的数据进行压缩和降采样
这套方案的工作原理如下:
Prometheus单机存储空间有限,SideCar和Prometheus在同POD内共享数据volume,然后sidecar可以自动将prometheus磁盘数据上传到Bucket里归档存储;后续Query可以通过Store组件从Bucket拉取到历史数据,这样就实现了历史数据的永久存储和查询能力。
Compactor则是对Bucket里的历史数据进行采样或者清理,有具体需求后再自行研究即可。
上述组件均是可选的,没有历史数据永存需求的可以直接忽略。
2、thanos receive + thanos query
thanos提供的另一种方案是计算与存储分离,利用thanos receive组件部署一个分布式存储集群,然后令prometheus通过remote write机制直接写入到thanos receive集群完成分布式存储,这样prometheus本身就无状态了。
再利用thanos query反向代理到thanos receive集群,最终对外提供统一的PromQL查询入口,这个阶段就没有prometheus什么事了。
核心组件
- Receive:从Prometheus的remote-write wal(Prometheus远程预写式日志)获取数据,暴露出去或者上传到云存储
这套方案的工作原理如下:
thanos receive组件需要部署N个进程组成集群,prometheus可以remote write写入数据到任意receive节点,收到写入请求的receive节点根据metrics label做哈希后计算出负责存储该部分数据的receive节点,并将数据转发过去;
同时,receive可以配置replica数量,这样的话receive会根据哈希结果将数据复制给多个receive做冗余,应该说是非常简单粗暴的复制方案吧,因为监控场景没那么严谨所以基本可用即可,查询去重和归拢则全部依靠querier组件反向查询所有Receive组件搞定。