Thanos设计简析
Thanos设计简析
Thanos是一个实现高可用和长时间存储prometheus方案的组件合集。其主要目标是操作简单并保留prometheus的高可靠性。
Prometheus的指标数据模型和其2.0的TSDB存储格式是Thanos各组件的基础,参见: prometheus/TSDB磁盘格式。
架构
Thanos包括了多种目的的集群组件,分类为:
- Metric sources(Data sources)
- Stores
- Queries
Data sources
数据取回
Data sources通过gRPC接口从集群后端存储里获取指标数据,并返回给对应的客户。
Thanos提供了两种组件作为 Data sources:the Prometheus sidecar和rule nodes。
Sidecar在prometheus的HTTP remote-read APIs基础上实现了gRPC服务获取指标数据。
Rule node则直接在prometheus的存储引擎API基础上实现了gRPC服务获取指标数据。
数据备份
Data sources基于Prometheus 2.0存储引擎实现长期数据存储。存储引擎周期产生固定时间段的持久化block。一个block包含该时间段的所有series和samples数据以及一些索引文件用于快速查询,其结构如下:
01BX6V6TY06G5MFQ0GPH7EMXRH
├── chunks
│ ├── 000001
│ ├── 000002
│ └── 000003
├── index
└── meta.json
一个block最上层是一个类似UUID的字符串,它将目录的创建时间进行了编码,并按字典序进行了排序。目录里的文件包括:
- chunks文件每个512MB,保存series的sample数据,每个chunk里每个series数据都在一起并且严格对齐。
- index文件用于根据label查询series以及其在chunk中的位置信息。
- meta.json保存元数据信息,包括统计信息、时间范围和compaction level级别。
这些block文件可以备份到对象存储上并在后续用于其他组件的查询。当block被存储引擎创建之后,立即被上传到对象存储。在Sidecar上传时会修改meta.json文件,增加thanos区段,用于记录thanos自定义的元数据信息。目前主要是有一个外部标签
用于标识block的生产者,用于查询时过滤相关的block。
┌────────────┬─────────┐ ┌────────────┬─────────┐ ┌─────────┐
│ Prometheus │ Sidecar │ ... │ Prometheus │ Sidecar │ │ Rule │
└────────────┴────┬────┘ └────────────┴────┬────┘ └┬────────┘
│ │ │
Blocks Blocks Blocks
│ │ │
v v v
┌──────────────────────────────────────────────────┐
│ Object Storage │
└──────────────────────────────────────────────────┘
Stores
Stores是一个gateway角色用于访问对象存储bucket里的block数据。它提供了和Data sources一样的gRPC API去获取block里所有的指标数据。
Stores持续的同步对象存储里写入的block信息,并将指标查询请求转换为对象存储的请求。它实现多种策略来降低对对象存储的访问频率,包括根据元数据信息过滤block,缓存高频访问的index文件等。
Prometheus 2.0存储引擎设计时为了降低读放大做了优化。如:同一个series的指标数据在chunk里是连续和对齐的,以及:具有相同指标名的series也是对齐的。Store node利用这些特性去降低查询时访问对象存储的请求数。每次请求可能访问多大上十万个chunk,这对降低访问次数以满足更大的查询请求来说是非常必要的。
目前只有index文件会被缓存,chunk也可以被缓存,但它要大几个数量级,并且目前获取chunk所消耗的时间在端到端查询中占比不大,因此chunk目前没有必要进行缓存以降低store node的资源消耗。
Stores & Data sources
因为Stores和Data sources实现了一样的gRPC store API,因此客户端可以把他们看成一样的,查询的时候不用在乎连接哪个组件。
每个store API的提供者也提供了元数据信息用于描述它所能提供的数据,因此客户端可以据此最小化查询的nodes来满足特定的数据查询。
本质上,store API提供了根据label matchers (PromQL)和时间范围去过滤查找数据的能力。它提供简单的数据查找返回接口,返回找到的block内压缩的chunk数据,并且不提供复杂的查询语句执行。
┌──────────────────────┐ ┌────────────┬─────────┐ ┌────────────┐
│ Google Cloud Storage │ │ Prometheus │ Sidecar │ │ Rule │
└─────────────────┬────┘ └────────────┴────┬────┘ └─┬──────────┘
│ │ │
Block File Ranges │ │
│ Store API │
v │ │
┌──────────────┐ │ │
│ Store │ │ Store API
└────────┬─────┘ │ │
│ │ │
Store API │ │
│ │ │
v v v
┌──────────────────────────────────┐
│ Client │
└──────────────────────────────────┘
Querier
Querier是无状态的,支持水平扩展,它基于集群中提供的store API实现了PromQL接口。Querier也参与集群中,用于获取集群中的data sources和store nodes。 同时query nodes也能被rule nodes发现用于评估聚合和告警规则。
基于data sources和store nodes提供的元数据,query node最小化查询提供store API的服务,以满足特定的查询请求。
┌──────────────────┐ ┌────────────┬─────────┐ ┌────────────┐
│ Store Node │ │ Prometheus │ Sidecar │ │ Rule │
└─────────────┬────┘ └────────────┴────┬────┘ └─┬──────────┘
│ │ │
│ │ │
│ │ │
v v v
┌─────────────────────────────────────────────────────┐
│ Query layer │
└─────────────────────────────────────────────────────┘
^ ^ ^
│ │ │
┌────────┴────────┐ ┌──────┴─────┐ ┌────┴───┐
│ Alert Component │ │ Dashboards │ ... │ Web UI │
└─────────────────┘ └────────────┘ └────────┘
Compactor
Compactor它是一个单独的组件,不参与到Thanos集群中,即不跟其他组件发生联系。它专注于对象存储里的某个bucket进行block的合并。类似于Prometheus存储引擎的compactor,它的目的是降低存储空间,减少store node的查询次数和负载。
Compactor的另一个工作是block过期删除,以及down-sampling降精度。
Scaling
Thanos的任何组件都没有提供sharding,只有query nodes因为无状态是明确可以任意扩展的,存储能力的扩展则依赖外部的对象存储系统。
store, rule 和compactor节点通过新增单节点或者主备节点对来进行显示扩展。和Prometheus类似,sharding分片功能通常并没有存在的必要。
例如,rule规则可以分散配置到多个rule节点。store节点也可以通过配置为专门的区域/数据中心的bucket从而无需实现分片能力。
总之,分片方式的水平扩展能力虽然很好,但暂时不是Thanos考虑的重点,因为这样的场景不多。
Cost
Thanos引入的额外消耗包括存储和查询对象存储系统,以及store节点的运行消耗。
Queriers、compactors和rule节点则没有额外引入消耗,因为这些事情在原有的Prometheus上也需要。
数据从Prometheus传输到Thanos上引入了额外的网络消耗。