prometheus -- 错开时间拉取(scrape jitterSeed)分析

prometheus拉取target的metrics时,都是错开时间分别拉取的,这样有以下好处:

  • 避免prometheus实例启动太多的goroutine同时拉取;
  • 多prometheus实例拉取同一个target时,避免同时拉取对target造成流量压力;
单prometheus实例内的不同target 单实例内的不同target,不是在某个时刻一起拉取,而是错开时间分别拉取。比如scrape_interval=30s,不同target的拉取时间如下:
  • job=prometheus:20秒、50秒拉取;
  • job=node-exporter:04秒、34秒拉取;
  • job=pvc-test:07秒、37秒拉取;
> select * from up where job='prometheus' order by time desc limit 5; name: up time__name__ __type__ instancejobvalue ------------ -------- ---------------- 2021-09-24T05:52:20.665Z upgaugelocalhost:9090 prometheus 1 2021-09-24T05:51:50.665Z upgaugelocalhost:9090 prometheus 1 2021-09-24T05:51:20.665Z upgaugelocalhost:9090 prometheus 1 2021-09-24T05:50:50.665Z upgaugelocalhost:9090 prometheus 1 2021-09-24T05:50:20.665Z upgaugelocalhost:9090 prometheus 1 > > select * from up where job='node-exporter' order by time desc limit 5; name: up time__name__ __type__ instancejobvalue ------------ -------- ---------------- 2021-09-24T05:52:34.184Z upgauge10.21.1.74:9100 node-exporter 0 2021-09-24T05:52:04.183Z upgauge10.21.1.74:9100 node-exporter 0 2021-09-24T05:51:34.183Z upgauge10.21.1.74:9100 node-exporter 0 2021-09-24T05:51:04.183Z upgauge10.21.1.74:9100 node-exporter 0 2021-09-24T05:50:34.183Z upgauge10.21.1.74:9100 node-exporter 0 > > select * from up where job='pvc-test' order by time desc limit 5; name: up time__name__ __type__ instancejobvalue ------------ -------- ---------------- 2021-09-24T05:53:07.315Z upgauge127.0.0.1:9999 pvc-test 0 2021-09-24T05:52:37.315Z upgauge127.0.0.1:9999 pvc-test 0 2021-09-24T05:52:07.315Z upgauge127.0.0.1:9999 pvc-test 0 2021-09-24T05:51:37.315Z upgauge127.0.0.1:9999 pvc-test 0 2021-09-24T05:51:07.315Z upgauge127.0.0.1:9999 pvc-test 0

多prometheus实例 在HA场景下,不同的prometheus实例可能拉取相同的target。
多个prometheus实例同时启动时,开始拉取的时间也要随机错开,否则会出现多个实例同时拉取同一个target的现象。
实现机制 通过全局的scrape jitterSeed实现:
  • 不同实例:jitterSeed是跟特定prometheus实例相关的uint64哈希值,不同实例的jitterSeed不同;
  • 单实例不同target: 使用target哈希值与jitterSeed做异或操作,得到target拉取的启动时间,不同的target启动时间不同;
prometheus -- 错开时间拉取(scrape jitterSeed)分析
文章图片

prometheus实例的scrape jitterSeed的计算
// unit64 jitterSeed=hash({hostname}+{extertalLabels})

不同实例的hostname/externalLabels一般不同,故计算出来的jitterSeed值也不同。
// scrape/manager.go // setJitterSeed calculates a global jitterSeed per server relying on extra label set. func (m *Manager) setJitterSeed(labels labels.Labels) error { h := fnv.New64a() hostname, err := getFqdn() if err != nil { return err } if _, err := fmt.Fprintf(h, "%s%s", hostname, labels.String()); err != nil { return err } m.jitterSeed = h.Sum64() return nil }

prometheus实例内target拉取启动时间的计算
target被拉取前,先随机等待一个时间,然后再用interval固定间隔循环拉取:
// scrape/scrape.go func (sl *scrapeLoop) run(interval, timeout time.Duration, errc chan<- error) { //等待一段时间再开始 select { case <-time.After(sl.scraper.offset(interval, sl.jitterSeed)): // Continue after a scraping offset. case <-sl.ctx.Done(): close(sl.stopped) return } // interval固定间隔拉取 ticker := time.NewTicker(interval) defer ticker.Stop()for { // HTTP拉取 last = sl.scrapeAndReport(interval, timeout, last, errc) select { ...... case <-ticker.C: } } }

不同target有不同的等待时间,这样就可以将不同target的开始时间错开。
//target等待时间的计算方法 offset=((hash(target) ^ jitterSeed) % interval)

【prometheus -- 错开时间拉取(scrape jitterSeed)分析】代码实现:
// scrape/target.go // offset returns the time until the next scrape cycle for the target. // It includes the global server jitterSeed for scrapes from multiple Prometheus to try to be at different times. func (t *Target) offset(interval time.Duration, jitterSeed uint64) time.Duration { now := time.Now().UnixNano()// Base is a pinned to absolute time, no matter how often offset is called. var ( base= int64(interval) - now%int64(interval) offset = (t.hash() ^ jitterSeed) % uint64(interval) next= base + int64(offset) )if next > int64(interval) { next -= int64(interval) } return time.Duration(next) }

    推荐阅读