服务发现在Wikipedia的描述是:
Service discovery is the automatic detection of devices and services offered by these devices on a computer network.
换句话说,它允许应用程序动态发现服务,而不是在应用程序配置中静态定义服务。
对于Prometheus,可以使用多种方法进行服务发现,包括云提供商API(例如AWS,Azure,GCE,Openstack),基于DNS的发现(使用SRV记录)以及查询Kubernetes API中正在运行的服务。
可想而知,在目前云原生环境下,应用具备高度弹性,通过静态配置监控目标的行为是多么的低效。
当然Prometheus 提供了Hotreload机制,在配置文件变更的时候,可以通知Prometheus进行reload。而且在reload的过程中,服务不会停机
热更新的加载方法有两种:
当然新版本的Prometheus 的热加载功能默认是关闭的,你需要在Prometheus的启动参数中,添加如下参数:
--web.enable-lifecycle
但是这种方式,并不是最优雅的,你需要维护整个配置文件。
除静态配置之外,Prometheus 已经支持了如下的服务发现方式:
# List of Azure service discovery configurations. azure_sd_configs: [ - <azure_sd_config> ... ] # List of Consul service discovery configurations. consul_sd_configs: [ - <consul_sd_config> ... ] # List of DNS service discovery configurations. dns_sd_configs: [ - <dns_sd_config> ... ] # List of EC2 service discovery configurations. ec2_sd_configs: [ - <ec2_sd_config> ... ] # List of OpenStack service discovery configurations. openstack_sd_configs: [ - <openstack_sd_config> ... ] # List of file service discovery configurations. file_sd_configs: [ - <file_sd_config> ... ] # List of GCE service discovery configurations. gce_sd_configs: [ - <gce_sd_config> ... ] # List of Kubernetes service discovery configurations. kubernetes_sd_configs: [ - <kubernetes_sd_config> ... ] # List of Marathon service discovery configurations. marathon_sd_configs: [ - <marathon_sd_config> ... ] # List of AirBnB's Nerve service discovery configurations. nerve_sd_configs: [ - <nerve_sd_config> ... ] # List of Zookeeper Serverset service discovery configurations. serverset_sd_configs: [ - <serverset_sd_config> ... ] # List of Triton service discovery configurations. triton_sd_configs: [ - <triton_sd_config> ... ]
下图是一个Prometheus + consul sd 的架构。对于线上环境我们可能会划分为:dev, stage, prod不同的集群。每一个集群运行多个主机节点,每个服务器节点上运行一个Node Exporter实例。Node Exporter实例会自动注册到Consul中,而Prometheus则根据Consul返回的Node Exporter实例信息动态的维护Target列表,从而向这些Target轮询监控数据。
当然目前官方对于增加新的服务发现方式比较慎重,与Alertmanager 通知类型情况类似,官方不希望新的不稳定服务发现方式会影响Prometheus自身的稳定性。
能够与其他SD机制(例如Docker Swarm)集成是源源不断的需求。为了解决这个问题,最近,Prometheus存储库中的文档目录进行了一些小的代码更改,并提供了一个示例,以实现自定义服务发现集成,而无需将其合并到Prometheus主分支中。
例如,我在实际落地Prometheus的工程中,增加了下面两种服务发现方式:
官方推荐的方式:按照官方的接口约定,实现新的服务发现方式,将发现的目标和标签写到文件中,然后结合prometheus本身支持的file_sd方式。Prometheus会定期到指定文件中获取最新的目标。
如下所示:
scrape_configs: - job_name: "custom-sd" scrape_interval: "15s" file_sd_configs: - files: - /path/to/custom_sd.json
首先了解一下adapter.go文件,您可以复制此文件以实现自定义SD实现。
// Adapter runs an unknown service discovery implementation and converts its target groups // to JSON and writes to a file for file_sd. type Adapter struct { ctx context.Context disc discovery.Discoverer groups map[string]*customSD manager *discovery.Manager output string name string logger log.Logger } // Run starts a Discovery Manager and the custom service discovery implementation. func (a *Adapter) Run() { go a.manager.Run() a.manager.StartCustomProvider(a.ctx, a.name, a.disc) go a.runCustomSD(a.ctx) }
Adapter利用 Discovery.Manager
在goroutine中启动自定义SD提供程序的Run函数。Manager有一个通道,自定义SD将向其发送更新。这些更新包含SD目标。groups字段包含所有目标和标签。
type customSD struct { Targets []string `json:"targets"` Labels map[string]string `json:"labels"` }
存在这个 customSD
结构主要是为了帮助我们将内部Prometheus targetgroup.Group
结构转换为JSON以用于 file_sd
格式。
运行时,Adapter将在通道上监听来自我们的自定义SD实现的更新,接收到更新后,它将解析 targetgroup.Groups
到另一个映射 [string]* customSD
中,并将其存储在Adapter,如果两者不同,我们将新组分配给Adapter结构,并将它们作为JSON写入输出文件中。
现在我们要实际使用Adapter来实现我们自己的自定义SD。完整的工作示例位于此处的 examples目录 中。
在这里,您可以看到我们导入Adapter代码“ github.com/prometheus/prometheus/documentation/examples/custom-sd/adapter”以及其他一些Prometheus库。为了编写自定义SD,我们需要一个实现Discoverer接口:
// 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. // // Discoverer does not know if an actual change happened. // It does guarantee that it sends the new TargetGroup whenever a change happens. // // Discoverers should initially send a full set of all discoverable TargetGroups. type Discoverer interface { // Run hands a channel to the discovery provider(consul,dns etc) through which it can send // updated target groups. // Must returns if the context gets canceled. It should not close the update // channel on returning. Run(ctx context.Context, up chan<- []*targetgroup.Group) }
我们实际上只需要实现一个函数 Run(ctx context.Context,up chan <-[] * targetgroup.Group)
。这是Adapter代码中的管理器将在goroutine中调用的函数。Run函数包含了知道何时退出的上下文,并传递了用于发送目标组更新的通道。
查看提供的示例中的Run函数,我们可以看到在另一个SD的实现中需要做的一些关键事情。
如果实际场景中,公司已经有统一的服务注册中心或是配置中心,那么完全可以自定义SD,这样的好处是,利用了现有的基础设施,实现无缝对接。
另外一点是,Prometheus 中kubernetes SD的方式,对于容器化部署的业务,更加简单。
欢迎关注我们的微信公众号,每天学习Go知识