vlambda博客
学习文章列表

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现

Choosing the Right Service Discovery

在处理动态环境时,手动维护目标文件不是一种选择。服务发现为您处理不断变化的基础架构的复杂性,确保没有服务或主机从裂缝中溜走。本章重点介绍如何利用 Prometheus 服务发现来减少应对不断变化的基础设施管理工作。

简而言之,本章将涵盖以下主题:

  • Test environment for this chapter
  • Running through the service discovery options
  • Using a built-in service discovery
  • Building a custom service discovery

Test environment for this chapter

在本章中,我们将专注于服务发现。为此,我们将部署两个新实例来模拟 Prometheus 使用流行的服务发现软件动态生成目标的场景。这种方法不仅可以让我们公开所需的配置,还可以验证一切如何协同工作。

我们将使用的设置类似于下图:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.1: Test environment for this chapter

Consul 的通常部署模式是在基础架构中的每个节点上以客户端模式运行代理,然后该代理将联系以服务器模式运行的 Consul 实例。此外,客户端实例充当 API 代理,因此 Prometheus Consul 服务发现通常使用 localhost 进行配置。然而,为了明确他们不同的职责,我们选择在一个虚拟机中只使用一个 Prometheus 实例,并在我们的测试环境中将一个 Consul 作为服务器运行在另一个虚拟机中。

在下一节中,我们将解释如何启动并运行测试环境。

Deployment

要启动一个新的测试环境,请进入本章的路径,相对于存储库根目录:

cd ./chapter12/

确保没有其他测试环境正在运行并启动本章的环境:

vagrant global-status
vagrant up

您可以使用以下命令验证测试环境的成功部署:

vagrant status

这将为您提供以下输出:

Current machine states:

prometheus                running (virtualbox)
consul                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed above with their current state. For more information about a specific VM, run `vagrant status NAME`.

当部署任务结束时,您将能够使用您最喜欢的支持 JavaScript 的 Web 浏览器验证主机上的以下端点:

服务

端点

普罗米修斯

http://192.168.42.10:9090

领事

http://192.168.42.11:8500

您应该能够使用以下命令之一访问所需的实例:

实例

命令

普罗米修斯

vagrant ssh prometheus

领事

vagrant ssh 领事

Cleanup

完成测试后,只需确保您在 ./chapter12/ 中并执行以下命令:

vagrant destroy -f

不用太担心 如果需要,您可以轻松地再次启动环境。

Running through the service discovery options

Prometheus 附带了几个开箱即用的发现集成。这些涵盖了应用程序和机器清单的大多数主流数据源,例如公共和私有云计算 API、VM 和容器编排系统、独立服务注册和发现系统等。对于 Prometheus 不直接支持的发现机制,可以通过使用文件系统和一些胶水代码的通用发现系统来完成集成,我们将在本章后面看到。

每个集成都以相同的方式工作 - 通过将所有发现的地址设置为目标,并将其关联的元数据设置为临时标签(如果没有重新标记以保留它们,则不会持久化)。对于每个发现的目标,__address__ 标签通常设置为服务地址和端口。这是相关的,因为这个标签是 Prometheus 用来连接到抓取目标的标签; instance 标签在未明确定义时默认使用 __address__ 值,但可以将其设置为任何其他更容易识别目标的值。

服务发现集成提供的元数据标签遵循 __meta_<服务发现名称>_ 的模式。还有一些 Prometheus 添加的标签,如 __scheme____metrics_path__,它们定义了抓取应该使用 HTTP 还是 HTTPS 以及分别配置要抓取的端点。

URL parameters are not supported in the metrics_path scrape configuration. Instead, these need to be set in the params configuration. This is covered in Chapter 5, Running a Prometheus Server.

以下部分概述了可用的发现选项,并提供了一些有关如何配置它们的示例,并附有它们生成的数据的屏幕截图。

Cloud providers

随着云基础架构的兴起,在这些环境中运行工作负载变得越来越普遍。这带来了一系列新的挑战;例如,短暂和高度动态的基础设施供应。可扩展性的易用性也是需要牢记的:在过去,可能需要几个月的时间来协商、商定支持合同、购买、部署和配置新硬件;如今,启动并运行一个新的实例队列只需几秒钟。借助自动扩展等技术,它会在您甚至不知道的情况下部署新实例,因此很难跟上变化。为了减轻密切关注这种云原生动态基础架构的负担,Prometheus 与基础架构即服务(IaaS)中的一些大玩家进行了开箱即用的集成市场,例如 Amazon Web Services、Microsoft Azure Cloud、Google Cloud Platform、OpenStack 和 Joyent。

以 Amazon Elastic Compute (EC2) 为例进行虚拟机发现,抓取作业的配置可以很简单,如下所示:

scrape_configs:
 - job_name: ec2_sd
   ec2_sd_configs:
    - region: eu-west-1
      access_key: ACCESSKEYTOKEN
      secret_key: 'SecREtKeySecREtKey+SecREtKey+SecREtKey'

其他云提供商会有不同的设置,但逻辑几乎相同。基本上,我们需要设置适当级别的凭据来查询云提供商 API,以便 Prometheus 发现集成可以使用生成目标所需的所有数据,以及它们相关的元数据。以下屏幕截图说明了与前面列出的配置类似但具有实际凭据的配置如何转换为一组目标:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.2: Prometheus /service-discovery endpoint depicting ec2_sd data

正如我们在前面的屏幕截图中看到的,EC2 发现将大量元数据标签附加到每个发现的目标。这些在重新标记阶段可用,因此您可以使用它们仅抓取正在运行的目标,将抓取从私有 IP 地址更改为公共 IP 地址,或将实例标签重命名为更友好的名称。

从发现过程中收集的这些信息要么定期刷新(刷新间隔可在服务发现级别配置),要么通过监视自动刷新,从而使 Prometheus 能够了解正在创建或删除的目标。

Container orchestrators

容器编排器是提取正在运行的服务以及在何处运行的理想场所,因为它们的工作是准确管理这些信息。因此,Prometheus 发现机制支持一些最广泛使用的容器编排平台,例如 Kubernetes 和 Marathon,Mesos 和 DC/OS 的容器编排平台。由于我们在本书的大部分示例中都使用了 Kubernetes,因此我们将专注于这个平台来解释这些类型的系统是如何工作的。

与 Prometheus 一样,Kubernetes 是 Cloud Native Computing Foundation (CNCF) 的毕业项目。虽然这并不意味着一个是专门为与另一个一起工作而创建的,但两者之间的联系是不可否认的。 Borg 和 Borgmon,谷歌的容器编排和监控系统,绝对是 Kubernetes 和 Prometheus 的灵感来源。为了解决对云原生平台(如 Kubernetes)的监控,其中变化率几乎是压倒性的,需要一组特殊的功能。 Prometheus 符合这些要求,例如有效地处理容器的短暂性。

Prometheus 服务发现集成通过 Kubernetes API 检索所有需要的数据,与集群的状态保持同步。由于可供查询的 API 对象的数量,Prometheus 的发现配置有角色的概念,可以是 nodeservicepod 端点入口。虽然解释 Kubernetes 核心概念超出了本书的范围,但我们可以快速了解每个角色用于发现的内容:node 用于收集构成 Kubernetes 集群的实际节点(例如例如,运行 kubelet 代理的虚拟机),因此可用于监控集群本身及其底层基础设施; Kubernetes 中的服务对象就像一个负载均衡器,而 service 将为您提供 - 每个配置服务的每个端口的单个端点,无论它是由一个或多个应用实例- ,仅用于黑盒监控; pod 用于发现单个 pod,与它们是否属于服务无关; endpoint 发现 Pod 中支持给定服务的主进程;最后,ingress,类似于 service,为一组应用程序实例返回面向外部的负载均衡器,因此只能用于端到端探测。

以下代码片段提供了一个示例,说明如何查询 pod,匹配具有标签 app 且匹配值 hey 的 pod:

scrape_configs:
  - job_name: kubernetes_sd
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - action: keep
        regex: hey
        source_labels:
          - __meta_kubernetes_pod_label_app

前面的配置生成以下屏幕截图中描述的数据,我们可以在其中看到通过 Kubernetes API 收集的所有元数据:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.3: Prometheus /service-discovery endpoint depicting kubernetes_sd data

这是可以做的一个非常小的例子。使用 Kubernetes 服务发现的配置通常广泛使用 relabel_configs 来过滤目标,重写 job 标签以匹配容器名称,并且通常基于围绕 Kubernetes 注释的约定。

Service discovery systems

随着服务数量的增长,将所有内容联系在一起变得越来越困难——无论是在服务被正确配置为相互联系的方面,还是运营商对系统行为的可见性方面。这些问题的一个常见解决方案是实施一个服务发现系统,该系统充当注册表,然后可以被软件客户端以及监控系统查询。

Prometheus 与少数主流服务发现系统无缝集成,目前支持 Consul、Nerve 和 ServerSets。直接与发现服务集成允许 Prometheus 始终拥有关于正在运行的内容和位置的最新视图,从而允许在创建服务实例时自动监控它们,直到它们被销毁。

Consul 是迄今为止最受欢迎的,因为它提供了一套完整的功能来实现服务发现和强大而易于使用的命令行工具和 API,并且易于扩展。让我们在示例中使用以下内容:

scrape_configs:
  - job_name: 'consul_sd'
    consul_sd_configs:
      - server: http://consul.prom.inet:8500
        datacenter: dc1
    relabel_configs:
      - source_labels: [__meta_consul_service]
        target_label: job
      - source_labels: [job, __address__]
        regex: "consul;([^:]+):.+"
        target_label: __address__
        replacement: ${1}:9107

前面的示例转换为以下屏幕截图,我们不仅可以看到生成的标签,还可以看到目标的定义:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.4: Prometheus /service-discovery endpoint depicting consul_sd data

前面的示例显示了一个从 Consul 服务器中注册的所有可用服务收集数据的工作配置,使用 relabel_configs 将目标的 job 标签重写为服务名称而不是job_name。这意味着在 Consul 中注册的每个应用程序实例都会被自动拾取为抓取目标并正确分配正确的作业名称。此外,当服务命名为 Consul 时,最后一个重新标记规则将目标端口更改为 9107,从而将目标从 Consul 本身更改为它的导出器。

DNS-based service discovery

这种类型的服务发现依赖于 DNS 来收集数据。它通过定义将定期查询以获取目标的域名列表来工作。用于解析的名称服务器在 /etc/resolv.conf 中查找。这种发现集成除了支持 A 和 AAAA DNS 记录外,还能够查询 SRV 记录,这些记录也为服务提供端口:

~$ dig SRV hey.service.example.inet
...
;; QUESTION SECTION:
;hey.service.example.inet. IN SRV

;; ANSWER SECTION:
hey.service.example.inet. 0 IN SRV 1 1 8080 server01.node.example.inet.

;; ADDITIONAL SECTION:
server01.node.example.inet. 0 IN A 192.168.42.11
server01.node.example.inet. 0 IN TXT "squad=purple"
...

我们可以看到,在这个例子中通过查询hey.service.example.inet的SRV记录,我们得到了服务位置server01.node.example.inet和端口8080。我们还获得了带有服务 IP 地址的 A 记录和带有一些元数据的 TXT 记录。

以下代码片段说明了使用此 DNS 服务发现集成的示例抓取配置。它通过使用之前的域 hey.service.example.inet 来做到这一点:

scrape_configs:
  - job_name: 'dns_sd'
    dns_sd_configs:
      - names:
        - hey.service.example.inet

返回的 SRV 记录将被转换为新的目标。 Prometheus 不支持 RFC 6763 中指定的高级 DNS-SD,它允许在关联的 TXT 记录中传输元数据(如之前的 dig 命令中所示)。这意味着使用此方法只能发现服务地址和端口。我们可以在以下屏幕截图中看到哪些发现的标签可用:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.5: Prometheus /service-discovery endpoint depicting dns_sd data

在所有发现集成中,这是提供的元数据数量最少的集成。除此之外,使用 DNS 进行服务发现很难做到正确-考虑到可能会或可能不会尊重记录 TTL 的几个不同的缓存层以及其他问题,规划缓慢的收敛。这只应在高级情况下考虑。

File-based service discovery

类似于 webhook 通知器是与不受支持的通知系统集成的解决方案(如 第 11 章了解和扩展 Alertmanager),基于文件的集成为服务发现提供了相同类型的解决方案。它通过加载有效 JSON 或 YAML 文件列表来工作,这些文件又用于生成所需的目标及其标签。发现文件更改后无需重新加载或重新启动 Prometheus,因为它们会被监视更改并自动重新读取,具体取决于操作系统。此外,作为回退,还按计划(默认情况下每 5 分钟)读取发现文件。

以下 JSON 片段显示了一个有效的 Prometheus 发现文件。正如我们所看到的,有一个标签列表和一个标签适用的目标数组:

[
    {
        "labels": {
            "job": "node"
        },
        "targets": [
            "192.168.42.11:9100"
        ]
    }
]

以下抓取配置使用 file_sd 发现,它加载具有我们之前显示的内容的 file_sd.json

scrape_configs:
  - job_name: 'file_sd'
    file_sd_configs:
      - files:
        - file_sd.json
The files list also allows globing on the last element of the path, at the file level.

从这个配置中发现的目标可以在以下屏幕截图中看到,我们可以在其中检查文件提供的元数据,以及 Prometheus 自动生成的标签:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.6: Prometheus /service-discovery endpoint depicting file_sd data

很容易看出这种集成如何打开了一个充满可能性的世界:这些文件可以通过不断运行的守护程序或 cron 作业、使用 shell 脚本(甚至是简单的 wget)或成熟的编程语言来创建,或者简单地说通过配置管理到位。我们将在本章后面讨论如何构建自定义服务发现时探讨这个主题。

Using a built-in service discovery

要了解 Prometheus 和服务发现提供者之间的集成是如何工作的,我们将依赖我们的测试环境。更进一步,我们将提供一个在 Kubernetes 中运行的 Prometheus 的工作示例,依赖于该平台的本机服务发现。这些动手示例将展示一切如何联系在一起,帮助您不仅了解好处,而且最重要的是,了解这些机制的简单性。

Using Consul service discovery

在本章中,我们将 Consul 配置为基于虚拟机的测试环境中的示例服务发现系统 - Consul 的设置非常简单,这使其非常适合我们的示例。它的工作方式是在每个节点中以客户端模式运行一个代理,并在服务器模式下运行奇数个代理来维护服务目录。客户端节点上可用的服务直接与服务器节点通信,而集群成员资格使用八卦协议(随机点对点消息传递)在集群中的每个节点之间传播。由于我们的主要目标是使用 Consul 展示 Prometheus 服务发现,我们将测试环境配置为在开发模式下运行的代理,这使得内存中的服务器可以使用。当然,这完全无视安全性、可扩展性、数据安全性和弹性;关于如何正确配置 Consul 的文档可以在 https://learn.hashicorp.com/consul/,在生产环境中部署和维护Consul时应该考虑到。

要了解如何在测试环境中设置它,我们需要连接到运行 Consul 的实例:

vagrant ssh consul

从这里开始,我们可以开始探索 Consul 是如何设置的。例如,下面的代码片段显示了正在使用的 systemd 单元文件,我们可以在其中看到正在使用的配置标志 它被配置为在开发模式下作为代理运行,并且必须将其端口绑定到实例的面向外部的 IP 地址:

vagrant@consul:~$ systemctl cat consul.service 
...
[Service]
User=consul
ExecStart=/usr/bin/consul agent \
            -dev \
            -bind=192.168.42.11 \
            -client=192.168.42.11 \
            -advertise=192.168.42.11
...

如果我们运行 ss 并过滤其输出以仅显示属于 Consul 的行,我们可以找到它正在使用的所有端口:

vagrant@consul:~$ sudo /bin/ss -lnp | grep consul
udp UNCONN 0 0 192.168.42.11:8301 0.0.0.0:* users:(("consul",pid=581,fd=8))
udp UNCONN 0 0 192.168.42.11:8302 0.0.0.0:* users:(("consul",pid=581,fd=6))
udp UNCONN 0 0 192.168.42.11:8600 0.0.0.0:* users:(("consul",pid=581,fd=9))
tcp LISTEN 0 128 192.168.42.11:8300 0.0.0.0:* users:(("consul",pid=581,fd=3))
tcp LISTEN 0 128 192.168.42.11:8301 0.0.0.0:* users:(("consul",pid=581,fd=7))
tcp LISTEN 0 128 192.168.42.11:8302 0.0.0.0:* users:(("consul",pid=581,fd=5))
tcp LISTEN 0 128 192.168.42.11:8500 0.0.0.0:* users:(("consul",pid=581,fd=11))
tcp LISTEN 0 128 192.168.42.11:8502 0.0.0.0:* users:(("consul",pid=581,fd=12))
tcp LISTEN 0 128 192.168.42.11:8600 0.0.0.0:* users:(("consul",pid=581,fd=10))

正如我们所见,Consul 监听了很多端口,包括 TCP 和 UDP。我们感兴趣的端口是服务于 HTTP API 的端口,默认为 TCP 端口 8500。如果我们打开网络浏览器访问 http://192.168.42.11:8500,我们会看到类似下面的内容:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.7: Consul web interface displaying its default configuration

默认配置了一个服务,即 Consul 服务本身。

为了让这个例子更有趣,我们还在 consul 实例中部署了 consul_exporter(Prometheus 项目提供的导出器)。这个导出器不需要 Consul 方面的任何额外配置,所以它应该可以工作。我们可以在 systemd 单元文件中找到用于运行此服务的配置,如下所示:

vagrant@consul:~$ systemctl cat consul-exporter.service 
...
[Service]
User=consul_exporter
ExecStart=/usr/bin/consul_exporter --consul.server=consul:8500
...
源代码和安装文件 consul_exporter 可在 https://github.com/prometheus/consul_exporter

要验证导出器是否正确联系 Consul 并解析其指标,我们可以运行以下指令:

vagrant@consul:~$ curl -qs localhost:9107/metrics | grep "^consul"
consul_catalog_service_node_healthy{node="consul",service_id="consul",service_name="consul"} 1
consul_catalog_services 1
consul_exporter_build_info{branch="HEAD",goversion="go1.10.3",revision="75f02d80bbe2191cd0af297bbf200a81cbe7aeb0",version="0.4.0"} 1
consul_health_node_status{check="serfHealth",node="consul",status="critical"} 0
consul_health_node_status{check="serfHealth",node="consul",status="maintenance"} 0
consul_health_node_status{check="serfHealth",node="consul",status="passing"} 1
consul_health_node_status{check="serfHealth",node="consul",status="warning"} 0
consul_raft_leader 1
consul_raft_peers 1
consul_serf_lan_members 1
consul_up 1

当导出器可以成功连接并从 Consul 收集指标时,导出器将 consul_up 指标设置为 1。我们还可以看到 consul_catalog_services 指标,它告诉我们 Consul 知道一项服务,与我们在 Web 界面中看到的相匹配。

我们现在可以断开与 consul 实例的连接,并使用以下命令连接到 prometheus 实例:

exit
vagrant ssh prometheus

如果我们看一下 Prometheus 服务器配置,我们会发现以下内容:

vagrant@prometheus:~$ cat /etc/prometheus/prometheus.yml 
...

  - job_name: 'consul_sd'
    consul_sd_configs:
      - server: http://consul:8500
        datacenter: dc1
    relabel_configs:
      - source_labels: [__meta_consul_service]
        target_label: job
...

此配置允许 Prometheus 连接到 Consul API 地址(可在 http://192.168.42.11:8500 获得),并通过 relabel_configs 重写 job 标签,使其与服务名称相匹配(如 __meta_consul_service 标签中所示)。如果我们检查 Prometheus Web 界面,我们可以找到以下信息:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.8: Prometheus /service-discovery endpoint showing Consul default service

现在,有趣的部分:让我们通过在 Consul 中将其定义为服务来自动为 consul_exporter 添加一个抓取目标。本章的资源中提供了带有 Consul 服务配置的 JSON 有效负载,因此我们可以通过 Consul API 添加它。可以在以下路径中找到有效负载:

vagrant@prometheus:~$ cat /vagrant/chapter12/configs/consul_exporter/payload.json 
{
  "ID": "consul-exporter01",
  "Name": "consul-exporter",
  "Tags": [
    "consul",
    "exporter",
    "prometheus"
  ],
  "Address": "consul",
  "Port": 9107
}

使用以下指令,我们将通过 HTTP API 将这个新服务添加到 Consul 的服务目录中:

vagrant@prometheus:~$ curl --request PUT \
--data @/vagrant/chapter12/configs/consul_exporter/payload.json \
http://consul:8500/v1/agent/service/register

运行此命令后,我们可以通过查看 Consul Web 界面来验证是否添加了新服务,该界面将显示如下内容:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.9: Consul web interface showing the consul-exporter service

最后,我们可以检查 Prometheus /service-discovery 端点并检查我们是否有一个新目标,证明 Consul 服务发现按预期工作:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.10: Prometheus /service-discovery endpoint showing consul-exporter target

如果我们再次查询 consul_catalog_services 指标,我们可以看到它已更改为 2。由于我们现在正在 Prometheus 中收集 consul_exporter 指标,我们可以查询其当前使用 promtool 的值:

vagrant@prometheus:~$ promtool query instant http://localhost:9090 'consul_catalog_services'
consul_catalog_services{instance="consul:9107", job="consul-exporter"} => 2 @[1555252393.681]

Consul 标签可用于使用 relabel_configs 为具有不同要求的服务进行抓取作业配置,例如在存在给定标签时更改指标路径,或者具有标记来标记是否使用 HTTPS 抓取. __meta_consul_tags 标签值的开头和结尾都有逗号分隔符,以便于匹配;这样,您不需要根据您要匹配的标签在字符串中的位置对正则表达式进行特殊处理。工作中的一个例子可能是:

...
    relabel_configs: 
      - source_labels: [__meta_consul_tags]
        regex: .*,exporter,.*
        action: keep
...

这只会保留在 Consul 中使用 exporter 标记注册的服务,而丢弃其他所有内容。

Using Kubernetes service discovery

在此示例中,我们将放弃在前几章中使用的 Prometheus Kubernetes Operator,以便我们可以专注于该容器编排平台的 Prometheus 原生服务发现集成。在我们的 Kubernetes 测试环境中启动和运行 Prometheus 的清单可以在以下路径中找到,相对于代码存储库根路径:

cd ./chapter12/provision/kubernetes/

以下步骤将确保一个新的 Kubernetes 环境配备了所有必需的软件,以便我们可以专注于服务发现组件。

验证没有其他环境正在运行:

minikube status
minikube delete

启动一个空的 Kubernetes 环境:

minikube start \
  --cpus=2 \
  --memory=3072 \
  --kubernetes-version="v1.14.0" \
  --vm-driver=virtualbox \
  --extra-config=kubelet.authentication-token-webhook=true \
  --extra-config=kubelet.authorization-mode=Webhook

需要我们为 minikube 提供的额外配置,以便 Prometheus 能够使用服务帐户令牌与 kubelets 进行交互。当前面的命令完成后,一个新的 Kubernetes 环境就可以使用了。然后,我们可以使用以下说明继续部署我们的配置:

kubectl apply -f ./bootstrap/
kubectl rollout status deployment/prometheus-deployment -n monitoring

前面的命令应用了几个清单,其中包括创建一个名为 monitoring 的命名空间、一个 ServiceAccount 以及所有必需的 RBAC 配置,以便 Prometheus 可以查询 Kubernetes API。还包括带有 Prometheus 服务器配置的 ConfigMap,可在 bootstrap/03_prometheus-configmap.yaml 中找到。它为通过使用服务发现定位的 Kubernetes 组件定义了几个抓取作业,我们可以在以下代码段中看到:

$ cat bootstrap/03_prometheus-configmap.yaml
...
data:
  prometheus.yml: |
    scrape_configs:
...
    - job_name: kubernetes-pods
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      - action: keep
        regex: hey
        source_labels:
        - __meta_kubernetes_pod_label_app
...

我们可以通过发出以下命令打开 Prometheus Web 界面:

minikube service prometheus-service -n monitoring

通过移动到 /service-discovery 端点上可用的服务发现部分,我们可以看到,即使发现了几个 pod,它们都没有与标签值 hey 匹配对于 app 标签,因此被删除:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.11: Prometheus /service-discovery endpoint showing dropped targets for the kubernetes-pods job

现在是时候添加一些具有正确标签/值对的新 pod 来触发我们的服务发现配置了。我们可以继续运行以下命令,这将部署 hey 应用程序,然后跟踪部署状态:

kubectl apply -f ./services/
kubectl rollout status deployment/hey-deployment -n default

成功部署后,我们可以再次访问位于 /service-discovery 端点的 Prometheus Web 界面,我们可以看到 kubernetes 中现在有三个活动目标 - pods 抓取作业。以下屏幕截图描述了其中一个目标和 Kubernetes API 提供的所有标签:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.12: Prometheus /service-discovery endpoint showing the discovered targets for the kubernetes-pods job

完成测试后,您可以通过发出以下命令来删除这个基于 Kubernetes 的环境:

minikube delete

这种服务发现方法允许我们自动跟踪多个 Kubernetes 对象,而无需手动更改 Prometheus 配置。这个环境允许我们测试各种设置,并为根据我们的特定需求定制 Kubernetes 服务发现提供了基础。

Building a custom service discovery

即使有所有可用的服务发现选项,也有许多其他系统/提供程序不支持开箱即用。对于这些情况,我们有几个选择:

  • Open a feature request for Prometheus to support that particular service discovery, and rely on the community and/or maintainers to implement it.
  • Implement the service discovery integration yourself in Prometheus and either maintain a fork or contribute it back to the project.
  • Figure out a way to get the targets you require into your Prometheus instances with minimal maintenance work and time cost, and without relying on the Prometheus roadmap to get the job done.

前两个选项不是很好,因为它们要么超出我们的控制范围,要么维护起来很麻烦。此外,在没有相当大的兴趣和支持社区的情况下向 Prometheus 添加额外的服务发现集成会给维护者带来过度的支持负担,他们目前不接受任何新的集成。幸运的是,有一种方法可以轻松与任何类型的服务或实例目录集成,而无需维护成本高昂的分叉或创造性的黑客攻击。在下一节中,我们将解决如何将我们自己的服务发现与 Prometheus 集成。

Custom service discovery fundamentals

集成自定义服务发现的推荐方法是依赖基于文件的服务发现 file_sd。应该实现这种集成的方式是让进程(本地或远程、计划或永久运行)查询数据源(目录/API/数据库/配置管理数据库(CMDB )),然后在 Prometheus 可访问的路径上编写一个 JSON 或 YAML 格式的文件,其中包含所有目标及其各自的标签。然后,Prometheus 会自动通过磁盘监视或按计划读取该文件,这反过来又允许您动态更新可用于抓取的目标。

下图说明了上述工作流程:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
Figure 12.13: Custom service discovery flow

这种类型的方法足够通用,可以满足大多数(如果不是全部)所需的用例,从而可以以直接的方式构建自定义服务发现机制。

Community-driven file_sd integrations can be found at https://prometheus.io/docs/operating/integrations/#file-service-discovery.

现在我们知道了这种类型的集成应该如何工作,让我们直接开始构建我们自己的。

Recommended approach

正如我们到目前为止所解释的,构建自定义服务发现似乎是一项易于管理的工作。我们需要按照标准格式查询数据并将该数据写入文件。为了让我们的生活更轻松,Prometheus 团队提供了一个可用的适配器,它删除了大部分样板,用于创建新的服务发现集成。这个适配器只为 Go 编程语言提供,因为它重用了 Prometheus 本身的一些代码。适配器是这样制作的,因此一些维护较少的服务发现集成可以毫不费力地迁移到独立服务,以及轻松迁移到使用适配器构建的外部发现集成的主要 Prometheus 二进制文件中,所有其中已经证明了自己。请注意,没有什么可以阻止您使用您选择的语言来构建此类集成,但为了遵循推荐的方法,我们将坚持使用 Go 和发现适配器。解释如何用 Go 编程超出了本书的范围。

在 Prometheus 主存储库中,我们可以找到适配器的代码,以及使用 Consul 的示例,奇怪的是,我们已经在测试环境中设置了它。正如我们现在所知,Prometheus 原生支持 Consul 集成;然而,让我们假装它不是并且我们需要与它整合。在以下主题中,我们将讨论如何将所有内容放在一起并构建自定义服务发现。

The service discovery adapter

作为高级概述,适配器负责启动和管理我们的自定义服务发现代码,使用它生成的目标组,将它们转换为 file_sd 格式,并确保写入 JSON 数据需要时写入文件。使用此适配器编写服务发现集成时,无需更改其代码,因此可以将其作为库导入。为了提供更多关于适配器正在做什么的上下文,我们将解释一些较低级别的细节,以便在我们使用它实现我们自己的发现时它的行为是清晰的。

以下代码片段说明了我们需要从代码中调用的适配器的 Run 函数。此函数将负责在其自己的 goroutine (a.manager.Run) 中启动 discovery.Manager,指示它运行我们的发现实现 (a. disc),最后,在另一个 goroutine (a.runCustomSD) 中运行适配器本身:

// 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)
}

启动后,适配器从 Manager 提供的通道消费,该通道更新我们的代码将生成的目标组。当更新到达时,它将目标组转换为 file_sd 格式并验证自上次更新以来是否有任何更改。如果有更改,它将存储新的目标组以供将来比较,并将它们作为 JSON 写入输出文件。这意味着应在每次更新中发送目标群体的完整列表;未通过通道发送的组将从生成的发现文件中删除。

Custom service discovery example

现在我们已经了解了适配器的工作原理,让我们来看看我们需要实现什么才能让我们的自定义服务发现工作。正如我们之前看到的,适配器使用 discovery.Manager,因此我们需要为它提供 Discoverer 接口的实现,以便它可以运行我们的发现。界面如下所示:

type Discoverer interface {
    Run(ctx context.Context, up chan<- []*targetgroup.Group)
}
Discoverer 接口文档可以在以下位置找到 https://godoc.org/github.com/prometheus/prometheus/discovery#Discoverer

这意味着我们只需要实现 Run 函数,我们将在其中循环运行我们的发现逻辑,生成适当的目标组并通过 up 发送它们通道到适配器。 ctx 上下文在那里,以便我们知道何时需要停止。然后,我们实施的代码将定期从我们的数据源收集所有可用的目标/元数据。在此示例中,我们使用 Consul,它要求我们首先获取服务列表,然后为每个服务查询哪些实例支持它以及它们的元数据以生成标签。如果出现故障,我们将不会通过通道发送任何更新,因为提供陈旧数据比提供不完整或不正确的数据要好。

最后,在我们的 main 函数中,我们只需要实例化一个新适配器,并为其提供背景上下文、输出文件的名称、我们的发现实现名称、实现Discoverer 接口和一个 log.Logger 实例:

func main() {
...
  sdAdapter := adapter.NewAdapter(ctx, *outputFile, "exampleSD", disc, logger)
  sdAdapter.Run()
...
}

下一步是部署这个新创建的服务发现提供程序并将其与 Prometheus 集成,这就是我们在下一节中要做的事情。

Using the custom service discovery

要亲自了解自定义服务发现的行为方式,我们将依赖我们的测试环境。 custom-sd 二进制文件重新创建了 Consul 发现集成作为自定义服务发现的示例,它与 Prometheus 一起部署并准备好使用。与 Consul 部署一起,我们在测试环境中拥有所有必需的组件,以查看所有组件如何组合在一起。

custom-sd 可以通过发出以下命令在设置了 Go 开发环境的机器上构建: 去 github.com/prometheus/prometheus/documentation/examples/custom-sd/adapter-usage

首先,我们需要确保我们连接到 prometheus 实例。我们可以使用以下命令:

vagrant ssh prometheus

然后我们可以继续更改 Prometheus 配置以使用 file_sd 作为我们的集成。为此,我们必须将配置为使用 consul_sd 的抓取作业替换为新作业。为了让事情变得更简单,我们在 /etc/prometheus/ 中放置了一个已进行此更改的配置文件。要使用它,您只需将当前配置替换为新配置:

vagrant@prometheus:~$ sudo mv /etc/prometheus/prometheus_file_sd.yml /etc/prometheus/prometheus.yml

我们感兴趣的抓取工作如下:

- job_name: 'file_sd'
    file_sd_configs:
      - files:
        - custom_file_sd.json

为了让 Prometheus 知道这些变化,我们必须重新加载它:

vagrant@prometheus:~$ sudo systemctl reload prometheus

我们还应该确保 Consul 服务器具有我们之前添加的 consul-exporter 的配置。如果您有任何机会错过了该步骤,您现在可以通过简单地运行以下代码来添加它:

vagrant@prometheus:~$ curl --request PUT \
--data @/vagrant/chapter12/configs/consul_exporter/payload.json \
http://consul:8500/v1/agent/service/register

如果我们看一下 Prometheus Web 界面,我们会看到类似以下内容:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
12.14: Prometheus /service-discovery endpoint without any file_sd targets

我们现在准备试用 custom-sd 应用程序。我们需要指定 Consul API 地址和输出文件的路径,Prometheus 服务器配置为从中读取。以下命令将处理此问题,并确保正确的用户正在创建文件,以便 Prometheus 进程能够访问它:

vagrant@prometheus:~$ sudo -u prometheus -- custom-sd --output.file="/etc/prometheus/custom_file_sd.json" --listen.address="consul:8500"

我们现在正在运行自定义服务发现。如果我们回到 /service-discovery 端点中的 Prometheus 的 Web 界面,我们将能够看到发现的目标:

读书笔记《hands-on-infrastructure-monitoring-with-prometheus》选择正确的服务发现
12.15: Prometheus /service-discovery endpoint depicting the discovered target

我们还可以检查由 custom-sd 创建的文件,并验证其内容,如下所示(为简洁起见,输出已压缩):

vagrant@prometheus:~$ sudo cat /etc/prometheus/custom_file_sd.json 
[
    {
        "targets": ["consul:9107"],
        "labels": {
            "__address__": "consul:9107",
            "__meta_consul_address": "192.168.42.11",
            "__meta_consul_network_segment": "",
            "__meta_consul_node": "consul",
            "__meta_consul_service_address": "consul",
            "__meta_consul_service_id": "consul-exporter01",
            "__meta_consul_service_port": "9107",
            "__meta_consul_tags": ",consul,exporter,prometheus,"
        }}]

就是这样!您现在已经启动并运行了一个自定义服务发现,它使用基于文件的服务发现机制与 Prometheus 完全集成。更严肃的部署会将 custom-sd 服务作为守护程序运行。如果您更熟悉脚本语言,您可以选择编写一个服务发现脚本来生成发现文件并退出,在这种情况下,可以选择将其作为 cron 作业运行。最后的建议是,您可以让配置管理软件按计划动态生成发现文件。

Summary

在本章中,我们有机会了解为什么服务发现对于以理智的方式管理不断增长的基础设施至关重要。 Prometheus 利用了几个开箱即用的服务发现选项,可以以非常快速和友好的方式启动您的采用。我们浏览了 Prometheus 为服务发现提供的可用选项,并向您展示了对它们的期望。然后,我们使用 Consul 和 Kubernetes 进入了几个示例,以具体化我们之前公开的概念。最后,我们介绍了如何使用推荐的方法并依赖 file_sd 将自定义服务发现与 Prometheus 集成。

在下一章中,我们将介绍如何扩展和联合 Prometheus。

Questions

  1. Why should you use a service discovery mechanism in Prometheus?
  2. When you're using a cloud provider service discovery, what is the major requirement for setting the integration?
  3. What are the types of records supported by the DNS-based service discovery integration?
  4. What purpose does the concept of role serve in the Kubernetes service discovery integration?
  1. When you're building a custom service discovery, what available integration will you be relying upon?
  2. Do you need to reload Prometheus when a target file configured in file_sd is updated?
  3. What is the recommended way of building your own custom service discovery?

Further reading