背景
首先解释一个问题,为什么会选择让InfluxDB来持久化Prometheus的数据,直接存在Prometheus中不行吗?下面是Claude的回答,我来总结一下:
1)InfluxDB提供了更强大的Flux查询语言,比如提供了复杂的聚合和函数等,要强于Prometheus提供的PromQL语言;
2)Prometheus没有接口对存储的数据进行管理,只能按存储块删除,查询。而InfluxDB可以可以完全自由的实现增删改查的功能,而且它支持更多种标签和字段,数据可以呈现更丰富。也就是说Prometheus无法纯粹的作为一个TSDB,它更多的是为了满足实时采集监控数据;
3)InfluxDB的各方面性能更好,适合存储大量的数据。而Prometheus数据只能存在本地磁盘中,还是呢句话Prometheus并不是一个纯粹的TSDB,更多的是为了满足实时采集监控数据。
有几个主要的原因会选择让InfluxDB来持久化Prometheus的数据:
1.性能和容量:InfluxDB专注于时序列数据,性能更好,容量更大。而Prometheus主要用于 scraping 和 querying,不适合作为长期存储。
2.查询能力:InfluxDB提供了强大的Flux查询语言,提供复杂的聚合和函数,比Prometheus的PromQL更强大。
3.多维分析:InfluxDB支持多种标签和字段,可以进行多维分析,而Prometheus只支持少量标签。
4.数据可以共享:将数据发送到InfluxDB后,其他系统也可以查询和使用这些数据。
5.自动化:可以使用Telegraf或Prometheus Exporter自动向InfluxDB输送数据。
直接使用Prometheus存储的数据有几个缺点:
1.Prometheus通常只用于短期存储,2-3周,磁盘空间有限。
2.PromQL查询能力相对有限。
3.Prometheus不易扩展到PB级别的数据量。
4.数据只能在Prometheus里使用,没有共享。
所以总的来说,使用InfluxDB的优势主要在于:
1.专注于长期时序数据存储,性能和容量更好。
2.提供更强大的多维查询和分析能力。
3.易于与其他系统集成,可以共享查询的数据。
直接在Prometheus存储数据的优势在于部署简单,但是限制也很多,所以一般都通过Prometheus Exporter的方式将数据导出到InfluxDB来进行存储和分析。
Prometheus直接对接InfluxDB网上有很多,但是我觉得应该不是每个数据需要持久化吧,但网络上并没有这样的文章,我也不知道为什么,可能全部存下也不会占用多少空间?或者不在Prometheus处过滤?这篇文章就介绍一下如何从Prometheus单独提取个别数据持久化到InfluxDB,当然可能这样是没有意义的,但也在这个过程中学到了Prometheus的一些高级用法,也算开阔一下思路吧。
解决方式
首先需要注意的是InfluxDB分为1代和2代,1代是可以直接通过在prometheus.yaml配置remote_write
标签,直接从Prometheus传给InfluxDB的。而2代需要通过Telegraf(InfluxDB他们家的产品)进行一个中转。Telegraf与Prometheus功能类似,也是采集指标的,一些不同之处在于Prometheus只能采集指标,而Telegraf什么也能采集,比如你想采集日志,就安装上对应插件,就可以采集日志了;Prometheus自身也是数据库,而Telegraf不是,等。
为了方便我就使用一代产品了,只需要在prometheus.xml文件加入下面的语句即可成功,主要就是write_relabel_configs:
这里的内容。下面是官网对这个字段的解释,简单来说就是采集到数据,在发送到InfluxDB时可以改变原始数据,可能就像流处理一样。关于这个标签的更详细内容可以去relabel_config查看,还是挺复杂的,这里只展示如何解决本文的问题。
write_relabel_configs is relabeling applied to samples before sending them to the remote endpoint. Write relabeling is applied after external labels. This could be used to limit which samples are sent.
上面是官方对这个字段的说明,如何解决我的问题,我是在这个网站发现了解决方式,原文如下:
remote_write:
- url: "http://47.115.226.81:8086/api/v1/prom/write?db=prometheus"
write_relabel_configs:
- source_labels: [__name__]
regex: "go_memstats_mcache_inuse_bytes"
action: keep
来总结一下这几个标签分别是啥:
url: InfluxDB的地址(这是InfluxDB提供的8086写入接口,官方文档在https://docs.influxdata.com/influxdb/v1.8/supported_protocols/prometheus/;Prometheus也有自己的接口为9090,和InfluxDB长的挺像的,别搞混了,官方文档在https://prometheus.io/docs/prometheus/latest/querying/api/)。
write_relabel_configs: 写入时需要进行标签重写的配置项。
source_labels:表示根据什么匹配,在这个例子中,待重写的内容为根据指标名称进行匹配。
regex:表示匹配规则,也就是正则表达式,这里会匹配go_memstats_mcache_inuse_bytes这个标签。
action:表示匹配上后要执行的操作,这里keep就是要留下这个数据,如果是drop就是删除这个数据。
结果
其它
另外简单说一些InfluxDB的内容:
InfluxDB是一个用Go语言编写的时序数据库,与传统关系型数据库相比,它对时序数据场景有专门的优化。主要用在运维和IOT(物联网)(比如每隔一段时间,监控一下一氧化碳浓度什么的),比起MySQL来说,它取决于业务,比如发起个什么请求,然后才会对数据库有什么操作;而InfluxDB可以是取决于设备的。
为什么InfluxDB适合这种场景呢,因为MySQL底层是B+树,而InfluxDB是LSM(日志结构合并树)。并且为了性能,也不支持事务,不能删除某条数据,很多更新也不行
我们之前说,时序数据库一般用于指标监控场景。这个场景的数据有一个非常明显的特点就是冷热差别明显。通常,指标监控值会使用近期一段时间的数据,比如我只查询某个设备近10分钟的记录,10分钟前的数据我就不再用了。那么这10分钟前的数据,对我们来说就是冷数据,应该被压缩放到磁盘里去来节省空间。而热数据因为经常要用,数据库就应该让它留在内存里,等待查询。
关系型数据库也是支持时间戳的,也能基于时间进行查询。但是,从我们的使用场景出发,需要注意数据库的写入性能,通常,关系型数据库会采用B+数据结构,在数据写入时,有可能会触发叶裂变,从而产生了对磁盘的随机独写,降低写入速度。
当前市面上的时序数据库通常都是采用LSM Tree的变种,顺序写磁盘来增强数据的写入能力。网上有不少关于性能测试的文章,通常时序数据库都会保证在单点每秒数十万的写入能力。
下面时InfluxDB行协议,也就是数据格式。InfluxDB是没有表的,它有一个叫bucket,这就相当于数据库,数据库里存放这些数据,第一个measurement其实就是代表了表;tag set相当于是一个索引;field set也就是存储的值;timestamp就是时间戳。总的来说measurement和tag set组成了一个索引,timestamp也是一个索引,组成了双索引的结构。
例子:
home,room=Living\ Room temp=21.1,hum=35.9,co=0i 1641024000
home,room=Kitchen temp=21.0,hum=35.9,co=0i 1641024000
home,room=Living\ Room temp=21.4,hum=35.9,co=0i 1641027600
home,room=Kitchen temp=23.0,hum=36.2,co=0i 1641027600
这里另外提一下Prometheus的数据格式<metric name>{<label name>=<label value>, ...}
,但其实可以由四部分组成,首先就是指标名称;tag标签,可选;值,只能是一个,不像InfluxDB;时间戳,这个可选,不写,就是默认进入的那一刻。
例子:
node_cpu_seconds_total {cpu="0",mode="iowait"} 455.09
Prometheus和InfluxDB格式不一样,是怎么传的?Prometheus在根据Remote Write API传到InfluxDB时,会将标签labels编码为InfluxDB中的tag键值对,将测量值(value)编码为field。
最后说一下,InfluxDB对应的查询语言为Flux,功能就是像SQL一样,查东西,大概长下面这样:
from(bucket: "get-started")
|> range(start: 2022-01-01T08:00:00Z, stop: 2022-01-01T20:00:01Z)
|> filter(fn: (r) => r._measurement == "home")
|> filter(fn: (r) => r._field== "co" or r._field == "hum" or r._field == "temp")
而Prometheus的PromQL长下面这样,查询功能比较简单:
prometheus_http_requests_total{}[5m]