grafana agent 动态配置目前属于一个体验特性,但是设计上利用了gomplate 一个强大的模版引擎工具
参考配置
- 运行配置参考
agentv2:
image: grafana/agent:main
ports:
- 12345:12345
- 12347:12347
entrypoint:
- /bin/agent
- -config.file.type=dynamic
- -server.http.address=0.0.0.0:12345
- -config.file=file:///etc/agent-config/agent.yaml # 动态配置默认加载地址
- -metrics.wal-directory=/tmp/agent/wal
- -enable-features=dynamic-config,integrations-next # 需要开启的特性
- -config.enable-read-api
volumes:
- ./agentv2.yaml:/etc/agent-config/agent.yaml
- ./agent-data:/etc/agent/data
- ./confs:/opt/confs # 挂载的配置文件
- ./logs:/var/log
- 配置参考
比如metrics ,需要移除metrics
global:
scrape_interval: 10s
remote_write:
- url: http://victoriametrics:8428/api/v1/write
configs:
- name: default
scrape_configs:
- job_name: avalanche
static_configs:
- targets: ['${AVALANCHE_HOST:-localhost:9001}']
log 配置
configs:
- name: default
positions:
filename: /tmp/positions.yaml
scrape_configs:
- job_name: varlogs
static_configs:
- targets: [localhost]
labels:
job: varlogs
__path__: /var/log/*log
clients:
- url: http://loki:3100/loki/api/v1/push
filter 配置 (agentv2.yaml)
template_paths:
- file:///opt/confs # 基于文件格式
# Filters allow you to override the default naming convention
agent_filter: "agent-*.yml" # defaults to agent-*.yml
server_filter: "server-*.yml" # defaults to server-*.yml
metrics_filter: "metrics-*.yml" # defaults to metrics-*.yml
#metrics_instance_filter: string # defaults to metrics_instances-*.yml
integrations_filter: "integrations-*.yml" # defaults to integrations-*.yml
logs_filter: "logs-*.yml" # defaults to logs-*.yml
traces_filter: "traces-*.yml" # defaults to traces-*.yml
内部机制
grafana agent 动态配置实际上上利用了gomplate,但是目前来说似乎只能是第一次加载的时候,并不能实时reload
处理机制上核心是开发的一些filter,如果需要加载其他数据源推荐配置datasource,默认可以通过本地文件
filter 处理部分
// ProcessConfigs loads the configurations in a predetermined order to handle functioning correctly.
func (c *DynamicLoader) ProcessConfigs(cfg *Config) error {
if c.cfg == nil {
return fmt.Errorf("LoadConfig or LoadConfigByPath must be called")
}
var returnErr error
err := c.processAgent(cfg)
returnErr = errorAppend(returnErr, err)
serverConfig, err := c.processServer()
returnErr = errorAppend(returnErr, err)
if serverConfig != nil {
cfg.Server = *serverConfig
}
metricConfig, err := c.processMetrics()
returnErr = errorAppend(returnErr, err)
if metricConfig != nil {
cfg.Metrics = *metricConfig
}
instancesConfigs, err := c.processMetricInstances()
returnErr = errorAppend(returnErr, err)
cfg.Metrics.Configs = append(cfg.Metrics.Configs, instancesConfigs
logsCfg, err := c.processLogs()
returnErr = errorAppend(returnErr, err)
if logsCfg != nil {
cfg.Logs = logsCfg
}
traceConfigs, err := c.processTraces()
returnErr = errorAppend(returnErr, err)
if traceConfigs != nil {
cfg.Traces = *traceConfigs
}
integrations, err := c.processIntegrations()
returnErr = errorAppend(returnErr, err)
cfg.Integrations.ExtraIntegrations = append(cfg.Integrations.ExtraIntegrations, integrations
return returnErr
}
不同filter 处理(log 参考)
func (c *DynamicLoader) processLogs() (*logs.Config, error) {
var returnError error
found := 0
var cfg *logs.Config
// 基于配置的模版进行模版数据的处理
for _, path := range c.cfg.TemplatePaths {
filesContents, err := c.retrieveMatchingFileContents(path, c.cfg.LogsFilter, "logs")
returnError = errorAppend(returnError, err)
found = len(filesContents) + found
if len(filesContents) == 1 {
cfg = &logs.Config{}
err = yaml.Unmarshal([]byte(filesContents[0]), cfg)
returnError = errorAppend(returnError, err)
}
}
// 只支持一个文件,实际上官方文档也说明了log 只支持一个
if found > 1 {
returnError = errorAppend(returnError, fmt.Errorf("found %d logs templates; expected 0 or 1", found))
}
return cfg, returnError
}
datasource 的加载(基于了gomplate的能力)
// DynamicLoader is used to load configs from a variety of sources and squash them together.
// This is used by the dynamic configuration feature to load configurations from a set of templates and then run them through
// gomplate producing an end result.
type DynamicLoader struct {
loader *loader.ConfigLoader
mux fsimpl.FSMux
cfg *LoaderConfig
}
// NewDynamicLoader instantiates a new DynamicLoader.
func NewDynamicLoader() (*DynamicLoader, error) {
// 此处基于了hairyhenderson 提供的vfs 能力,目前只支持本地以及对象存储文件,实际上hairyhenderson/go-fsimpl 还提供其他费事(git,http。。。。)
return &DynamicLoader{
mux: newFSProvider(),
}, nil
}
// LoadConfig loads an already created LoaderConfig into the DynamicLoader.
func (c *DynamicLoader) LoadConfig(cfg LoaderConfig) error {
sources := make(map[string]*data.Source)
for _, v := range cfg.Sources {
sourceURL, err := url.Parse(v.URL)
if err != nil {
return err
}
sources[v.Name] = &data.Source{
URL: sourceURL,
Alias: v.Name,
}
}
// Set Defaults
if cfg.IntegrationsFilter == "" {
cfg.IntegrationsFilter = "integrations-*.yml"
}
if cfg.AgentFilter == "" {
cfg.AgentFilter = "agent-*.yml"
}
if cfg.ServerFilter == "" {
cfg.ServerFilter = "server-*.yml"
}
if cfg.MetricsFilter == "" {
cfg.MetricsFilter = "metrics-*.yml"
}
if cfg.MetricsInstanceFilter == "" {
cfg.MetricsInstanceFilter = "metrics_instances-*.yml"
}
if cfg.LogsFilter == "" {
cfg.LogsFilter = "logs-*.yml"
}
if cfg.TracesFilter == "" {
cfg.TracesFilter = "traces-*.yml"
}
cl := loader.NewConfigLoader(context.Background(), sources)
c.loader = cl
c.cfg = &cfg
return nil
}
说明
以上是一个简单的说明,详细的可以多看看源码
参考资料
https://github.com/grafana/agent/blob/987c214431af8abae7e13468468b99d21d2df3e3/pkg/config
https://github.com/hairyhenderson/gomplate
https://grafana.com/docs/agent/latest/configuration/dynamic-config/