grafana agent 动态配置内部机制简单说明

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/

posted on 2022-11-08 21:49  荣锋亮  阅读(533)  评论(0编辑  收藏  举报

导航