Prometheus源码专题精讲——监控系统 Prometheus 2.x/3.x 源码阅读和分析_配置文件的加载与刷新-config.go
配置文件的加载与刷新——config/config.go
https://github.com/prometheus/prometheus/blob/v2.55.0/config/config.go
当前https://github.com/prometheus/prometheus/releases
稳定版本号是2.55.0 / 2024-10-22
, 并且v3.x
版还是https://github.com/prometheus/prometheus/releases/tag/v3.0.0-beta.1
, 就以最新的Latest v2.55.0
版讲。
Prometheus配置文件的加载过程主要可以分为以下两步:
1、初始化时加载
2、运行时重新加载
一、初始化时加载
1.1、启动时加载
https://github.com/prometheus/prometheus/blob/v2.55.0/cmd/prometheus/main.go
Prometheus
在启动时,会自动查找并加载其配置文件。默认情况下,它会寻找当前目录下的prometheus.yaml
文件。但用户也可以通过命令行参数--config.file
来自定义配置文件的名称或路径。
在Prometheus
启动过程中
>,配置文件的首先加载和查找发生在https://github.com/prometheus/prometheus/blob/v2.55.0/cmd/prometheus/main.go
的main
函数中进行。首先,
在第509-514
行处,这里,a.Parse(os.Args[1:])
解析命令行参数,包括配置文件路径(通过--config.file
参数指定)。
Prometheus
找到加载的prometheus.yaml
路径后,开始进行配置文件加载。下面这段代码调用https://github.com/prometheus/prometheus/blob/v2.55.0/config/config.go
的LoadFile
函数来加载Prometheus
配置文件,这个函数负责从指定的的prometheus.yaml
文件中解析并加载配置。
这里解释下该函数签名:
1 | func LoadFile(filename string, agentMode, expandExternalLabels bool, logger log.Logger) (*Config, error) |
-
-
-
-
filename string
: 要加载的Prometheus.yaml
配置文件的路径。 -
agentMode bool
: 一个布尔值,指示Prometheus
是否以代理模式运行。代理模式和服务器模式在配置要求上有所不同。 -
expandExternalLabels bool
: 一个布尔值,指示是否应该扩展外部标签。 -
logger log.Logger
: 一个日志记录器,用于记录日志信息。 -
返回值:
*Config
是一个指向解析后的配置对象的指针,error
是一个错误对象,如果发生错误则返回。
-
-
-
该函数逻辑:
1、先读取文件内容
1 2 3 4 | content, err := os.ReadFile(filename) if err != nil { return nil, err } |
使用os.ReadFile
函数读取指定路径的文件内容。如果读取失败,则返回错误。
2、解析Prometheus.yaml内容
1 2 3 4 | cfg, err := Load(string(content), expandExternalLabels, logger) if err != nil { return nil, fmt.Errorf( "parsing YAML file %s: %w" , filename, err) } |
将文件内容转换为字符串,并调用Load
函数来解析Prometheus.yaml
内容。
Load
函数负责将Prometheus.yaml字符串解析为Config
对象。如果解析失败,则返回错误。
3、验证是否开启代理模式
1 2 3 4 5 6 7 8 9 10 11 12 13 | if agentMode { if len(cfg.AlertingConfig.AlertmanagerConfigs) > 0 || len(cfg.AlertingConfig.AlertRelabelConfigs) > 0 { return nil, errors.New( "field alerting is not allowed in agent mode" ) } if len(cfg.RuleFiles) > 0 { return nil, errors.New( "field rule_files is not allowed in agent mode" ) } if len(cfg.RemoteReadConfigs) > 0 { return nil, errors.New( "field remote_read is not allowed in agent mode" ) } } |
如果agentMode
是true
, 则检查配置中是否包含来在代理模式下不允许的字段(比如cfg.AlertingConfig.AlertmanagerConfigs
、cfg.AlertingConfig.AlertRelabelConfigs
、cfg.RuleFiles
和cfg.RemoteReadConfigs
)。如果包含来这些字段,则返回错误。
4、设置配置目录
1 | cfg.SetDirectory(filepath.Dir(filename)) |
这里的cfg
是解析后的配置对象,filename
是配置文件的路径,
filepath.Dir(filename)
返回了filename
的目录部分,就是配置文件所在的目录。调用SetDirectory
方法就能确保配置中所有相对路径都是配置文件中所在的目录。简单地将,就是将相对路径转成绝对路径。
5、函数返回配置的对象
1 | return cfg, nil |
1.2、配置初始化
https://github.com/prometheus/prometheus/blob/release-2.28/config/config.go
在Prometheus的配置文件加载过程中,配置初始化是一个关键步骤。它确保了配置文件在加载后能够被正确地解析和应用到系统中。配置初始化主要涉及到将配置文件中的配置信息解析为系统内部使用的数据结构,并设置一些初始状态。
在Prometheus
中,配置初始化的过程通常发生在config/config.go
文件中,特别是在LoadFile
函数调用之后,系统启动之前。LoadFile
函数负责读取并解析配置文件,返回一个配置对象。随后,系统会调用该配置对象的相关方法来设置初始状态,并检查配置文件的完整性和有效性。如果配置文件存在错误或缺失必要的信息,系统会在初始化阶段报错并终止启动。
让我从源码角度详细解释 Prometheus的配置初始化过程。
1、配置文件加载和解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | https: //github.com/prometheus/prometheus/blob/release-2.28/config/config.go // LoadFile 解析YAML配置文件 func LoadFile(filename string, expandExternalLabels bool, logger log.Logger) (*Config, error) { // 读取配置文件内容 content, err := ioutil.ReadFile(filename) if err != nil { return nil, err } // 解析配置内容 cfg, err := Load(string(content), expandExternalLabels, logger) if err != nil { return nil, errors.Wrapf(err, "parsing YAML file %s" , filename) } // 设置配置文件目录 cfg.SetDirectory(filepath.Dir(filename)) return cfg, nil } |
2、配置验证和初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | https: //github.com/prometheus/prometheus/blob/release-2.28/config/config.go // UnmarshalYAML 实现配置解析和验证 func (c *Config) UnmarshalYAML(unmarshal func ( interface {}) error) error { // 设置默认配置 *c = DefaultConfig // 解析YAML到配置结构 type plain Config if err := unmarshal((*plain)(c)); err != nil { return err } // 验证全局配置 if c.GlobalConfig.isZero() { c.GlobalConfig = DefaultGlobalConfig } // 验证规则文件路径 for _, rf := range c.RuleFiles { if !patRulePath.MatchString(rf) { return errors.Errorf( "invalid rule file path %q" , rf) } } // 验证抓取配置 jobNames := map [string] struct {}{} for _, scfg := range c.ScrapeConfigs { // 检查配置是否为空 if scfg == nil { return errors.New( "empty or null scrape config section" ) } // 设置抓取间隔 if scfg.ScrapeInterval == 0 { scfg.ScrapeInterval = c.GlobalConfig.ScrapeInterval } // 验证超时设置 if scfg.ScrapeTimeout > scfg.ScrapeInterval { return errors.Errorf( "scrape timeout greater than scrape interval for scrape config with job name %q" , scfg.JobName) } // 检查作业名称唯一性 if _, ok := jobNames[scfg.JobName]; ok { return errors.Errorf( "found multiple scrape configs with job name %q" , scfg.JobName) } jobNames[scfg.JobName] = struct {}{} } } |
3、特定组件配置验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // 远程写入配置验证 func (c *RemoteWriteConfig) UnmarshalYAML(unmarshal func ( interface {}) error) error { // 设置默认值 *c = DefaultRemoteWriteConfig // 基本验证 if c.URL == nil { return errors.New( "url for remote_write is empty" ) } // 验证重标记配置 for _, rlcfg := range c.WriteRelabelConfigs { if rlcfg == nil { return errors.New( "empty or null relabeling rule in remote write config" ) } } // 验证HTTP头 if err := validateHeaders(c.Headers); err != nil { return err } // 验证认证配置 if err := c.HTTPClientConfig.Validate(); err != nil { return err } return nil } |
4、配置目录设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // SetDirectory 设置相对路径 func (c *Config) SetDirectory(dir string) { // 设置全局配置路径 c.GlobalConfig.SetDirectory(dir) // 设置告警配置路径 c.AlertingConfig.SetDirectory(dir) // 设置规则文件路径 for i, file := range c.RuleFiles { c.RuleFiles[i] = config.JoinDir(dir, file) } // 设置抓取配置路径 for _, c := range c.ScrapeConfigs { c.SetDirectory(dir) } } |
二、运行时加载
Prometheus支持在运行时重新加载配置文件,这使得用户可以在不停止Prometheus服务的情况下更新配置。运行时加载配置文件的机制是Prometheus灵活性和可扩展性的重要组成部分。
2.1、动态加载支持
Prometheus通过实现动态加载支持,允许在运行时重新加载配置文件。
这个功能主要依赖于cmd/prometheus/main.go
和web/api_v1.go
等文件。在cmd/prometheus/main.go
中,Prometheus
设置了一个信号处理器来监听SIGHUP
信号,当收到该信号时,会触发配置文件的重新加载。而在web/api_v1.go
中,Prometheus
实现了一个HTTP
API
端点,允许用户通过发送HTTP POST
请求来触发配置文件的重新加载。
2.2、重新加载触发方式
Prometheus
提供了两种触发配置文件重新加载的方式:发送SIGHUP
信号和HTTP
POST
请求。
2.2.1、发送SIGHUP信号
用户可以通过向Prometheus
进程发送SIGHUP
信号来触发配置文件的重新加载。在cmd/prometheus/main.go
文件中,Prometheus
设置了一个信号通道来监听操作系统发送的信号。当收到SIGHUP
信号时,系统会调用reloadConfig
函数来尝试重新加载配置文件。
这段代码通过监听 SIGHUP
信号,调用 reloadConfig
函数来重新加载配置文件。reloadConfig
函数的实现逻辑如下:
-
-
-
- 首先,它会尝试获取配置文件的锁,以确保在重新加载过程中不会有其他并发操作。
-
然后,它会调用
config.LoadFile
函数来读取并解析新的配置文件。 - 接着,它会验证新配置的有效性,并更新内部配置快照。
- 最后,它会释放锁并返回结果。
-
-
2.2.2、HTTP POST请求
除了发送SIGHUP
信号外,Prometheus
还支持通过HTTP
POST
请求来触发配置文件的重新加载。这个功能主要在web/api_v1.go
文件中实现。
当用户向Prometheus
的特定端点发送POST
请求时,Prometheus
会调用reloadHandler
函数来处理该请求。reloadHandler
函数的实现逻辑与reloadConfig
函数类似,也是先获取锁,然后读取并解析新的配置文件,验证配置的有效性,并更新内部配置快照。最后,它会释放锁并返回结果给用户。
通常情况下,使用方式如下:
1 | POST /-/reload |
2.3、重新加载过程
当Prometheus收到重新加载配置文件的请求时,reloadConfig
函数负责实际的配置文件重新加载工作。它会读取新的配置文件,并将新的配置应用到各个组件中。
关键步骤包括:
- 读取新配置文件:Prometheus会读取指定路径下的新配置文件。这个步骤主要在config/config.go的LoadFile函数中实现,它会检查文件的存在性和可读性,并读取文件内容。
- 解析新配置:接下来,Prometheus会解析新配置文件的内容。这个步骤也在LoadFile函数中实现,它会调用Load函数将YAML格式的配置文件解析为内部使用的数据结构。
- 验证新配置:在解析完新配置后,Prometheus会对其进行验证。这个步骤会在解析函数之后进行,它会检查配置中是否存在错误或不一致之处,并确保新配置与现有系统状态兼容。
- 更新规则管理器、Scrape 管理器、通知管理器、openTSDB等组件的配置:如果新配置通过验证,Prometheus会将其应用到系统中。这个步骤会更新内部配置快照,并调整相关监控任务以反映新的配置要求。这个步骤主要在cmd/prometheus/main.go的reloadConfig函数或web/api_v1.go的reloadHandler函数中实现。
- 记录日志:在整个重新加载过程中,Prometheus会记录相关日志信息。这有助于用户跟踪配置更新的进度和结果,并在出现问题时进行故障排查。日志记录主要在各个相关函数中使用log.Logger接口来实现。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具