1 前言
在高可用prometheus:问题集锦文章中有简单提到 Prometheus 的高可用方案,尝试了联邦、Remote Write 之后,我们最终选择了 Thanos 作为监控配套组件,利用其全局视图来管理我们的多地域、20+集群的监控数据。本文主要介绍 Thanos 的一些组件使用和心得体会。
Prometheus官方的高可用有几种方案:
- HA:即两套 Prometheus 采集完全一样的数据,外边挂负载均衡
- HA + 远程存储:除了基础的多副本 Prometheus,还通过 Remote write 写入到远程存储,解决存储持久化问题
- 联邦集群:即 Federation,按照功能进行分区,不同的 Shard 采集不同的数据,由 Global 节点来统一存放,解决监控数据规模的问题。
使用官方建议的多副本 + 联邦仍然会遇到一些问题,本质原因是 Prometheus 的本地存储没有数据同步能力,要在保证可用性的前提下再保持数据一致性是比较困难的,基本的多副本 Proxy 满足不了要求,比如:
- Prometheus 集群的后端有 A 和 B 两个实例,A 和 B 之间没有数据同步。A 宕机一段时间,丢失了一部分数据,如果负载均衡正常轮询,请求打到A 上时,数据就会异常。
- 如果 A 和 B 的启动时间不同,时钟不同,那么采集同样的数据时间戳也不同,就多副本的数据不相同
- 就算用了远程存储,A 和 B 不能推送到同一个 TSDB,如果每人推送自己的 TSDB,数据查询走哪边就是问题
- 官方建议数据做 Shard,然后通过 Federation 来实现高可用,但是边缘节点和 Global 节点依然是单点,需要自行决定是否每一层都要使用双节点重复采集进行保活。也就是仍然会有单机瓶颈。
- 另外部分敏感报警尽量不要通过 Global 节点触发,毕竟从 Shard 节点到 Global 节点传输链路的稳定性会影响数据到达的效率,进而导致报警实效降低。
目前大多数的 Prometheus 的集群方案是在存储、查询两个角度上保证数据的一致:
- 存储角度:如果使用 Remote Write 远程存储, A 和 B 后面可以都加一个 Adapter,Adapter 做选主逻辑,只有一份数据能推送到 TSDB,这样可以保证一个异常,另一个也能推送成功,数据不丢,同时远程存储只有一份,是共享数据。方案可以参考这篇文章
- 存储角度:仍然使用 Remote Write 远程存储,但是 A 和 B 分别写入 TSDB1 和 TSDB2 两个时序数据库,利用 Sync 的方式在 TSDB1 和 TSDB2 之间做数据同步,保证数据是全量的。
- 查询角度:上边的方案需要自己实现,有侵入性且有一定风险,因此大多数开源方案是在查询层面做文章,比如 Thanos 或者 Victoriametrics,仍然是两份数据,但是查询时做数据去重和 join。只是 Thanos是通过 Sidecar 把数据放在对象存储,Victoriametrics是把数据 Remote Write 到自己的 Server 实例,但查询层 Thanos Query 和 Victor的 Promxy的逻辑基本一致,都是为全局视图服务
1.1 实际需求
随着我们的集群规模越来越大,监控数据的种类和数量也越来越多:如Master/Node 机器监控、进程监控、4 大核心组件的性能监控,POD 资源监控、kube-stats-metrics、K8S events监控、插件监控等等。除了解决上面的高可用问题,我们还希望基于 Prometheus 构建全局视图,主要需求有:
- 长期存储:1 个月左右的数据存储,每天可能新增几十G,希望存储的维护成本足够小,有容灾和迁移。考虑过使用 Influxdb,但 Influxdb 没有现成的集群方案,且需要人力维护。最好是存放在云上的 TSDB 或者对象存储、文件存储上。
- 无限拓展:我们有300+集群,几千节点,上万个服务,单机 Prometheus 无法满足,且为了隔离性,最好按功能做 Shard,如 Master 组件性能监控与 POD 资源等业务监控分开、主机监控与日志监控也分开。或者按租户、业务类型分开(实时业务、离线业务)。
- 全局视图:按类型分开之后,虽然数据分散了,但监控视图需要整合在一起,一个 Grafana 里 n 个面板就可以看到所有地域+集群 + POD 的监控数据,操作更方便,不用多个 Grafana 切来切去,或者 Grafana 中多个 Datasource 切来切去。
- 无侵入性:不要对已有的 Prometheus 做过多的修改,因为 Prometheus 是开源项目,版本也在快速迭代,我们最早使用过 1.x,可1.x 和 2.x的版本升级也就不到一年时间,2.x 的存储结构查询速度等都有了明显提升,1.x 已经没人使用了。因此我们需要跟着社区走,及时迭代新版本。因此不能对 Prometheus 本身代码做修改,最好做封装,对最上层用户透明。
在调研了大量的开源方案(Cortex/Thanos/Victoria/StackDriver..)和商业产品之后,我们选择了 Thanos,准确的说 Thanos 只是监控套件,与原生 Prometheus 结合,满足了长期存储 + 无限拓展 + 全局视图 + 无侵入性的需求。
2 什么是 Thanos
Thanos 是什么,是打了响指灭了一半人类的灭霸吗?不是,在监控领域,Thanos 是 Prometheus 的高可用解决方案,由英国游戏技术公司 Improbable 开源
Thanos 官网主页上简单易懂一段英文介绍如下:Open source, highly available Prometheus setup with long term storage capabilities。开源,高可用性的 Prometheus 设置,并提供长期存储能力。这是对 Thanos 最准确的描述。
Thanos 以简单且高成本效益的方式,为 Prometheus 提供一个集群,目的是要实现大规模的监控。Thanos目前的核心维护者来自 Polar Signals、红帽和 字节跳动,已经被字节跳动、腾讯、华为、VIVO、eBay 等公司用于生产环境。
Thanos 很早就进入了 CNCF 的沙盒阶段,在 2020.08.19 通过 TOC 的审核进入 CNCF 的孵化阶段,目前已经稳定发布一年多了,在不久的将来也许就毕业了。
Thanos 维护者 Frederic Branczyk 提到,Thanos 是一种易于安装的解决方案,可以将用户的 Prometheus 执行个体过渡到具有长期储存功能的监控系统。大多数的 Thanos 都部署在 Kubernetes 上,可用来监控跨多集群与多云的微服务或是基础设施。
也就是说 Thanos 所有的数据采集都是基于 Prometheus 的基础上实现
Thanos是一个“开源、高可用、具有长期存储能力的 Prometheus 设置”。Thanos 被许多知名公司使用。它也是CNCF 孵化项目的一部分。
Thanos 的主要功能之一是允许“无限”存储。怎么会这样 ?通过使用几乎每个云提供商都提供的对象存储(例如 S3)。如果在本地运行,可以使用rook或minio等解决方案提供对象存储。
2.1 hanos 特点:
Thanos 在首页就大方的放出了自己的 4 个优点,分别是:
- 全局查询
- 无限制的存储
- 兼容 Prometheus
- 数据降准和压缩 其实 Thanos 还有第 5 个优点,那就是高可用集群。
全局查询
Thanos 可以跨多个 Prometheus 实例来查询存储的监控指标,这样就实现了 Prometheus 的水平扩展,当单机 Prometheus 性能不足时,通过水平拆分就可以完成扩展 Prometheus 的承载能力。
无限制存储
Thanos 本身不提供存储,但是它支持多种对象存储系统,使用对象存储来扩容数据存储,以此来无限的存储监控数据,它支持 Google 的 GCP、AWS 的 S3、Azure、Swift、Tencent 的 COS、AliYun 的 OSS。
兼容 Prometheus
Thanos 完全兼容 Prometheus 的 API 接口,无论你使用 Grafana 或者其他支持 Prometheus API 的工具,使用过程非常丝滑,一点都感觉不到在使用 Thanos ,就像在直接操作 Prometheus 一样。
数据降准和压缩
Thanos 会对 Prometheus 的数据进行降准,当查询大时间范围的监控数据或者配置复杂的保留策略时可以加快查询速度,另外也会对实时数据进行压缩,这样可以节省一定的存储空间。
高可用集群
Thanos 可以针对 Prometheus 提供高可用集群,并且对重复数据进行删除和合并。
Thanos 可以跨多个 Prometheus 实例来查询存储的监控指标,这样就实现了 Prometheus 的水平扩展,当单机 Prometheus 性能不足时,通过水平拆分就可以完成扩展 Prometheus 的承载能力。
无限制存储
Thanos 本身不提供存储,但是它支持多种对象存储系统,使用对象存储来扩容数据存储,以此来无限的存储监控数据,它支持 Google 的 GCP、AWS 的 S3、Azure、Swift、Tencent 的 COS、AliYun 的 OSS。
兼容 Prometheus
Thanos 完全兼容 Prometheus 的 API 接口,无论你使用 Grafana 或者其他支持 Prometheus API 的工具,使用过程非常丝滑,一点都感觉不到在使用 Thanos ,就像在直接操作 Prometheus 一样。
数据降准和压缩
Thanos 会对 Prometheus 的数据进行降准,当查询大时间范围的监控数据或者配置复杂的保留策略时可以加快查询速度,另外也会对实时数据进行压缩,这样可以节省一定的存储空间。
高可用集群
Thanos 可以针对 Prometheus 提供高可用集群,并且对重复数据进行删除和合并。
2.2 Thanos 组件介绍
Thanos 与 Prometheus 并肩作战。从仅 Prometheus 的设置开始并升级到 Thanos 是很常见的。
Thanos 分为几个组件,每个组件都有一个目标(每个服务都应该是:))。组件之间通过 gRPC 进行通信。
2.2.1 THANOS SIDECAR 组件
如上图 Thanos 与 Prometheus(带有 sidecar)一起运行,并且每 2 小时将 Prometheus 指标导出到对象存储。这使得 Prometheus几乎是 无状态的。Prometheus 仍在内存中保留 2 小时的指标,因此您可能仍会在中断时丢失 2 小时的指标(这个问题应该由您的 Prometheus 设置处理,使用 HA/Sharding,而不是 Thanos)。
Thanos sidecar与Prometheus Operator和Kube Prometheus Stack一起开箱即用, 并且可以轻松部署。该组件充当 Thanos Query 的存储。
2.2.2 THANOS Store Gateway组件
Thanos Store充当将查询转换为远程对象存储的网关。它还可以在本地存储上缓存一些信息。基本上,这是允许您查询对象存储以获取指标的组件。该组件充当 Thanos Query 的存储
Thanos Store 命令(也称为 StoreGateway)在对象存储桶中的历史数据之上实现 StoreAPI。它主要作为一个 API 网关,因此不需要大量的本地磁盘空间。它在启动时加入一个 Thanos 集群,并公布它可以访问的数据。它在本地磁盘上保存有关所有远程块的少量信息,并使其与 bucket 保持同步。通常,在重新启动时删除这些数据是安全的,代价是增加了启动时间。
2.2.3 THANOS COMPACTOR 组件
Thanos compactor 是一个单例(不可扩展),负责压缩和下采样存储在对象存储中的指标。下采样是随着时间的推移失去指标粒度的行为。例如,您可能希望将指标保留 2 或 3 年,但您不需要像昨天的指标那样多的数据点。这就是压缩器的用途,为您节省对象存储的字节数,从而为您节省费用。
2.2.4 THANOS QUERY 组件
Thanos Query 是 Thanos 的主要组件,它是您发送 promQL 查询的中心点。Thanos Query 公开了与 Prometheus 兼容的端点。然后它将查询分派到所有“stores”。请记住,stores可能是提供指标的任何其他 Thanos 组件。Thanos Query可以将查询发送到以下地方:
- 另一个 Thanos query (它们可以堆叠)
- Thanos store
- Thanos sidecar
如果相同的指标来自不同的 store 或 Prometheuse,Thanos Query 还负责对指标进行重复数据删除。例如,如果您有一个位于 Prometheus 和对象存储中的指标,Thanos Query 可以对指标进行重复数据删除。在 Prometheus HA 设置的情况下,重复数据删除也基于 Prometheus 副本和分片工作。
2.2.5 THANOS QUERY FRONTEND 组件
正如其名称所暗示的那样,Thanos Query Frontend 充当 Thanos Query 的前端,其目标是将大型查询拆分为多个较小的查询并缓存查询结果(在内存中或在 memcached 中)
在远程写入的情况下还有其他组件,例如 Thanos Receive,但这仍然不是本文的主题。
3 架构
Thanos 的默认模式:sidecar 方式
我们在上面的两个Prometheus的节点服务器中部署Sidercar,用于获取监控数据。同时,配置历史数据写入到对象存储中进行持久化保存。部署一个Store Gateway对接对象存储,而Compactor组件会定时对存储中数据进行压缩索引及降采样操作。
Querier做为面向用户的组件,对接Sidercar和Store Gateway获取数据并进行展示。(另外还有的Receiver和ruler组件由于使用不是很多,本文不做介绍,有需要可自行查阅。
除了 这个 sidecar 方式,Thanos 还有一种不太常用的 receive 模式,后面会提到。
看起来组件很多,但其实部署时二进制只有一个,非常方便。只是搭配不同的参数实现不同的功能,如 query 组件就是 ./thanos query,sidecar 组件就是./thanos sidecar,组件 all in one,代码只有一份,体积很小。
其实核心的 sidecar + query 就已经可以运行,其他的组件只是为了实现更多的功能
最新版 Thanos 在 这里下载release,对于 Thanos 这种仍然在修 bug、迭代功能的软件,有新版本就不要用旧的。
Thanos 的架构看起来非常复杂,但是分析以后非常简单。
Thanos 提供了 Sidecar、Query、Store、Receive、Rule、Compact、Query Frontend、tools 8 个组件,但是构建一个 Thanos 集群最少使用 4 个就可以完成一个简单的集群,最多也只要 6 个组件就可以构建非常复杂的集群。
这些组件的功能分别是:
- Sidecar: Thanos 的数据上传组件,用来和 Prometheus 通信,并且将 Prometheus 的监控数据上传到对象存储
- Query:Thanos 的查询组件,用来查询监控数据
- Store:Thanos 的数据存储组件,用来和对象存储通信,为对象存储提供数据代理服务。
- Receive:Thanos 的数据收取组件,支持 Prometheus 的远程写功能,对于同一个 Prometheus 实例,只能在 Sidecar 和 Receiver 中间二选一。
- Rule:Thanos 的集中的告警管理组件
- Compactor:Thanos 的数据处理组件,用来将监控数据降准和压缩
- Query Frontend:Thanos 的查询前端
- tools:Thanos 的运维工具,用途很多。
Thanos 看上去有这么多组件,其实部署的时候只有一个二进制包,搭配不同的参数实现不同的功能,非常方便,比如 Query 组件就是 ./thanos query
,Sidecar 组件就是 ./thanos sidecar
,所有组件在同一个包里,而且包的体积很小。
Thanos 集群有两种模式,分别是使用 Sidecar 来进行监控数据上传的边车模式和使用 Receive 来接受数据的收取模式,两种模式只有数据从 Prometheus 到对象存储的方式有区别,其他结构是一样的。
我们首先来看边车模式: 从数据发生的 Prometheus 看起,Prometheus 在获取数据以后,通过 Sidecar 将数据上传到 对象存储,Store 从对象存储读取数据供其他组件查询,Query 从 Sidecar 获取实时数据、从 Store 获取历史数据对外提供查询功能,Rule 从 Sidecar 和 Store 获取数据进行规则计算,如果触发告警就推送给 AlertManager ,Compactor 对对象存储进行读写,下载数据进行数据降准和压缩,将处理好的数据上传到对象存储,并且删除已经降准过和压缩过的数据。Query Frontend 在 Query 前边提供查询缓存和查询分解的功能。
接着我们来看收取模式: 对于收取模式,大致的结构和边车模式是一样的,只是没有了 Sidecar 组件,Prometheus 通过远程写功能将数据直接写给 Receive ,Receiver 将数据写给对象存储,Query 查询数据从 Receive 和 Store 获取,Rule 从 Receive 和 Store 获取数据进行规则计算,Store 和 Compacter 的功能不变。
3.1 sidecar 架构模式
Thanos Sidecar 组件需要和 Pormetheus 实例一起部署,用于代理 Thanos Querier 组件对本地 Prometheus 的数据读取,允许 Querier 使用通用、高效的 StoreAPI 查询 Prometheus 数据。第二是将 Prometheus 本地监控数据通过对象存储接口上传到对象存储中,这个上传是实时的,只要发现有新的监控数据保存到磁盘,会将这些监控数据上传至对象存储。
Thanos Sidecar 组件在 Prometheus 的远程读 API 之上实现了 Thanos 的 Store API,这使得 Querier 可以将 Prometheus 服务器视为时间序列数据的另一个来源,而无需直接与它的 API 进行交互。
因为 Prometheus 每 2 小时生成一个时序数据块,Thanos Sidecar 会每隔 2 小时将这个块上传到一个对象存储桶中。这样 Prometheus 服务器就可以以相对较低的存储空间运行,同时通过对象存储提供历史数据,使得监控数据具有持久性和可查询性。但是这样并不意味着 Prometheus 可以完全无状态,因为如果 Prometheus 崩溃并重新启动,我们将失去大约 2 个小时的指标数据,所以 Prometheus 在实际运行中还是需要持久性磁盘的。
3.2 Receiver 模式
Thanos Receiver 实现了 Prometheus 远程写 API,它构建在现有的 Prometheus TSDB 之上,并保持其实用性,同时通过长期存储、水平可伸缩性和下采样扩展其功能。Prometheus 实例被配置为连续地向它写入指标,然后 Thanos Receiver 默认每 2 小时将时间序列格式的监控数据块上传到一个对象存储的桶中。Thanos Receiver 同样暴露了 Store API,以便 Thanos Querier 可以实时查询接收到的指标。
这两种模式有各种的优缺点,具体使用哪种模式需要结合生产环境综合考虑。
3.3 工作流程
Thanos 是同时支持 Prometheus 读和写的远程存储方案,首先我们先看下 sidecar 模式下指标写入的整个流程:
- 首先 Prometheus 从所采集服务的 metrics 接口抓取指标数据,同时根据自身所配置的
recording rules
定期对抓取到的指标数据进行评估,将结果以 TSDB 格式分块存储到本地,每个数据块的存储时长为 2 小时,且默认禁用了压缩功能。 - 然后
sidecar
嗅探到 Prometheus 的数据存储目录生成了新的只读数据块时,会将该数据块上传到对象存储桶中做为长期历史数据保存,在上传时会将数据块中的meta.json
进行修改添加 thanos 相关的字段,如external_labels
。 rule
根据所配置的recording rules
定期地向query
发起查询获取评估所需的指标值,并将结果以 TSDB 格式分块存储到本地。每个数据块的存储时长为 2 小时,且默认禁用了压缩功能,每个数据块的meta.json
也附带了 thanos 拓展的external_lables
字段。当本地生成了新的只读数据块时,其自身会将该数据块上传到远端对象存储桶中做为长期历史数据保存。compact
定期将对象存储中地数据块进行压缩和降准采样,进行压缩时数据块中的 truck 会进行合并,对应的meta.json
中的 level 也会一同增长,每次压缩累加 1,初始值为 1。在进行降准采样时会创建新的数据块,根据采样步长从原有的数据块中抽取值存储到新的数据块中,在meta.json
中记录resolution
为采样步长。
读取指标的流程为:
- 首先客户端通过
query API
向query
发起查询,query
将请求转换成StoreAPI
发送到其他的query
、sidecar
、rule
和store
上。 sidecar
接收到来自于query
发起的查询请求后将其转换成query API
请求,发送给其绑定的 Prometheus,由 Prometheus 从本地读取数据并响应,返回短期的本地采集和评估数据。rule
接收到来自于query
发起的查询请求后直接从本地读取数据并响应,返回短期的本地评估数据。store
接收到来自于query
发起的查询请求后首先从对象存储桶中遍历数据块的meta.json
,根据其中记录的时间范围和标签先进行一次过滤。接下来从对象存储桶中读取数据块的index
和chunks
进行查询,部分查询频率较高的index
会被缓存下来,下次查询使用到时可以直接读取。最终返回长期的历史采集和评估指标。
对于发送报警的流程如下所示:
- Prometheus 根据自身配置的
alerting
规则定期地对自身采集的指标进行评估,当告警条件满足的情况下发起告警到 Alertmanager 上。 rule
根据自身配置的alerting
规则定期的向query
发起查询请求获取评估所需的指标,当告警条件满足的情况下发起告警到 Alertmanager 上。- Alertmanager 接收到来自于 Prometheus 和
rule
的告警消息后进行分组合并后发出告警通知。
3.4 特性
Thanos 相比起原生的 Prometheus 具有以下的一些优势:
- 统一查询入口——以
Querier
作为统一的查询入口,其自身实现了 Prometheus 的查询接口和StoreAPI
,可为其他的Querier
提供查询服务,在查询时会从每个 Prometheus 实例的Sidecar
和Store Gateway
获取到指标数据。 - 查询去重——每个数据块都会带有特定的集群标签,
Querier
在做查询时会去除集群标签,将指标名称和标签一致的序列根据时间排序合并。虽然指标数据来自不同的采集源,但是只会响应一份结果而不是多份重复的结果。 - 高空间利用率——每个 Prometheus 本身不存储长时间的数据,
Sidecar
会将 Prometheus 已经持久化的数据块上传到对象存储中。Compactor
会定时将远端对象存储中的长期数据进行压缩,并且根据采样时长做清理,节约存储空间。 - 高可用——
Querier
是无状态服务,天生支持水平拓展和高可用。Store
、Rule
和Sidecar
是有状态服务,在多副本部署的情况下也支持高可用,不过会产生数据冗余,需要牺牲存储空间。 - 存储长期数据——Prometheus 实例的
Sidecar
会将本地数据上传到远端对象存储中作为长期数据 - 横向拓展——当 Prometheus 的指标采集压力过大时,可以创建新的 Prometheus 实例,将
scrape job
拆分给多个 Prometheus,Querier
从多个 Prometheus 查询汇聚结果,降低单个 Prometheus 的压力 - 跨集群查询——需要合并多个集群的查询结果时,仅需要在每个集群的
Querier
之上再添加一层Querier
即可,这样的层层嵌套,可以使得集群规模无限制拓展。
滴滴