0 - 本案例所涉及的知识点
云原生、微服务,带你了解大规模容器下的监控方式,通过各个案例分析,熟悉prometheus的内部原理。
涉及知识点:go prometheus
1 - 案例概要
收到用户反馈,使用grafana通过设置prometheus数据源,绘制容器的网络指标时,出现异常,guan j z
2 - 故障重现
服务端prometheus版本为2.9.2,通过访问内置UI地址 prometheus-server:9090,查询数据重现故障,错误截图如下,
通过浏览器开发者模式,获取当前具体的请求接口可表示为:
响应的HTTP状态码422 Unprocessable Entity,错误信息:
3 - 初步假设
看字面意思是查询返回的样本结果集合中存在相同的标签,至于是什么标签相同,只能通过查询源码定位问题了。
4 - 理论知识
4.1 - Prometheus基础数据结构
首先熟悉Prometheus几个基础数据结构
Sample与Series的区别:
- Sample是样本:一个标签组合,只包含一个时间点以及该点的时间数据
- Series是序列:一个标签组合,由多个时间点以及对应的数据组合而成
Vector与Matrix的区别:
- Vector是一个向量数据结构,由多个样本组成,这些样本以时间为方向,具有相同的方向
- Matrix是一个矩阵数据结构,由多个序列数据组成
4.2 - 通过关键字匹配定位源码
源码文件:github.com/prometheus/prometheus/promql/engine.go
- 关键函数result.ContainsSameLabelset的实现源码
该函数主要用于检测在查询返回的数据中,是否存在相同的标签集合,由于向量与矩阵标签类型相同,所以实际它们内部的实现也是一样的。
源码文件:github.com/prometheus/prometheus/promql/value.go
- 判断样本标签组唯一性,函数s.Metric.Hash()的实现
源码文件:github.com/prometheus/prometheus/pkg/labels/labels.go
也就是在这计算中Vector(向量)或Matrix(矩阵),不能存在相同的Labels(标签组)。
5 - 疑点提出
- Prometheus的持久存储方案是内部开发团队自实现的,是这个引起的?
- Prometheus本地存储设置只保留最近24h的数据,难道是这部分数据异常?
- 分别关闭本地与远程存储,如果均正常,难道是查询返回了相同的两条指标?
- 向量与矩阵里面的样本或序列数据是不能存在相同的标签组合?
6 - 实践检验
6.1 - 实验前准备
下载源码,通过以下命令可编译二进制可执行文件
cd github.com/prometheus/prometheusgo build cmd/prometheus/main.go
6.2 - 验证疑点1
由于prometheus本身不适合做长久数据存储,所以我们内部设计了一套持久存储的方案,结合配置remote_read、remote_write实现。
为了验证疑点1,可以先把prometheus.yml中的remote_read设置关闭,然后重载prometheus配置。
通过停止remote_read设置之后就可以正常使用了,但这里也并不能确定就是设置remote_read的问题。
6.2 - 验证疑点2
通过关闭远程数据读取可以解决问题,那通过删除本地数据呢?通过以下步骤:
- 设置prometheus.yml取消原先对remote_read的注释。
- 关闭prometehus进程,清除本地存储目录下的所有数据,实际由storage.tsdb.path设置,如:data/*
- 重新启动prometheus进程。
在刚启动时候,可以正常查询出数据:
等待一会后,出现同样问题:
6.3 - 验证疑点3
在同时开启本地与远程数据存储的情况下,通过promQL查询以下语句:
container_network_receive_bytes_total{pod_name="magick-img-converter-747d899b66-lsrn6"}[1m]
接口返回内容:
确实是访问两个相同标签组的数据,但是时间不同。
对这结果集使用rate函数计算,也就出现了原先的问题。
rate(container_network_receive_bytes_total{pod_name="magick-img-converter-747d899b66-lsrn6"}[1m])
6.4 - 验证疑点4
相同的标签组合是指:一组同样的标签名与标签值。这里通过更改prometheus代码,植入测试语句,获取从远程或本地的数据,对比差异,查询到底哪里导致这个问题的产生。
由以上几个疑点验证可知,问题出现在eval函数的case *Call语句中。
通过对以上函数添加调试代码,然后编译promethues运行,在执行相应的promQL查询。
这里可以看到,存在两个数据,在未移除标签__name__之前,上图存在两个__name__,下图只有一个__name__,当移除之后,他们就存在相同的标签组了。
7 - 问题定位
查询指标数据时,prometheus分别从本地存储与远程存储获取数据,然后进行合并,远程存储获取的数据相比本地多了一个__name__标签。
8 - 解决方案
8.1 - 方案1
修复远程存储返回多余的__name__标签。
8.2 - 方案2
修改prometheus代码,如果存在重复的__name__标签键值,则想办法去重。
9 - 总结讨论
通过方案1,我们就解决了这个问题,同时也继续跟踪prometheus源码,能否为它提交PR,增强健壮性。
如存在疑问点,欢迎留言讨论,本人关注云原生生态,如果您也喜欢,欢迎关注我,一起学习,共同进步。