当前位置: 首页 > article >正文

33.2 prometheus联邦功能源码解读和它的问题

本节重点介绍 :

  • prometheus 联邦使用的误解
  • federate源码分析

prometheus 联邦使用的误解

  • 我看到很多人会这样使用联邦:联邦prometheus 收集多个采集器的数据
  • 实在看不下下去了,很多小白还在乱用prometheus的联邦
  • 其实很多人是想实现prometheus数据的可用性,数据分片保存,有个统一的查询地方(小白中的联邦prometheus)
  • 今天写篇文章分析下联邦的问题,并给出一个基于全部是prometheus的multi_remote_read方案

联邦问题

  • 联邦文档地址

联邦使用配偶样例

  • 本质上就是采集级联
  • 说白了就是 a 从 b,c,d那里再采集数据过来
  • 可以搭配match指定只拉取某些指标
  • 下面就是官方文档给出的样例
scrape_configs:
  - job_name: 'federate'
    scrape_interval: 15s

    honor_labels: true
    metrics_path: '/federate'

    params:
      'match[]':
        - '{job="prometheus"}'
        - '{__name__=~"job:.*"}'

    static_configs:
      - targets:
        - 'source-prometheus-1:9090'
        - 'source-prometheus-2:9090'
        - 'source-prometheus-3:9090'

federate源码分析

看上面的样例配置怎么感觉是采集的配置呢

  • 不用怀疑就是,下面看看代码分析一下
  • 从上述配置可以看到采集的 path是/federate
  • 代码位置 D:\go_path\src\github.com\prometheus\prometheus\web\web.go
    // web.go 的 federate Handler
	router.Get("/federate", readyf(httputil.CompressionHandler{
		Handler: http.HandlerFunc(h.federation),
	}.ServeHTTP))

分析下联邦http处理函数 说白了就是读取本地存储数据处理

  • 代码位置 D:\go_path\src\github.com\prometheus\prometheus\web\federate.go
func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {

	// localstorage 的query
	q, err := h.localStorage.Querier(req.Context(), mint, maxt)

	defer q.Close()
	// 最终发送的Vector 数组
	vec := make(promql.Vector, 0, 8000)

	hints := &storage.SelectHints{Start: mint, End: maxt}

	var sets []storage.SeriesSet

	set := storage.NewMergeSeriesSet(sets, storage.ChainedSeriesMerge)
    // 遍历存储中的full series
	for set.Next() {
		s := set.At()


		vec = append(vec, promql.Sample{
			Metric: s.Labels(),
			Point:  promql.Point{T: t, V: v},
		})

	for _, s := range vec {
		nameSeen := false
		globalUsed := map[string]struct{}{}
		protMetric := &dto.Metric{
			Untyped: &dto.Untyped{},
		}
        // Encode方法根据请求类型编码
				if protMetricFam != nil {
					if err := enc.Encode(protMetricFam); err != nil {
						federationErrors.Inc()
						level.Error(h.logger).Log("msg", "federation failed", "err", err)
						return
					}
				}

		}

		protMetric.TimestampMs = proto.Int64(s.T)
		protMetric.Untyped.Value = proto.Float64(s.V)

		protMetricFam.Metric = append(protMetricFam.Metric, protMetric)
	}
	// 
	if protMetricFam != nil {
		if err := enc.Encode(protMetricFam); err != nil {
			federationErrors.Inc()
			level.Error(h.logger).Log("msg", "federation failed", "err", err)
		}
	}
}

解读一下

  • 因为是将本地的数据发送走,所以首先创建一个localstorage 的Querier
	q, err := h.localStorage.Querier(req.Context(), mint, maxt)

  • 构造发送数据用的vector 容器
	// 最终发送的Vector 数组
	vec := make(promql.Vector, 0, 8000)
  • 遍历存储中的full series,塞入数据
    // 遍历存储中的full series
	for set.Next() {
		s := set.At()


		vec = append(vec, promql.Sample{
			Metric: s.Labels(),
			Point:  promql.Point{T: t, V: v},
		})
  • 对vector 容器数据进行排序,并准备一会要注入的外部标签组externalLabelNames
	sort.Sort(byName(vec))

	externalLabels := h.config.GlobalConfig.ExternalLabels.Map()
	if _, ok := externalLabels[model.InstanceLabel]; !ok {
		externalLabels[model.InstanceLabel] = ""
	}
	externalLabelNames := make([]string, 0, len(externalLabels))
	for ln := range externalLabels {
		externalLabelNames = append(externalLabelNames, ln)
	}
	sort.Strings(externalLabelNames)
  • 遍历vector,进行protocol 编码,并注入externalLabel
	for _, s := range vec {
				for _, ln := range externalLabelNames {
			lv := externalLabels[ln]
			if _, ok := globalUsed[ln]; !ok {
				protMetric.Label = append(protMetric.Label, &dto.LabelPair{
					Name:  proto.String(ln),
					Value: proto.String(lv),
				})
			}
		}

		protMetric.TimestampMs = proto.Int64(s.T)
		protMetric.Untyped.Value = proto.Float64(s.V)

		protMetricFam.Metric = append(protMetricFam.Metric, protMetric)
    }
最终调用压缩函数压缩发送数据
  • 代码位置 D:\go_path\src\github.com\prometheus\prometheus\util\httputil\compression.go
type CompressionHandler struct {
	Handler http.Handler
}

// ServeHTTP adds compression to the original http.Handler's ServeHTTP() method.
func (c CompressionHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	compWriter := newCompressedResponseWriter(writer, req)
	c.Handler.ServeHTTP(compWriter, req)
	compWriter.Close()
}

federate问题结论

如果没有过滤那么只是一股脑把分片的数据集中到了一起,没意义

  • 很多时候是因为数据量太大了,分散在多个采集器的数据是不能被一个联邦消化的

正确使用联邦的姿势

  • 使用match加过滤,将采集数据分位两类
    • 第一类需要再聚合的数据,通过联邦收集在一起
      • 举个例子
        • 只收集中间件的数据的联邦
        • 只收集业务数据的联邦
    • 其余数据保留在采集器本地即可
  • 这样可以在各个联邦上执行预聚合alert,使得查询速度提升

默认prometheus是不支持降采样的

  • 可以在联邦配置scrape_interval的时候设置的大一点来达到 模拟降采样的目的
  • 真实的降采样需要agg算法支持的,比如5分钟的数据算平均值、最大值、最小值保留,而不是这种把采集间隔调大到5分钟的随机选点逻辑

正确实现统一查询的姿势是使用prometheus multi_remote_read

本节重点总结 :

  • prometheus 联邦使用的误解
  • federate源码分析

http://www.kler.cn/a/418385.html

相关文章:

  • 修改IDEA配置导致Spring Boot项目读取application.properties中文乱码问题
  • Spring Boot优雅读取配置信息 @EnableConfigurationProperties
  • 电机控制理论基础及其应用
  • Pinia管理用户数据
  • 行驶证 OCR 识别API接口的影响因素
  • Java多线程学习二
  • 【深度学习】服务器常见命令
  • 【JavaEE】多线程(2)
  • 【汇编】逻辑指令
  • 重生之我在异世界学编程之C语言:二维数组篇
  • vue 2 父组件根据注册事件,控制相关按钮显隐
  • 1 MyBatis 的增删改查操作
  • 瑞芯微rv1106的自带的录音和播放功能
  • 图论入门教程:GTM173 Graph Theory
  • centos里docker安装jenkins
  • python实现TCP Socket控制测试仪器
  • go语言的成神之路-筑基篇-对文件的操作
  • 基于LSTM的文本多分类任务
  • C、C++ 和 Java的区别
  • stm32 HAL读取GZP6816D传感器
  • 移远通信携手紫光展锐,以“5G+算力”共绘万物智联新蓝图
  • WPF控制文本框输入的小数点位数
  • 使用Dify与BGE-M3搭建RAG(检索增强生成)应用-改进一,使用工作流代替Agnet
  • Mybatis:接口方法中的参数传递
  • Vue 集成和使用 SQLite 的完整指东
  • SpringBoot开发——Spring Boot 3.3 高效批量插入万级数据的多种方案