Prometheus配置文件热加载

Promtheus的TSDB时序数据库在存储了大量的数据后,每次重启Prometheus进程的时间会越来越慢, 而且日常运维工作中会经常调整Prometheus的配置文件信息,比如一些静态配置,实际上Prometheus提供了在运行时热加载配置信息的功能,在这里介绍一下。

Prometheus配置热加载提供了2种方法:

  1. kill -HUP pid 发送SIGHUP信号方法
  2. 通过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也采用类似约定的实现方式。

0 个评论

要回复文章请先登录注册