一、整体架构
我们需要在生成环境为业务应用提供一套 Prometheus 指标自助埋点的方案,经过评估,我们采用了下面的架构方案。
下面会逐个分析各个组件。
二、采集层
1.1 指标采集模式选择 pull 还是 push?
| pull | push | |
|---|---|---|
| 开发成本 | 低。官方SDK默认的方式,非常成熟稳定,可直接使用 | 需要基于原有的SDK(simpleclient_pushgateway)做改造 |
| target管理 | 1.可以在控制台查看client(target),方便管理接入情况 2.由服务端控制拉取哪些client(target)的指标、拉取的频率 3.自动检查target的可用性,注册服务中未能拉取会记录为未收集状态,能发现哪些client有问题 | 无法管理,不可控 |
| 服务发现 | 1.依赖服务发现机制,需要搭建注册中心 | 1.不需要注册中心,我们可以用vmagent替代pushgateway,接收client push的指标,然后存到vmstore |
| 结论 | 推荐选择该方式,可以管理target | 架构更简单。但是需要改造prometheus sdk,且无法管理client |
我们在线上环境采用的是 pull 模式,因为对于监控平台而言,target 端的管理是非常重要的。
1.2 pull 模式
pull 模式需要用到注册中心
consul 或 nacos 是流行的 prometheus 注册中心,他们的对比如下:
| nacos | consul | |
|---|---|---|
| 政策限制 | 无限制 | 背后公司跟中国政策有冲突,不建议使用 |
| 功能 | 拉取服务时,没法指定各类传参进行过滤 | 拉取服务时,支持多种传参进行过滤 |
| 官方文档 | docs.victoriametrics.com/sd_configs/… github.com/alibaba/nac… | docs.victoriametrics.com/sd_configs/… |
| 结论 | 选择该方式,更可控,且符合政策要求 |
也可以参考这个方案:github 上已经有人开源了一个对应的 nacos 作为注册中心、提供 prometheus consul api 的 adapter,我们可以直接拿来使用,详见:github.com/weixiaohui-… adapter 的作用是将 Nacos 伪装成 Consul,让 Prometheus 能够自动发现 Nacos 中的服务,而不用每个微服务单独配置。用户需要引入一个 jar 包,并在 Prometheus 配置中使用 consul_sd_configs 指向 Nacos 的地址。但是对 spring cloud、spring boot 版本有要求,我们没有采用这个方案。
1.3 metric-client SDK
基于 prometheus-client 封装一个 metric-client SDK,通过该 SDK 可以自动注册 client 到 nacos。
1.4 VictoriaMetrics vmagent
我们采用 VictoriaMetrics 作为 prometheus 的集群方案。
vmagent 是 VictoriaMetrics 提供的一个轻量级的 Prometheus 采集代理,用于从各种数据源(如 Prometheus、OpenTelemetry、Telegraf 等)采集监控数据,并将这些数据发送到 VictoriaMetrics 集群进行存储和查询。
vmagent相比 prometheus 采集器具有下面的优势:
- 资源效率更高:CPU、内存、磁盘 IO 和网络带宽占用显著低于 Prometheus。
- 数据缓冲与重试:在远程存储不可用时,vmagent 会将数据暂存本地磁盘,待恢复后重试,避免数据丢失。
三、nacos 注册中心
当选择 nacos 作为注册中心,选择版本什么版本?
| 版本 | 2.0.4 | 2.4.2 |
|---|---|---|
| 协议 | 不支持prometheus http sd协议 | 已支持prometheus http sd协议,prometheus可以直接配置nacos http接口地址 |
| 接入成本 | 通过开发nacos proxy服务,提供prometheus http sd协议的http接口给prometheus使用 | 1.方案一、需要部署一套新版本的nacos困难:部署需要耗费时间、且后期多维护一套集群 2.方案二、现有nacos升级2.4.2版本困难:有一定风险,可能有版本不兼容的情况。 |
| 结论 | 选择该方式,更可控。后期nacos升级版本后,可以去除sd http接口,直接对接新版本nacos |
我们线上使用的 nacos 2.0.4 版本,但是短期内又不想升级到 2.4.2 版本 nacos,有一定风险,可能有版本不兼容的情况;也不想部署一套新的 2.4.2 版本的 nacos,增加机器和维护成本。
所以,我们还是使用现有的 2.0.4 版本集群,然后将现有的一个 Java 的监控应用 A,集成作为 nacos proxy 服务,提供 prometheus http sd 协议的 http 接口给 prometheus 使用。
下面是 nacos sd 协议 proxy 接口的示例代码:
@Slf4j
@RestController
@RequestMapping("api/prometheusSD")
public class PrometheusServiceDiscoveryController {
private NamingService namingService;
@PostConstruct
public void init() throws Exception {
try {
log.info("Starting to create Nacos NamingService client.");
createNacosNamingServiceClient();
log.info("Nacos NamingService client created successfully.");
} catch (Exception e) {
log.error("Error creating Nacos NamingService client.", e);
// 如果不忽略报错则需要抛异常
log.error("Nacos client creation error and will throw an exception.");
throw e;
}
}
private void createNacosNamingServiceClient() throws Exception {
// 初始化 Nacos 配置
Properties properties = new Properties();
// 服务地址
properties.put("serverAddr", MetricClientConfig.getNacosServerAddr());
// 指定命名空间
properties.put("namespace", MetricClientConfig.getNacosNamespace());
// Nacos 用户名
properties.put("username", MetricClientConfig.getNacosUsername());
// Nacos 密码
properties.put("password", MetricClientConfig.getNacosDecryptPassword());
this.namingService = NacosFactory.createNamingService(properties);
}
/**
* Prometheus Http SD(服务发现) 接口
* 参考:
* https://docs.victoriametrics.com/sd_configs/#http_sd_configs
* https://prometheus.io/docs/prometheus/latest/http_sd/
* @param namespace
* @return 注意:返回结果跟Nacos官方sd实现有差异
*/
@GetMapping("/namespace/{namespace}")
public ResponseEntity<?> prometheusSdNamespace(@PathVariable String namespace) {
// 如果应用启动时namingService创建失败,namingService可能是null
if (namingService == null) {
String status = "error";
String message = "nacos namingService is null.";
return getPrometheusSdErrorResponseEntity(status, message);
}
List<Map<String, Object>> serviceResult = Lists.newArrayList();
int pageSize = 100;
int maxPageNo = 1000;
try {
// 分页获取服务名称
// 使用for循环进行分页处理
for (int pageNo = 1; pageNo <= maxPageNo ; pageNo++) {
ListView<String> serviceList = namingService.getServicesOfServer(pageNo, pageSize);
// 检查是否有服务数据,没有则退出
if (serviceList.getData().isEmpty()) {
break;
}
for (String serviceName : serviceList.getData()) {
// 获取当前页面的实例列表
List<Instance> instances = namingService.getAllInstances(serviceName, false);
// 将实例转化为 Prometheus SD 格式
List<String> targets = Lists.newArrayList();
Map<String, Object> serviceResultItem = Maps.newLinkedHashMap();
Map<String, String> labels = Maps.newLinkedHashMap();
labels.put("_service", serviceName);
labels.put("_namespace", namespace);
for (Instance instance : instances) {
targets.add(instance.getIp() + ":" + instance.getPort());
}
serviceResultItem.put("targets", targets);
serviceResultItem.put("labels", labels);
serviceResult.add(serviceResultItem);
}
// 如果一页的数据小于分页大小,说明后面没有数据了,可以退出循环,这样可以少调用一下获取service的接口
if (serviceList.getData().size() < pageSize) {
break;
}
}
return ResponseEntity.ok(serviceResult);
} catch (Exception e) {
String status = "error";
String message = "Failed to fetch services: " + e.getMessage();
return getPrometheusSdErrorResponseEntity(status, message);
}
}
@NotNull
private ResponseEntity<Map<String, String>> getPrometheusSdErrorResponseEntity(String status, String message) {
Map<String, String> errorResp = Maps.newLinkedHashMap();
// 捕获异常并设置错误的响应结果,返回 HTTP 500,不要返回200,
errorResp.put("status", status);
errorResp.put("message", message);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResp);
}
}
四、存储层
我们选用 VictoriaMetrics 的 vminsert、vmstorage 和 vmselect 组件,构建 Prometheus 集群的存储解决方案。
组件说明:
-
vminsert:
- 功能:负责数据插入,接收来自外部的数据并进行写入操作。
- 特点:使用一致性哈希算法将数据分散到多个
vmstorage节点上,确保数据均匀分布。 - 扩展性:支持水平扩展,通过增加节点来提升写入能力。
-
vmstorage:
- 功能:负责数据存储和代理功能,提供数据持久化存储。
- 特点:支持数据压缩,减少存储空间需求;支持多租户(命名空间)环境,确保每个租户的数据隔离。
- 扩展性:支持横向扩展,通过增加节点来提升存储能力。
-
vmselect:
-
功能:负责数据查询,根据输入的查询条件从
vmstorage中获取数据。 -
特点:支持全局查询视图,可以处理来自多个 vmstorage 实例的数据,提供统一的查询接口。
-
扩展性:支持水平扩展,通过增加节点来提升查询能力。
-
VictoriaMetrics 相关内容后面再发文再写,如需要了解,可以参考这些文章:
-
VictoriaMetrics 官方教程 docs.victoriametrics.com/quick-start…
-
一文搞懂 VictoriaMetrics 的使用 www.qikqiak.com/post/victor…
-
VictoriaMetrics 中文教程 flashcat.cloud/blog/victor…
五、展示层
使用 grafana 展示指标,生产环境的效果图不方便展示。下面展示一下,VictoriaMetrics cluster 自监控的指标看板。