Prometheus Service Discovery 设计与实现指南:从接口规范到 VictoriaMetrics 落地实践
2026/9/14 4:48:37 网站建设 项目流程

Prometheus Service Discovery 设计与实现指南:从接口规范到 VictoriaMetrics 落地实践

【免费下载链接】VictoriaMetricsVictoriaMetrics: fast, cost-effective monitoring solution and time series database项目地址: https://gitcode.com/GitHub_Trending/vi/VictoriaMetrics

导读

本文以 Prometheus 官方 Service Discovery(服务发现,简称 SD)组件设计文档为主体,系统讲解"什么样的机制才适合做成 SD""SD 如何向 Prometheus 映射元数据""如何实现一个DiscovererConfig接口"以及"新增 SD 的检查清单"等核心问题。当前仓库(VictoriaMetrics)通过vendor/github.com/prometheus/prometheus/discovery/完整引入该组件,并在 lib/promscrape/discovery/ 下实现了 20 余种 Prometheus 兼容的 SD 机制(Consul、Kubernetes、EC2、DNS、Docker、file_sd 等),本文将结合这些真实源码与实践文档,帮助读者既理解 SD 的抽象模型,又能落地到 VictoriaMetrics / vmagent 的抓取配置实战中。

一、Service Discovery 在监控体系中的位置

在 Prometheus 兼容的抓取架构中,抓取器需要知道"去抓谁"。scrape_configs中每个job_name下的*_sd_configs小节负责动态回答这个问题:它从各类基础设施(云厂商、服务注册中心、编排系统、DNS、文件等)中发现一批机器或服务的地址,把结果转成统一的 target 元数据,再交给 relabeling 加工后形成最终的抓取目标。

VictoriaMetrics 的单机版与 vmagent 均通过-promscrape.config指向的配置文件支持全部 Prometheus 兼容 SD,完整列表见 docs/victoriametrics/sd_configs.md:azure_sd_configsconsul_sd_configsconsulagent_sd_configsdigitalocean_sd_configsdns_sd_configsdocker_sd_configsdockerswarm_sd_configsec2_sd_configseureka_sd_configsfile_sd_configsgce_sd_configshetzner_sd_configshttp_sd_configskubernetes_sd_configskuma_sd_configslinode_sd_configsmarathon_sd_configsnomad_sd_configsopenstack_sd_configsovhcloud_sd_configspuppetdb_sd_configsstatic_configsvultr_sd_configsyandexcloud_sd_configs

一个值得注意的细节:VictoriaMetrics 不支持在配置文件里写refresh_interval,而是统一用命令行 flag(如-promscrape.consulSDCheckInterval=60s)控制各类 SD 的刷新周期,详情同样在 docs/victoriametrics/sd_configs.md 中说明。

二、什么才是一个合理的 SD 机制

Prometheus 对"是否值得做成原生 SD"有一套明确的判断标准,核心诉求是集成基础设施里已有的服务发现方式,而不是发明新方式

  • 机制应当成熟且被广泛使用:一个 SD 至少应在多个组织中实际使用,能发现"运行在某处的机器和/或服务"。此外,自解冻新 SD 引入限制以来,新实现还要求有具备 push 权限的专职维护者。
  • 不做全新或变种的发现机制:不应为了绕开用户缺乏 SD 或配置管理基础设施的现状而发明新的发现途径。
  • "发现同类软件"不算服务发现:例如向一个 Kafka 或 Cassandra 节点询问其他节点,这不属于服务发现;决定"哪台机器成为 Kafka 节点"的是机器数据库或配置管理系统,那才是应该对接的 SD。
  • 高度定制化的场景交给file_sd:Prometheus 的哲学是"对无限变化的事物提供一个通用机制"(与 alertmanager webhook、remote read/write、node_exporter textfile collector 一脉相承)。凡是需要对接关系型数据库等极端定制逻辑的场景,一律推荐通过file_sd接入;像 Chef 这类配置管理系统,惯用做法也是用其模板能力把目标写成一个文件再交给file_sd读取。

VictoriaMetrics 延续了这一约定:file_sd_configs是配置解析中的一等公民,其实现定义在 lib/promscrape/config.go:

// FileSDConfig represents file-based service discovery config. type FileSDConfig struct { Files []string `yaml:"files"` // `refresh_interval` is ignored. See `-promscrape.fileSDCheckInterval` }

file_sd只需声明files文件列表(支持 glob),刷新周期由-promscrape.fileSDCheckInterval统一控制;配置加载时通过getFileSDScrapeWork(lib/promscrape/config.go)把每个文件中的 target 展开成 ScrapeWork。

三、从 SD 到 Prometheus 的元数据映射模型

SD 的通用原则是:把发现机制中所有可能有用的信息都提取出来,具体取舍交给用户用 relabeling 决定。这些信息统称为 metadata(元数据)。

3.1 标签命名与暴露约定

  • 元数据以 key/value(标签)形式暴露在目标上,key 统一加前缀__meta_<sdname>_<key>
  • 每个目标必须有一个__address__标签,值为host:port优先使用 IP 地址以避免 DNS 解析;
  • 除上述两类标签外,不应暴露其他标签名。

VictoriaMetrics 对这套模型有完整落地。以 Consul 为例,其配置结构见 lib/promscrape/discovery/consul/consul.go,而每个发现目标可用的元数据标签完整列出在 docs/victoriametrics/sd_configs.md,包括__meta_consul_address__meta_consul_dc__meta_consul_health__meta_consul_tag_<tagname>等;所有 SD 的 meta 标签在 relabeling 完成后会被清理——lib/promscrape/config.go 的注释明确指出"Remove labels starting from__meta_prefix",这正对应本文开头提到的生命周期约定。

3.2 数组、映射与多端口目标的规范化

  • 数组:合并成单个标签,值以逗号分隔,并在首尾也加上逗号。例如[a, b, c]变成,a,b,c,。由于 relabeling 正则默认全量锚定,这种写法让.*,a,.*无论a出现在列表何处都能正确匹配。规范范例是__meta_consul_tags
  • 映射/哈希(key/value 对):全部加前缀暴露为标签。例如 EC2 的 tag 会产生__meta_ec2_tag_Description=mydescription。标签名只允许[_a-zA-Z0-9],非法字符须替换为下划线。
  • 多端口目标:a) 暴露为列表;b) 具名端口暴露为映射;c) 每个端口各自作为独立 target。Kubernetes SD 采用"每端口一目标"的方式,a) 与 b) 可以组合。
  • 机器型 SD(OpenStack、EC2,部分程度上的 Kubernetes)可能有多块网卡,目前只上报第一块/主网卡的信息即可。

3.3 其他实现考量

  • 全量倾倒 + 可选过滤:SD 的设计意图是把所有可能的目标全部给出(例如 EC2 SD 的典型用法是把整个 region 的实例一次拿回,在一个scrape_config内完成所有工作)。当规模很大而只关心其中一小部分时,允许利用 SD 自身机制提供过滤(如 EC2DescribeInstancesFilter),但要意识到这仅是性能优化——同样的过滤必须能用 relabeling 独立完成;Prometheus 不为发明新的目标过滤方式,只透传 SD 自带的功能。
  • 配置必须全部来自配置文件:SD 实现不应通过读取环境变量或文件来获取配置(EC2 的 SDK 依赖环境变量即是一个反例警示)。VictoriaMetrics 的各 SD 配置同样遵循此约定,配置字段内联HTTPClientConfigProxyClientConfig等统一结构。
  • 警惕速率限制:有些 SD 的 API 速率限制低到无法实用(文档中明确提到 Amazon ECS 因此被拒)。
  • 多类型 SD 的选择:若一个系统提供多种不同类型的 SD,应用配置项选择当前使用哪一种,而不是用一个"大杂烩 SD"返回全部再靠 relabeling 挑拣(目前只有 Kubernetes 出现这种情况)。
  • 失败即中止:与 SD 通信失败时应中止而非返回部分数据,宁可基于陈旧目标工作,也不要基于残缺/错误元数据工作。
  • 不返回敏感信息:SD 获得的信息在安全上不被视为敏感,但绝不能在 metadata 中返回密钥——任何能访问 Prometheus 服务的人都能看到它们。

四、编写一个 SD 机制:Discoverer 接口与 TargetGroup

4.1 数据载体:targetgroup.Group

SD 发现的相似目标会被分组,以 target group 列表的形式下发给 Prometheus。其定义在 vendor/github.com/prometheus/prometheus/discovery/targetgroup/targetgroup.go:

// Group is a set of targets with a common label set(production , test, staging etc.). type Group struct { // Targets is a list of targets identified by a label set. Each target is // uniquely identifiable in the group by its address label. Targets []model.LabelSet // Labels is a set of labels that is common across all targets in the group. Labels model.LabelSet // Source is an identifier that describes a group of targets. Source string }

可以看到Group由三部分组成:Targets(每个目标一个 LabelSet)、Labels(组内所有目标共有的标签,如job)、Source(组标识符)。同一 SD 实例发出的所有 target group 的Source必须全局唯一——它是 Manager 追踪增删改的钥匙。

4.2 核心接口:Discoverer

一个 SD 机制必须实现Discoverer接口(定义在 vendor/github.com/prometheus/prometheus/discovery/discovery.go):

// Discoverer provides information about target groups. It maintains a set // of sources from which TargetGroups can originate. Whenever a discovery provider // detects a potential change, it sends the TargetGroup through its channel. type Discoverer interface { // Run hands a channel to the discovery provider (Consul, DNS, etc.) through which // it can send updated target groups. It must return when the context is canceled. // It should not close the update channel on returning. Run(ctx context.Context, up chan<- []*targetgroup.Group) }

Prometheus 会调用提供者的Run()来初始化发现机制,机制随即把全部target group 发进 channel;之后持续监听变化,每次更新可以发送全部目标组,也可以只发送变化/新增的目标组,Manager对两种情况都能处理。

4.3 发送语义:全量、变更与清空

假设某发现机制首次检索得到两个 group(一个Source: file1对应 MySQL,一个Source: file2对应 Postgres):

[]targetgroup.Group{ { Targets: []model.LabelSet{ { "__instance__": "10.11.150.1:7870", "hostname": "demo-target-1", "test": "simple-test", }, { "__instance__": "10.11.150.4:7870", "hostname": "demo-target-2", "test": "simple-test", }, }, Labels: model.LabelSet{ "job": "mysql", }, "Source": "file1", }, { Targets: []model.LabelSet{ { "__instance__": "10.11.122.11:6001", "hostname": "demo-postgres-1", "test": "simple-test", }, { "__instance__": "10.11.122.15:6001", "hostname": "demo-postgres-2", "test": "simple-test", }, }, Labels: model.LabelSet{ "job": "postgres", }, "Source": "file2", }, }

分组方式是实现相关的,甚至可以是"每目标一组"。更新时只需下发发生变化的整个 group。例如demo-postgres-2消失后,下发:

&targetgroup.Group{ Targets: []model.LabelSet{ { "__instance__": "10.11.122.11:6001", "hostname": "demo-postgres-1", "test": "simple-test", }, }, Labels: model.LabelSet{ "job": "postgres", }, "Source": "file2", }

若某个 group 的所有目标全部消失,则下发Targets为空的 group,例如所有job: postgres目标都没了:

&targetgroup.Group{ Targets: nil, "Source": "file2", }

这种"空 Targets 即删除信号"的协议,与 lib/promscrape/config.go 中file_sd_configs的空目标处理逻辑相互印证:抓取配置会在文件内容变化时重算 ScrapeWork 集合,消失的目标随之被移除。

五、让 Prometheus 认识你的 SD:Config 接口与注册机制

SD 机制准备好之后,还必须帮助 Prometheus"发现"它:实现discovery.Config接口,并在包内init函数中用discovery.RegisterConfig完成注册。

5.1 Config 接口与 DiscovererOptions

type Config interface { // Name returns the name of the discovery mechanism. Name() string // NewDiscoverer returns a Discoverer for the Config // with the given DiscovererOptions. NewDiscoverer(DiscovererOptions) (Discoverer, error) // NewDiscovererMetrics returns the metrics used by the service discovery. NewDiscovererMetrics(prometheus.Registerer, RefreshMetricsInstantiator) DiscovererMetrics } type DiscovererOptions struct { Logger *slog.Logger // A registerer for the Discoverer's metrics. Registerer prometheus.Registerer HTTPClientOptions []config.HTTPClientOption }

(注:在当前仓库 vendor 的 discovery.go 中,DiscovererOptions还包含Metrics DiscovererMetricsSetName string字段,说明该组件后续版本又补充了指标注册与集合命名能力。)

Name()的返回值应当简短、具描述性、全小写且唯一,它有两个用途:作为 Logger 的标签;作为该 SD 在scrape_config/alertmanager_config中 YAML 键的一部分(即${NAME}_sd_configs)。

5.2 注册机制的源码实现

注册的核心逻辑在 vendor/github.com/prometheus/prometheus/discovery/registry.go:

// RegisterConfig registers the given Config type for YAML marshaling and unmarshaling. func RegisterConfig(config Config) { registerConfig(config.Name()+"_sd_configs", reflect.TypeOf(config), config) } func init() { // N.B.: static_configs is the only Config type implemented by default. // All other types are registered at init by their implementing packages. elemTyp := reflect.TypeFor[*targetgroup.Group]() registerConfig(staticConfigsKey, elemTyp, StaticConfig{}) }

注意init()中的注释:默认只有static_configs一种 Config 类型,其余全部由各自实现包在init阶段注册。注册时通过反射动态构造字段,使Configs的 YAML 编解码能把形如consul_sd_configskubernetes_sd_configs的键自动映射到对应类型(见 discovery.go 中Configs.UnmarshalYAML的反射实现)。若出现同名注册,registerConfig会直接panic,从机制上杜绝命名冲突。

static_configs同样是一个 Config,其Name()返回"static"NewDiscoverer返回一个一次性发送全部静态组的 discoverer(discovery.go)。VictoriaMetrics 侧对应的静态配置定义在 lib/promscrape/config.go,并支持通过文件加载静态目标(loadStaticConfigs,支持 HTTP 读取与环境模板替换)。

5.3 Manager:接收与同步

Manager负责启动各 provider、汇总其 channel 输出并周期性地把最新 target group 集合同步出去。其实现细节见 vendor/github.com/prometheus/prometheus/discovery/manager.go:默认以 5 秒(updatert)为周期把targets快照写入syncCh,并维护poolKey{setName, provider}粒度的 provider 生命周期。这意味着 SD 的"发现-变更"与下游"抓取目标重算"是解耦的异步流程。

六、新增一个 SD 的检查清单

原文档给出了一份"易踩坑"清单,逐条对应到 VictoriaMetrics 仓库可以找到实锤:

  1. DeepEqual 校验:把新配置加入config/testdata/conf.good.yml及相关测试,确保配置可以被深度比较(VictoriaMetrics 侧对应 lib/promscrape/config_test.go 对抓取配置的严格解析测试)。
  2. 目录相关配置:若配置直接或间接包含文件路径(如 TLSConfig、HTTPClientConfig 字段),必须实现config.DirectorySetter,以支持-promscrape.config相对路径基准目录的拼接。VictoriaMetrics 中每个 SD 的GetLabels(baseDir string)方法都接收基准目录参数,例如 lib/promscrape/discovery/kuma/kuma.go 与 lib/promscrape/discovery/consul/consul.go。
  3. 从 install 包导入:SD 包必须在prometheus/discovery/install中被导入,main通过导入 install 包注册全部内置 SD。VictoriaMetrics 的做法异曲同工——所有 SD 实现在 lib/promscrape/config.go 中被统一 import,从而进入抓取配置解析器。
  4. 文档登记:在docs/configuration/configuration.md<scrape_config><alertmanager_config>两处列出新 SD。VictoriaMetrics 则要求在 docs/victoriametrics/sd_configs.md 中登记并给出配置示例与 meta 标签清单。

七、VictoriaMetrics 中的 SD 实现范式

从源码结构看,VictoriaMetrics 的每个 SD 都遵循一套高度一致的范式,可以看作"Config 接口"思路在抓取器侧的工程化落地:

  • SDConfig 结构体:声明该 SD 的全部 YAML 字段。例如 Consul 支持servertokendatacenternamespacepartitionschemeservicestagsnode_metafilter等(lib/promscrape/discovery/consul/consul.go);Kuma 则只有serverclient_id(lib/promscrape/discovery/kuma/kuma.go)。凡是refresh_intervalfetch_timeout等字段,统一注释掉并在注释中说明由命令行 flag 提供。
  • GetLabels(baseDir):返回[]*promutil.Labels,把 API 返回的实例/服务信息转换为带__meta_*前缀的标签集合。这是"提取全部有用信息"原则的直接体现。
  • MustStop():通过configMap.Delete(sdc)释放 API 客户端等资源。
  • SDCheckInterval flag:每个 SD 在包内声明一个-promscrape.<name>SDCheckIntervalflag,例如 Kuma 的-promscrape.kumaSDCheckInterval默认 30s。这与"配置来自配置文件"原则并行不悖——刷新频率属于部署参数,故由命令行统一管理。

各 SD 的单元测试文件与实现一一对应(如consul_test.gokubernetes/pod_test.goec2/instance_test.go等),覆盖了从 API 响应解析到 meta 标签生成的全过程,可作为编写新 SD 时的参考样板。

八、实战:在 vmagent 中启用一个 SD

以最简单的file_sd_configs为例演示完整落地路径。配置文件(可对照 lib/promscrape/testdata/scrape_config_files/1.yml 与 lib/promscrape/testdata/file_sd_1.yml):

scrape_configs: - job_name: job1 static_configs: - targets: [foo, bar]

static_configsfile_sd_configs在 lib/promscrape/config.go 中分别通过getStaticScrapeWorkgetFileSDScrapeWork展开;file-based SD 的目标还会附带__meta_filepath标签(lib/promscrape/config.go),便于 relabeling 按来源文件区分目标。

启用抓取:

vmagent -promscrape.config=/path/to/prometheus.yml \ -remoteWrite.url=http://victoria-metrics:8428/api/v1/write

单机版 VictoriaMetrics 也可直接抓取(不写 remoteWrite):

victoria-metrics -promscrape.config=/path/to/prometheus.yml

排障时可用内置的 dry-run 严格校验配置,lib/promscrape/config.go 中的定义说明:

vmagent -promscrape.config=/path/to/prometheus.yml -promscrape.config.dryRun=true

若希望静默跳过不支持字段而非报错,可将-promscrape.config.strictParse设为false。各类 SD 的具体配置字段与 meta 标签,均以 docs/victoriametrics/sd_configs.md 与 docs/victoriametrics/relabeling.md 为准。

结语

Service Discovery 的价值在于把"基础设施里已有的发现能力"翻译成统一的__meta_*标签模型,从而让 relabeling 成为唯一的目标加工入口。理解了Discoverer/Config两个接口与 target group 的增量协议,就能判断一个机制是否适合做成 SD、以及如何正确地把它集成进 Prometheus 生态;而 VictoriaMetrics 在lib/promscrape/discovery/下 20 余个 SD 的高度一致实现,则为"按规范编写新 SD"提供了可直接参考的工程范本。

【免费下载链接】VictoriaMetricsVictoriaMetrics: fast, cost-effective monitoring solution and time series database项目地址: https://gitcode.com/GitHub_Trending/vi/VictoriaMetrics

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询