1. 前言
prometheus 已经是云原生时代的监控标准。在公司内部,我们采用的是 grafana agent(采集) + cortex(tsdb) 的分布式 prometheus 作为我们的监控方案。其中 grafana agent 负责数据采集部分,cortex 是分布式的 prometheus tsdb
而运行一段时间后发现,grafana agent 会偶发大量的对 apiserver 发起 LIST POD 请求,给 apiserver 带来很大的负载。
2. 分析
2.1 kubernetes 研发的建议
这种情况加 lister 就行了
负责 kubernetes 同事告诉我们,这种情况一般是没有使用 client-go 中的 cache,而是直接调用了 api LIST 接口,所以会造成大量的 List 请求,造成 apiserver 负载增加,只要改造成使用 cache 的方式就行
// NewPod creates a new pod discovery.
func NewPod(l log.Logger, pods cache.SharedInformer) *Pod {
if l == nil {
l = log.NewNopLogger()
}
p := &Pod{
informer: pods,
store: pods.GetStore(),
logger: l,
queue: workqueue.NewNamed("pod"),
}
p.informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(o interface{}) {
podAddCount.Inc()
p.enqueue(o)
},
DeleteFunc: func(o interface{}) {
podDeleteCount.Inc()
p.enqueue(o)
},
UpdateFunc: func(_, o interface{}) {
podUpdateCount.Inc()
p.enqueue(o)
},
})
return p
}
但仔细看了下其中 kubernetes discovery 的代码,发现 prometheus 的代码已经使用了最优解。所以排除 dicovery 代码层面的问题。
2.2 代码分析
无奈,只能深入理解下 grafana agent 的代码。grafana 的具体架构如下所示
对比 prometheus 本身,有个明显的变化是支持配置中心,可以通过接口的形式,往 grafana agent 中新增 scrape 配置。
而所有的配置,在最终管理层面,都会合并成一个大的配置,称之为 grouped config。问题就出现在这个 grouped config 上。在 grafana agent 中,配置管理和数据采集是两个模块,而配置的变更,是使用队列的方式,通过 channel 通知到 数据采集模块。
一旦有配置发生变更,会将 group 中的 config,都重启一遍。在少量的配置变更场景下,倒不是什么大问题。但是一旦服务重启。那么 scrape 模块的 restart 动作就会十分频繁,我们来分析下重启的过程
# 假设一共有 10 个配置,代号分别是 config0~9
# config0 入队,重启 1 个配置,调用 1 次 LIST POD 接口
# config1 入队,由于需要把所有配置都重启,所以重启 2 个配置(config0 + config1),调用 2 次 LIST POD 接口
# ...
# ...
# config9 入队,调用 10 次 LIST POD 接口。
所以一次完整的 grafana agent 重启,一共需要调用 LIST POD 接口 1+2+3+...+10 = 55 次,这也是为什么实际现网才 100+ 配置,却会有 1600+ 次接口调用
3 解决
3.1 逻辑优化
通过 2.2 的分析我们发现,一次配置的变更,实际上只需要重启该配置对应的 service discovery 接口就行,没必要将所有的 service discovery 都重启一遍,所以主要的优化点是在这。而通过 prometheus 社区,我们也发现了同样的 issue # Service discovery provider should not be reinitialized on /-/reload if its config is unchanged。不过该特性,prometheus 暂时还没默认开启,需要手动通过 --enable-feature=new-service-discovery-manager 启动
而 grafana agent 依赖的 prometheus pkg,使用的是 grafana agent 自己仓库中的 fork 代码 github.com/grafana/pro… ,所以对于 grafana agent 的修复,只需要在这个 fork 代码中引入 issue 中最新特性即可。