3

prome02.png

服务发现在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中正在运行的服务。

可想而知,在目前云原生环境下,应用具备高度弹性,通过静态配置监控目标的行为是多么的低效。

Hotreload

当然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轮询监控数据。

bolg_sd_mutil_cluster.png

当然目前官方对于增加新的服务发现方式比较慎重,与Alertmanager 通知类型情况类似,官方不希望新的不稳定服务发现方式会影响Prometheus自身的稳定性。

能够与其他SD机制(例如Docker Swarm)集成是源源不断的需求。为了解决这个问题,最近,Prometheus存储库中的文档目录进行了一些小的代码更改,并提供了一个示例,以实现自定义服务发现集成,而无需将其合并到Prometheus主分支中。

例如,我在实际落地Prometheus的工程中,增加了下面两种服务发现方式:

  • 基于负载均衡器服务自动发现:根据负载均衡器的地址,获取后端服务器组。在Cloud上,后端服务器组大多要配置HPA,决定了需要动态的发现。
  • 基于自动伸缩组的服务自动发现:有些任务型工作负载,根据工作负载来扩缩worker节点数,比如根据消息队列的消息堆积数,来扩缩消费者的数量。此时根据自动伸缩组,获取实际运行的Worker节点。

如何实现自定义服务发现?

官方推荐的方式:按照官方的接口约定,实现新的服务发现方式,将发现的目标和标签写到文件中,然后结合prometheus本身支持的file_sd方式。Prometheus会定期到指定文件中获取最新的目标。

如下所示:

scrape_configs:
  - job_name: "custom-sd"
    scrape_interval: "15s"
    file_sd_configs:
    - files:
      - /path/to/custom_sd.json

Adapter

首先了解一下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写入输出文件中。

自定义SD实现

现在我们要实际使用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的方式,对于容器化部署的业务,更加简单。


iyacontrol
1.4k 声望2.7k 粉丝

专注kubernetes,devops,aiops,service mesh。