服务发现,英文全称 Service Discovery,简称 SD。
Prometheus 提供通用 HTTP 服务发现功能,使其能够通过 HTTP 端点发现目标(要监控的目标,如:mysql、redis 等)。HTTP 服务发现是对已经支持的服务发现机制的补充,也是基于文件的服务发现的替代方案。
Prometheus 的 HTTP 服务发现是一种动态发现目标主机上监控样本数据(metrics)的机制。Prometheus 通过这种方式能够自动发现集群中的新端点,并将它们加入到监控配置中。服务发现模块专门负责发现需要监控的目标采集点(target)信息,包括协议(scheme)、主机地址和端口(instance)、请求路径(metrics_path)、请求参数(params)等。Prometheus 通过这些信息构建出一个完整的 HTTP 请求,并定时通过 pull 方式去目标采集点拉取监控样本数据。
这种服务发现机制的出现主要是为了解决静态配置方式的不足。在静态配置方式下,如果集群中增加了新的节点或组件,需要手动修改 Prometheus 的配置,并重启 Prometheus,这显然不够灵活和高效。而通过 HTTP 服务发现,Prometheus 能够自动感知到集群中的变化,并动态地更新监控目标列表,从而实现对集群的全面监控。
下面的表格比较了我们的两种通用服务发现实现:
比较项 | 基于文件 | 基于 HTTP |
基于事件 | 是的,通过 inotify | 否 |
更新频率 | 及时更新,因为是基于事件监听 | 刷新间隔 |
格式 | Yaml 或 JSON | JSON |
传输方式 | 本地文件 | HTTP/HTTPS |
安全性 | 基于文件的安全性 | TLS, Basic auth, Authorization header, OAuth2 |
什么是 inotify?
Inotify是Linux核心子系统之一,它允许监控程序打开一个独立文件描述符,并针对事件集监控一个或者多个文件。这些事件可以包括文件的打开、关闭、移动/重命名、删除、创建以及属性改变等。Inotify是一个非常灵敏且高效的工具,比传统的cron任务轮询方式要高效得多。
如果你要实现 HTTP SD 端点,需要注意以下几点要求:
(1)HTTP SD 端点的响应,Prometheus 服务将原样读取和使用。在每个刷新间隔(默认值:1 分钟),Prometheus 会向 HTTP SD 端点执行 GET 请求。GET 请求包含一个带有刷新间隔的 X-Prometheus-Refresh-Interval-Seconds 请求头。
(2)HTTP SD 端点必须返回 HTTP 200 状态,HTTP 标头 Content-Type 必须是 application/json。响应必须是 UTF-8 字符格式。如果不传输目标,也必须发送 HTTP 200 状态,并附带一个空列表 []。注意:目标列表是无序的。
(3)Prometheus 会缓存目标列表。如果在获取更新的目标列表时发生错误,Prometheus 会继续使用当前缓存的目标列表,缓存的目标列表在重启时会丢失。prometheus_sd_http_failures_total 计数器指标跟踪刷新失败的次数。
(4)每次抓取都必须返回整个目标列表,不支持增量更新。因为,Prometheus 实例不发送主机名,HTTP SD 端点无法知道 HTTP SD 请求是否是重启后的第一个请求。
(5)HTTP SD 端点的 URL 不是保密的,任何人都可见。因此,身份验证和任何 API 密钥都应通过适当的身份验证机制传递。Prometheus 支持 TLS 身份验证、基本身份验证(Basic auth)、OAuth2 和 Authorization 请求头。
[ { "targets": [ "<host>", ... ], "labels": { "<labelname>": "<labelvalue>", ... } }, ... ]
示例:
[ { "targets": ["10.0.10.2:9100", "10.0.10.3:9100", "10.0.10.4:9100", "10.0.10.5:9100"], "labels": { "__meta_datacenter": "london", "__meta_prometheus_job": "node" } }, { "targets": ["10.0.40.2:9100", "10.0.40.3:9100"], "labels": { "__meta_datacenter": "london", "__meta_prometheus_job": "alertmanager" } }, { "targets": ["10.0.40.2:9093", "10.0.40.3:9093"], "labels": { "__meta_datacenter": "newyork", "__meta_prometheus_job": "alertmanager" } } ]
仔细观察上面配置,再和配置文件进行一下对比:
# 抓取配置,其中包含一个要抓取的端点: # 在这里,它就是 Prometheus 本身 scrape_configs: # 定义了一个名为 "prometheus" 的 job,它将在 Prometheus 服务发现中可用。 # 作业名称作为标签 'job=<job_name>' 添加到从该配置中抓取的任何时间序列中。 - job_name: "prometheus" # 指定了要抓取的目标,这里是 Prometheus 服务器自身,也可以指定 Exporter # metrics_path 默认为"/metrics",scheme 默认为 "http" static_configs: - targets: ["localhost:9090"]
是不是很像,如果将 HTTP SD 端点的返回值配置到 prometheus.yml 配置文件,应该如下:
... scrape_configs: - job_name: "node" static_configs: - targets: ["10.0.10.2:9100", "10.0.10.3:9100", "10.0.10.4:9100", "10.0.10.5:9100"] ...
注意:再上面的 HTTP SD 端点返回的示例中,存在 __meta_prometheus_job 和 __meta_datacenter 元数据标签(meta labels),它们通常与服务发现(Service Discovery)结合使用。Prometheus 使用服务发现来自动发现和监控目标。这些目标可能是服务、主机或其他需要监控的资源。其中:
__meta_prometheus_job:用于标识 Prometheus 作业(job),它表示 Prometheus 作业的名称。
__meta_datacenter:用于标识数据中心(datacenter)或类似的概念。在某些服务发现机制中,特别是像 Consul 这样的分布式配置和服务发现系统中,目标可能分布在不同的数据中心。__meta_datacenter 标签允许 Prometheus 在服务发现过程中获取目标所在的数据中心信息,并将其作为标签添加到时间序列中。这对于跨多个数据中心的监控和警报非常有用,因为它允许 Prometheus 根据数据中心来过滤、聚合或区分时间序列数据。
(1)假如在 192.168.0.32 主机的 9100 端口上开启 Exporter 服务,如下图:
(2)创建一个简单的 Spring Boot 项目,点击查看如何创建 Spring Boot 项目。
(3)创建一个 Controller,用来返回目标列表,源码如下:
package com.hxstrive.springboot.prometheus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Prometheus HTTP SD 服务发现 * @author hxstrive.com */ @Controller @RequestMapping("/prometheus/sd") public class PrometheusSdController { /** * 返回格式: * [ * { * "targets": ["10.0.40.2:9093", "10.0.40.3:9093"], * "labels": { * "__meta_datacenter": "newyork", * "__meta_prometheus_job": "alertmanager" * } * } * ] * @return */ @GetMapping("/targets") @ResponseBody public Object targets() { List<Map<String,Object>> retList = new ArrayList<>(); // 添加一个目标 retList.add(new HashMap<String,Object>(){ { put("targets", new String[]{"192.168.0.32:9100"}); put("labels", new HashMap<String,String>(){ { put("__meta_datacenter", "localhost"); put("__meta_prometheus_job", "remote-host"); } }); } }); return retList; } }
启动 Spring Boot 项目,使用浏览器访问 http://localhost:8080/prometheus/sd/targets 地址,输出如下图:
(4)配置 Prometheus,打开本地的 prometheus.yml 文件,修改配置:
... scrape_configs: ... # 我的 HTTP 服务发现作业 - job_name: "my-sd-job" http_sd_configs: - url: 'http://localhost:8080/prometheus/sd/targets' refresh_interval: 5s ...
(5)重启 Prometheus 服务,访问 http://localhost:9090/targets?search= 地址,如下图: