Prometheus配置文件热加载
Promtheus的TSDB时序数据库在存储了大量的数据后,每次重启Prometheus进程的时间会越来越慢, 而且日常运维工作中会经常调整Prometheus的配置文件信息,比如一些静态配置,实际上Prometheus提供了在运行时热加载配置信息的功能,在这里介绍一下。
Prometheus配置热加载提供了2种方法:
- kill -HUP pid 发送SIGHUP信号方法
- 通过Prometheus服务API接口,发送发送一个POST请求到
/-/reload
Tips: 从 Prometheus2.0 开始,热加载功能是默认关闭的,如需开启,需要在启动 Prometheus 的时候,添加
--web.enable-lifecycle
参数。
我个人更倾向于采用curl -XPOST http://localhost:9090/-/reload
的方式,因为每次reload过后, pid会改变,使用kill
方式需要找到当前进程号。
如果配置热加载成功,Prometheus会打印出下面的log:
... msg="Loading configuration file" filename=prometheus.yml ...
下面我们来看看这两种方式内部实现原理。
第一种方法: 通过 kill 命令的 HUP (hang up) 参数实现
首先Prometheus在 cmd/promethteus/main.go
中实现了对进程系统调用监听,如果收到syscall.SIGHUP
信号,将执行reloadConfig
函数。
代码类似如下:
{
// Reload handler.
// Make sure that sighup handler is registered with a redirect to the channel before the potentially
// long and synchronous tsdb init.
hup := make(chan os.Signal, 1)
signal.Notify(hup, syscall.SIGHUP)
cancel := make(chan struct{})
g.Add(
func() error {
<-reloadReady.C
for {
select {
case <-hup:
if err := reloadConfig(cfg.configFile, logger, noStepSubqueryInterval, reloaders...); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
}
case rc := <-webHandler.Reload():
if err := reloadConfig(cfg.configFile, logger, noStepSubqueryInterval, reloaders...); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
rc <- err
} else {
rc <- nil
}
case <-cancel:
return nil
}
}
},
func(err error) {
// Wait for any in-progress reloads to complete to avoid
// reloading things after they have been shutdown.
cancel <- struct{}{}
},
)
}
第二种:通过 web 模块的 /-/reload
请求实现:
首先 Prometheus 在 web(web/web.go
) 模块中注册了一个 POST 的 http 请求 /-/reload
, 它的 handler 是 web.reload
函数,该函数主要向 web.reloadCh chan 里面发送一个 error。
其次在Prometheus 的cmd/promethteus/main.go
中有个单独的 goroutine 来监听web.reloadCh
,当接受到新值的时候会执行 reloadConfig
函数。
func() error {
<-reloadReady.C
for {
select {
case <-hup:
if err := reloadConfig(cfg.configFile, logger, noStepSubqueryInterval, reloaders...); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
}
case rc := <-webHandler.Reload():
if err := reloadConfig(cfg.configFile, logger, noStepSubqueryInterval, reloaders...); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
rc <- err
} else {
rc <- nil
}
case <-cancel:
return nil
}
}
},
Prometheus内部提供了成熟的hot reload
方案,这大大方便配置文件的修改和重新加载,在Prometheus生态中,很多Exporter
也采用类似约定的实现方式。