注:compaction,在本文档中有些地方意译为压缩。
原文:Thanos - Highly available Prometheus setup with long term storage capabilities
Compact UI:http://IP:10902/new/loaded
mete.json中compaction.level的含义
level:1 未合并的block
level:2 4个level0的block合并成的block
level:3 5个level1的block合并成的block
thanos compact命令运用Prometheus 2.0存储引擎的compaction过程处理存储在对象存储中数据。Compactor组件通常不是并发安全的,因此必须作为单例部署。
Compactor还负责数据的降采样:
- 为超过40小时(2d,2w)的blocks创建5分钟间隔的降采样
- 为超过10天(2w)的blocks创建1小时间隔的降采样
例如:
thanos compact --data-dir /tmp/thanos-compact --objstore.config-file=bucket.yml
bucket.yml的一个示例内容如下:
type: GCS config: bucket: example-bucket
默认情况下,thanos compact命令会一直运行到处理结束,因此该命令可以用来在cronjob中执行。使用参数--wait和--wait-interval=5m可以让其持续运行。
Compactor、Sidecar、Receive和Ruler是唯四拥有对象存储写权限的Thanos组件,其中Compactor是唯一拥有删除数据权限的组件。
注意:Compactor的高可用通常是不必要的。
Compaction
Compactor及其他一些程序负责将多个blocks压缩成一个block。
为什么还要进一步压缩blocks?这是一个减少blocks数量和压缩索引的过程,Prometheus中也是这样做的。在大多数情况下,我们可以很好地压缩索引,因为时序通常比最小的block的存活时间长。
Block流
通常,这些blocks来自同一源。我们称来自同一数据源的blocks为“blocks流”或"可压缩组"。我们通过external labels来区分流,具有相同标签的blocks被视为由同一数据源生成。这是因为external_labels是由生成该block的Prometheus添加的。
⚠️这就是为什么block上的标签在不同的Prometheus实例中必须是唯一的和持久的。⚠️
- 通过保证“唯一性”,即每个Prometheus实例中的标签集必须不同于其他Prometheus实例,以便compactor能够按Prometheus实例对blocks进行分组。
- 通过保证“持久性”,即一个Prometheus实例在重启后时必须保持相同的标签,以便compactor能够继续压缩来自该实例的blocks,即使Prometheus实例宕机一段时间。
Prometheus天生不在任何地方储存外部标签external_labels。这就是为什么外部标签只有在数据上传时候才会被添加到到每个block中meta.json的ThanosMeta部分(可参看对象存储中每个block文件夹下的meta.json文件)。
注意:在默认模式下,拥有两个或多个具有相同外部标签且在时间上重叠的block的状态,被认为是异常状态。有关更多信息,请参阅重叠问题故障排除(Thanos - Highly available Prometheus setup with long term storage capabilities)。这会导致compactor挂起。
单例规则
警告:单个对象存储中的单个block流仅能由一个compactor处理。⚠️ ⚠️ ⚠️
由于没有针对对象存储提供安全锁机制,目前,您需要自己确保只有单个compactor针对单个bucket上的单个block流运行。运行多个compactor可能导致必须手动解决的重叠问题。
单例规则还意味着,当sidecar同时上载压缩块和非压缩块时,可能会出现问题。这就是为什么“upload-compacted”参数(sidecar的参数)被放在一个单独的--shipper.upload-compacted参数下的原因,该参数有助于确保压缩块优先于其他任何内容上传。单例规则也是为什么必须禁用本地Prometheus压缩功能才能使用带upload选项的sidecar。使用隐藏参数--shipper.ignore-unqualified-block-size可以屏蔽此检查(后果自负)。
注:在后续的Thanos版本中,这些限制可能会随着垂直压缩的成熟应用而被移除。
不过,你可以对单个bucket中的不同block流运行多个compactor程序,以扩展compaction过程。
垂直压缩
此部分功能(包括重复数据消除)有应用风险,不译。
Thanos - Highly available Prometheus setup with long term storage capabilities
Thanos - Highly available Prometheus setup with long term storage capabilities
Thanos - Highly available Prometheus setup with long term storage capabilities
Thanos - Highly available Prometheus setup with long term storage capabilities
配置数据保留策略
默认情况下,对象存储数据没有配置保留策略。这意味着你可以无限期地存储数据,这是一种有效且推荐的运行Thanos的方法。
你可以使用--retention.resolution-raw、--retention.resolution-5m和--retention.resolution-1h参数按不同的分辨率设置数据保留时间。不设置或设置为0s意味着永久保留。
注:⚠️保留在compaction和降采样后执行。如果这些步骤失败,数据将永远不会被删除。
降采样
降采样是重写时序以降低样本整体分辨率的过程,但不会在大跨度时间范围内失去精度。
Thanos是如何降采样的
Thanos Compactor获取原始(raw)分辨率的block,并创建一个降采样后的新block。降采样数据块(chunk)采用“AggrChunk”的形式存储:
message AggrChunk { int64 min_time = 1; int64 max_time = 2; Chunk raw = 3; Chunk count = 4; Chunk sum = 5; Chunk min = 6; Chunk max = 7; Chunk counter = 8; }
这意味着,对于每个时序,我们以给定的间隔收集各种聚合数据:5m或1h(取决于分辨率)。这使得我们在大跨度时间内查询时,能够在不获取过多样本的情况下,保持查询的精度。
⚠️ 降采样:关于分辨率和保留的注意事项⚠️
分辨率是图形上数据点之间的距离。例如。
- raw——与数据采集时的采集间隔相同
- 5分钟——数据点为每5分钟一个
- 1小时——数据点为每1小时一个
请记住,降采样的最初目标不是节省磁盘或对象存储的空间。事实上,降采样并不会节省任何空间,相反,它会为每个原始block额外增加2个block,这些block只比原始block稍小或基本相等。这是通过内部降采样过程实现的(保留数学计算聚合数据,参看上一节)。这意味着,如果选择存储所有分辨率(推荐的和默认的配置),降采样后将会增加存储量的大小(约为原来的3倍)。
降采样的目的是为大时间范围(如数月或数年)的查询快速提供结果。换句话说,如果将--retention.resolution-raw的时间设置为小于--retention.resolution-5m和--retention.resolution-1h,则可能会遇到无法“放大”历史数据的问题。在配置该参数之前,考虑如下问题——“我是否需要1年前某一天的原始数据?”,如果回答“是”,则应当将原始数据的保留时长设置为和1小时及5分钟分辨率数据相等,否则您将只能看到原始数据的降采样表示。
还有一种情况是,您可能希望使用--downsampling.disable完全禁用降采样。当你确定不会请求长范围的数据时,可能希望如此配置(显然,因为如果不进行降采样,这些请求将比使用它要昂贵得多)。这种情况的一个有效例子是,当你只关心最后几周的数据,或者只将其用于告警时。但如果是这样的情况,你还需要问问自己,你是否有必要引入thanos,而不是普通的Prometheus?
理想情况下,所有分辨率的数据拥有相同的保留时长(或不设置保留时长),这样既可以拥有“放大”功能,也便于执行大范围查询。由于对象存储通常非常便宜,所以存储大小可能没有那么重要。
不设置此参数,或将其设置为0d,例如--retention.resolution-X=0d,将意味着X分辨率级别的样本将永久保留。
请注意,block只有在完全“脱离”指定的保留策略后才会被删除。换句话说,block的“max time”(meta.json)必须早于您指定的时间量,才会被删除。
删除中止的部分上载
数据生产者在上传block的过程中可能意外终止,并且永远不会完成上传。sidecar在上传或处理过程中出现故障时将会重试(除非没有配置持久性存储),但最常见的情况是compactor。如果compactor进程在上传压缩块的过程中崩溃,则整个压缩过程会从零开始,并创建新的block ID。这意味着compactor永远不会重试部分上载。
要处理这种情况,可以使用一个--delete delay=48h的参数,它将在给定时间后开始删除对象存储中不带meta.json的目录。这个值必须小于上载持续时间和consistency delay。
进程挂起
由于compactor的特殊性质,即写入对象存储、删除敏感数据和下载GB级数据,因此默认情况下,我们会在某些数据故障时挂起compactor。这意味着在指标thanos_compactor_halteds设置为1时,compactor不会因挂起错误而崩溃,而是保持运行,并且不执行任何操作。
这么做的原因是,我们不希望在由于某种原因导致对象存储中已经存在重叠状态的情况下,去重试压缩和计算过程。
隐藏标志--no-debug.halt-on-error可以控制此行为。如果设置了此参数,则compactor将在发生挂起错误时退出。
资源
cpu
建议将--compact.concurrency参数设置为CPU核数。
memory
内存使用情况取决于对象存储中的block的大小和压缩并发配置。
通常,最大内存利用率与Prometheus的压缩过程完全相同:
- For each source block considered for compaction:
-
- 1/32 of all block’s symbols
- 1/32 of all block’s posting offsets
- Single series with all labels and all chunks.
将其与--compact.concurrency(默认为1)相乘,即得到最大内存利用量。
注意:不要只检查堆内存,而是应该检查进程或操作系统的内存利用率。Prometheus和Thanos的compaction严重利用了mmap,这不在Go运行时统计之内。在Linux/MacOS上,Go也将尽可能多地使用可用资源,因此利用率将始终接近极限。
一般来说,对于中等大小的bucket,10GB的内存足以使其正常工作。
Network
总体来说,compactor可能是对对象存储网络使用最多的组件,因此应该其放置在bucket的zone/location附近。
compactor会下载压缩和降采样所需的每个block,然后上传处理后的block,并且还需要经常刷新bucket的状态。
磁盘
compactor需要本地磁盘来存储用于处理的中间数据以及bucket状态缓存。一般来说,对于中等大小的bucket,随着处理数据时间范围的增加,大约100GB的本地空间应足以应付。然而,这在很大程度上取决于blocks的大小。在最坏的情况下,必须有2倍于2周数据量大小的空间以进行压缩操作(如果最大压缩级别为2周)。首先,compactor会下载两周内所有的block,然后在磁盘上生成由这些较小的block合并成的较大的block。
最终所需的磁盘空间还要与--compact.concurrency的值(默认为1)相乘。
磁盘上的数据可以在程序重启之间安全地删除,这也是使崩溃挂起的程序再次运行的首选方案。但是,建议为compactor提供持久磁盘,以便在重启之间有效地使用bucket状态缓存。
可用性
compactor通常不需要高可用性。
可伸缩性
Compactor的主要且唯一的服务级别指标是它上传TSDB block到bucket的速度。
需要关注的指标有:
thanos_objstore_bucket_last_successful_upload_time:时间正常刷新
thanos_compactor_halted:值不为1
hanos_blocks_meta_synced{state="loaded"}:值不断增加
通常有两个可伸缩方向:
1、过多的数据生产者(如Prometheus)上传数据到同一对象存储中,导致block流过多。compactor必须与bucket中的生产者数量成正比。
这种情况下应该水平拓展compactor,使用(label sharding)标签切分处理此问题,将不同的block流分配给不同的compactor。
2、来自单个流的TSDB block太大,需要太多的时间或资源去处理。
这种情况很少见的。不建议出现,应控制时序的数量。
最终一致性
取决于对象存储提供者(如S3、GCS、Ceph等),我们可以将存储划分为强一致或最终一致。由于某些对象存储没有提供一致性保证,因此无论选择何种对象存储,我们都必须确保采用一致的无锁方式处理对象存储。
一致性延迟
为了确保我们不读取部分上传的block,我们为所有组件读取block创建了一个延迟参数--consistency-delay=30m。 这意味着,block在开始往对象存储上传的30分钟后,才会对compactor可见/可加载。
Block删除
为了实现compactor和所有对象存储读取者之间的协调,block不会被直接删除,而是通过上传deletion-mark.json文件到要被删除的block中,将其标记为删除状态。deletion-mark.json中包含block被标记为删除时的unix时间。被标记的block将在--delete-delay参数指定的时间后被删除。