Prometheus整合Springboot实现报警

版本说明:

​ 本文档基于Promtheus-2.34.0.windows + alertmanager-0.24.0.windows + grafana-8.5.2.windows +SpringBoot2.4.9

主要实现功能:

​ 1. 操作系统/中间件/SpringBoot项目常用基本指标监控和展示

​ 2. SpringBoot自定义指标监控

​ 3. 报警信息发送SpringBoot处理

Prometheus官方文档

Prometheus以及部分Exporter下载

一、整体架构

1、监控架构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FhjBnsdq-1663838957100)(https://prometheus.io/assets/architecture.png)]

2、各模块功能描述
  1. Prometheus:监控主体,通过从jobs/exporter获取监控度量数据(pull/push两种方式,短时任务使用push);

  2. Grafana:监控数据展示,可以直接导入官方丰富的监控模板快速生成监控大盘;

  3. Exporter:开放被监控目标的对应监控端口,Exporter的实例为Target,Target通过对应的Exporter启动,例如linux_exporterMySQL_exporterSpring项目也可构建作为一个Exporter;

  4. Alertmanager:报警模块,当在Prometheus配置文件中配置的规则被触发后,会推送报警信息到Alertmanager,然后由Alertmanager实现对应的报警方式,例如邮件、WebHook、钉钉机器人

二、安装与配置

本文档以Windows为例,Linux除安装包不同但基本同理

1、Prometheus
  • 解压prometheus-2.34.0.windows-amd64.zip,修改prometheus.yml
# 此片段指定的是prometheus的全局配置, 比如采集间隔,抓取超时时间等.
global:
  # 抓取间隔 默认一分钟
  scrape_interval: 15s
  # 抓取超时时间 默认10s
  scrape_timeout: 10s
  # 评估规则间隔 默认一分钟
  evaluation_interval: 15s
 
# 此片段指定报警规则文件, prometheus根据这些规则信息,触发对应的报警
rule_files:
  # 规则文件需要在该配置文件同级目录手动创建
  - "rules/host_rules.yml"
  # - "first_rules.yml"
  # - "second_rules.yml"
 
# 配置Targets
scrape_configs:
  # Prometheus
  - job_name: "prometheus"
	# 默认监控端口,无需修改
    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    	# Prometheus自身地址
      - targets: ["192.168.1.81:9090"]

  # windows_exporter
  - job_name: "windows"
    static_configs:
      - targets: ["192.168.1.81:9182"]

  # MySQL_exporter
  - job_name: "mysql"
    static_configs:
      - targets: ["192.168.1.81:9104"]

  # SpringBoot_exporter
  - job_name: "huishan"
    # 因为该项目配置监控密码,需要在application.yml中配置用户名密码通过认证
    # spring.security.user.name=admin
    # spring.security.user.password=admin
    basic_auth:
      username: admin
      password: admin

    scrape_interval: 10m
    # 抓取端点
    metrics_path: "/actuator/prometheus"
    static_configs:
      # 目标路径 支持集群拉取
      - targets: ["127.0.0.1:8182"]

  # 一个独立监控报警模块,负责接收alertmanager报警数据,与主业务模块解耦
  - job_name: "housealert"
    scrape_interval: 5s
    metrics_path: "/actuator/prometheus"
    static_configs:
      - targets: ["127.0.0.1:9088"]
    
  # Websocket_exporter
  - job_name: "websocket"
    static_configs:
      - targets: ["192.168.1.81:9802"]
 
  #其他exporter(nginx、oracle、elasticsearch等)同理
 
# 配置alertmanager实例
alerting:
  alertmanagers:
    - static_configs:
        - targets: ["127.0.0.1:9093"]
 
  • 保存,建议第一次启动使用cmd,如果配置文件格式问题会直接报错并展示格式错误信息,双击prometheus.exe会闪烁一下消失,则同样格式错误报错。

同目录下的amtool.exe工具提供检查配置文件格式的功能

当需要清除时序数据库出现脏数据时,可使用HTTP API |普罗米修斯 提供的API进行删除,使用API之前需要将Prometheus重启并在启动命名增加后缀–web.enable-lifecycle,默认情况下API调用为关闭状态

当然,如果是清空所有数据,最快的办法是新起一个Prometheus,将配置文件和Rules复制过去

本文档不涉及PushGateway的使用,可参照Pushing metrics | Prometheus

2、Grafana

grafana8.5.2存在无法导入JSON文件的问题,换用8.5.9可解决

  • 解压grafana-8.5.2.windows-amd64.zip,执⾏ ./bin/grafana-server.exe,访问http://localhost:3000/,创建账号密码。

  • 保存后即可完成,效果图:

  • 接下来导入模板,模板下载地址,左侧DataSource选择Prometheus,选择相应的模板,可以选择复制模板id或者下载模板Json导入

    • 推荐中文模板id:

​ windows:10467

​ JVM:12856(模板首页-需要项目增加对应依赖

  • 自定义仪表可以通过模仿模板编写,编写完成后可以导出为JSON文件放到生成环境一键生成

将Grafana页面嵌入应用

设置grafana-8.5.2\conf\defaults.ini

# 允许嵌入iframe
allow_embedding = true

#允许匿名访问
enabled = true

重启即可

3、Alertmanager
  • 解压alertmanager-0.24.0.windows-amd64.zip,修改alertamanger.yml

比较通俗的alertmanager配置文件解释 或者 官方文档关于alertmanager配置

global:
  # ResolveTimeout是alertmanager在警报发生时使用的默认值 默认五分钟
  # 不包括EndsAt,在此时间过后,如果警报尚未更新,则可以将其声明为已解决。
  # 这对来自普罗米修斯的警报没有影响,因为它们总是包括EndsAt。
  # 经过此时间后,如果尚未更新告警,则将告警声明为已恢复。(即prometheus没有向alertmanager发送告警了)
  resolve_timeout: 10m

  # 邮件相关配置
  smtp_smarthost: 'smtp.qq.com:465'
  smtp_from: '发送人@foxmail.com'
  smtp_auth_username: '发送人@foxmail.com'
  # 如果是QQ邮箱,密码则需要从QQ邮箱网页-设置-授权码获取
  smtp_auth_password: 'password'
  # 默认SMTP TLS要求,Go不支持到远程SMTP端点的未加密连接 默认为true
  smtp_require_tls: false

# 根路由器 对promethues发送来的警报信息进行分流匹配 到对应的接受者
route:
  group_by: ['alertname']
  # 第一组告警发送通知需要等待的时间,这种方式可以确保有足够的时间为同一分组获取多个告警,然后一起触发这个告警信息。
  group_wait: 10s
    # 发送第一个告警后,等待"group_interval"发送一组新告警。
  group_interval: 10s
   # 分组内发送相同告警的时间间隔。内存警报若1分钟内发送6次 参数为1分钟 则发送一次
  repeat_interval: 1m
  # 默认接收者名称
  receiver: 'webhook'
  # 子路由-这里配置的参数可以覆盖根路相同参数
  routes:
    - match:
        owner: a
        severity: page
      receiver: webhook
      group_wait: 10s
    - match:
        owner: b
        severity: warning
      receiver: mail
    # 正则匹配
    - match_re:
      # 这里可以匹配出标签含有service=foo1或service=foo2或service=baz的告警
      service: ^(foo1|foo2|baz)$
      receiver: mail

# 接收者列表(报警实现方式)
receivers:
  - name: 'webhook'
    webhook_configs:
    # 下面的url是自定义springboot项目中接口的访问url地址
    - url: 'http://127.0.0.1:9099/housealert/alert/demo'
  - name: 'mail'
    email_configs:
      - to: '1945192314@qq.com'
        send_resolved: true
        headers:
          subject: "[operations] 报警邮件"
          from: "监控报警中心"
          to: "指挥官"
inhibit_rules:
  - source_match:
      severity: 'critical'
    target_match:
      severity: 'warning'
    equal: ['alertname', 'dev', 'instance']

  • 双击exe启动,如果闪屏消失则为配置文件语法错误,报错信息查看方式与Prometheus相同,进入Alertmanager,则启动成功。

当需要实现多报警和多报警方式 一对一或一对多方式时 需要如下图所示进行配置

如图所示 在规则.yml文件下,一个报警项在配置labels时可以增加自定义标签,或者使用severity()标签,在配置报警路由分发时,通过match进行匹配指定选用的接受者receiver,匹配方式也可以使用match_re正则方式实现,在报警需求不复杂的情况下,建议使用一个标签匹配,即可实现一个规则的触发使用对应一个或多个报警方式实现报警。

4、Exporter

​ (1)windows_exporter

​ a. 双击启动windows_exporter-0.18.1-amd64.exe

​ b. 浏览器打开 localhost:9182/metrics 看到文本则启动成功

​ (2)linux_exporter

​ (占位符)

​ (3)mysql_exporter(以win版为例)

​ a. 解压mysqld_exporter-0.14.0.windows-amd64

​ b. 在该目录下新建my.cnf,建议单独创建一个MySQL用户用于监控

[client]
host=192.10.110.231
port=3308
user=root      
password=root

​ c. 双击mysqld_exporter.exe启动

​ (4)webscoket_exporter

​ a. 设置windows系统变量WEBSOCKET_EXPORTER_URI=ws://192.168.1.19:7777/urtc

​ b. 命令行管理员执行windows_exporter-0.18.1-amd64.exe

​ c. 监控地址 127.0.0.1:9802写入prometheus.yml

​ d. 监控指标参照:A Blackbox Websocket Uptime Exporter for Prometheus

​ (3)oracle_exporter

​ (占位符)

三、报警规则

1、规则配置
  • 上文在prometheus.yml中配置了rule的文件地址rules/host_rules.yml,在prometheus.yml同级目录创建对应文件:
# 参考样式

groups:
# 报警组组名称
- name: hostStatsAlert
  #报警组规则
  rules:
   #告警名称,需唯一
  - alert: hostCpuUsageAlert
    #promQL表达式
    expr: 100 - (avg by (instance) (irate(windows_cpu_time_total{job="windows",mode="idle"}[5m])) * 100) > 0.5
    #满足此表达式持续时间超过for规定的时间才会触发此报警
    for: 1m
    labels:
      #严重级别
      severity: page
      # 自定义标签
      owner: a
    annotations:
     #发出的告警标题
      summary: "实例 {{ $labels.instance }} CPU 使用率过高"
      #发出的告警内容
      description: "实例{{ $labels.instance }} CPU 使用率超过 85% (当前值为: {{ $value }})"
  - alert: hostMemUsageAlert
    expr: 100.0 - 100 * windows_os_physical_memory_free_bytes{job=~"windows"} / windows_cs_physical_memory_bytes{job=~"windows"} > 0.1
    # 持续时间为1min则报警
    for: 1m
    labels:
      severity: page
      owner: b
    annotations:
      summary: "实例 {{ $labels.instance }} 内存使用率过高"
      description: "实例 {{ $labels.instance }} 内存使用率 85% (当前值为: {{ $value }})"
  • 保存后重启Prometheus,http://localhost:9090/rules,查看上述配置的规则,出现则说明配置成功。配置文件中的PromQL(expr)表达式可以在Prometheus的Graph界面运行一下,检验是否符合需要。

  • 进入报警页面[http://localhost:9090/alerts,规则图例:

2、检验报警
  • 进入http://127.0.0.1:9093/,出现如下图所示则说明Prometheus报警生效并推送到alertmanager

四、SpringBootExporter

1、SpringBoot接入监控
<!--spring boot监控-->

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient</artifactId>
    <version>0.15.0</version>
</dependency>
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient_pushgateway</artifactId>
    <version>0.15.0</version>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
# application.yml修改配置

management:
  server:
  	# 对应Prometheus.yml中,job_name为SpringBoot项目的targets地址
    port: 8182
    address: 127.0.0.1
    # 关闭监控端口身份验证,或者在Prometheus.yml中配置basic_auth
  security:
    enabled: false
  endpoint:
    shutdown:
      enabled: true
  endpoints:
    web:
      exposure:
        include: "*"
  metrics:
    export:
      # 开启Prometheus
      prometheus:
        enabled: true
      jmx:
        enabled: true
    tags:
      application: huishanhouse
  • 在上文已在prometheus.yml中配置该项目,启动项目,浏览器进入删除配置的地址端口

  • Grafana导入模板12856查看效果:

    建议将prometheus、alertmanager和rule规则文件放入项目中使用git管理和备份,方便监控更新和回退

2、自定义监控
  • 接口调用监控

    //参考代码
    package com.lulu.housealert.aspect;
    
    import io.micrometer.core.instrument.MeterRegistry;
    import io.micrometer.core.instrument.Metrics;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    import javax.servlet.http.HttpServletRequest;
    import java.time.LocalDate;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 监控接口调用切面类
     *
     * @author lulu
     * @date 2022/7/13
     */
    @Aspect
    @Component
    public class ControllerMetcisAspect {
    
        @Autowired
        private MeterRegistry builder;
    
        @Pointcut("execution(* com.lulu.housealert.controller..*.*(..))")
        public void controllerPointcut() {
        }
    
        @Around("controllerPointcut()")
        public Object MetricsCollector(ProceedingJoinPoint joinPoint) throws Throwable {
    
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String userId = StringUtils.hasText(request.getParameter("userId")) ? request.getParameter("userId") : "no userId";
            String appId = StringUtils.hasText(request.getParameter("appId")) ? request.getParameter("appId") : "no appId";
    
            // 获取api url
            String api = request.getServletPath();
            // 获取请求方法
            String method = request.getMethod();
            long timeMillis = System.currentTimeMillis();
            LocalDate now = LocalDate.now();
            //数组大小按实际需要创建,不能存在空值,tags为空会存现报错或不生效
            String[] tags = new String[6];
            
            tags[0] = "api";
            tags[1] = api;
            tags[2] = "method";
            tags[3] = method;
            tags[4] = "day";
            tags[5] = now.toString();
            tags[6] = "appId";
            tags[7] = appId;
            tags[8] = "userId";
            tags[9] = userId;
            // 请求次数加1
            //自定义的指标名称:http_request_test_all,指标包含数据
            builder.getMeters();
            builder.counter("http_request_test_all", tags).increment();
            Object object;
            try {
                object = joinPoint.proceed();
            } catch (Exception e) {
                // 请求失败次数加1
                builder.counter("http_request_test_error", tags).increment();
                throw e;
            } finally {
                long f = System.currentTimeMillis();
                long l = f - timeMillis;
                //记录请求响应时间
                builder.timer("http_request_test_time", tags).record(l, TimeUnit.MILLISECONDS);
            }
            return object;
        }
    }
    

    ​ 项目重启后随意调用接口,即可在http://url:ip/actuator/prometheus中看到http_request_test_all等指标,根据指标编写对应的PromQL实现监控和报警

  • 自定义指标监控

    • 本模块首先需要了解Prometheus提供的四种指标类型Counter(计数器)Gauge(仪表盘)Histogram(直方图)Summary(摘要)

      • Counter:只增不减,方便用于计数

      • Gauge:可增可见的仪表盘,方便用于展示实时状态,比如可用内存

      • Histogram:直方图,比如CPU平均使用率、接口平均相应时间

      • Summary:记录平均大小,需要两个信息count(事件发生次数)和sum(所有事件的总大小)

    • 使用度量指标实现自定义指标

      • 开箱使用方式(仅展示gauge)

        //伪代码
        import io.micrometer.core.instrument.MeterRegistry;
        
        @Autowired
        private MeterRegistry builder;
        
        //方法中使用-创建指标名称为print 标签为设备id-设备所属单位的指标
        //需要注意的是 Tag参数需要存在键值对应 否则会在启动报错
        //Caused by: java.lang.IllegalArgumentException: size must be even, it is a set of key=value pairs
        List<Object> print = builder.gauge("print", Tags.of("TagA", "TagA01"), new ArrayList<>(), List::size);
        
        //赋值 最后指标展示的是集合的size()
        print.add(1);
        
        //new ArrayList<>()可能导致该指标的数值为NaN,如果展示状态变化,可以规避使用0作为状态
        

        该方式创建度量指标的好处在于,Tag可以自定义生成。

      • 基于注册表使用

        • 首先在启动类注入指标MeterRegister

          package com.lulu.housealert;
          
          import io.micrometer.core.instrument.MeterRegistry;
          import org.springframework.beans.factory.annotation.Value;
          import org.springframework.boot.SpringApplication;
          import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
          import org.springframework.boot.autoconfigure.SpringBootApplication;
          import org.springframework.context.annotation.Bean;
          
          @SpringBootApplication
          public class HousealertApplication {
          
              public static void main(String[] args) {
                  SpringApplication.run(HousealertApplication.class, args);
              }
          
              @Bean
              MeterRegistryCustomizer<MeterRegistry> configurer(@Value("${spring.application.name}") String applicationName) {
                  return (registry) -> registry.config().commonTags("application", applicationName);
              }
          }
          
        • 创建指标构造类,在项目启动时即可构造所有所需要的指标,适合Tag固定的情况下使用

          //参考代码
          package com.lulu.housealert.actuator;
          
          import io.micrometer.core.instrument.MeterRegistry;
          import io.micrometer.core.instrument.Tags;
          import lombok.Data;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.stereotype.Component;
          import javax.annotation.PostConstruct;
          import java.util.ArrayList;
          import java.util.List;
          
          /**
           * 设备状态度量构造器
           *
           * @author lulu
           * @date 2022/7/18
           */
          @Component
          @Data
          public class DeviceStatusMetricBuilder {
          
              /**
               * 监控指标
               */
              private List<Object> printStatus;
              private List<Object> doorStatus;
              private List<Object> cardStatus;
          
              private final MeterRegistry registry;
          
              @Autowired
              public DeviceStatusMetricBuilder(MeterRegistry registry) {
                  this.registry = registry;
              }
          
              @PostConstruct
              private void init() {
                  //打印机状态-仪表构造器
                  printStatus = registry.gauge("print_status", Tags.of("device_status", "print_status"), new ArrayList<>(), List::size);
                  //门禁状态-仪表构造器
                  doorStatus = registry.gauge("door_status", Tags.of("device_status", "door_status"), new ArrayList<>(), List::size);
                  //读卡卡状态-仪表构造器
                  cardStatus = registry.gauge("card_status", Tags.of("device_status", "card_status"), new ArrayList<>(), List::size);
              }
          }
          
          /**
           * 业务代码使用
           */
          
          @Resource
          private DeviceStatusMetricBuilder builder;
          
          //从构造器中获取度量指标
          List<Object> printStatus = builder.getPrintStatus();
          List<Object> doorStatus = builder.getDoorStatus();
          List<Object> cardStatus = builder.getCardStatus();
          //清空度量操作
          printStatus.clear();
          doorStatus.clear();
          cardStatus.clear();
          //设备状态赋值
          printStatus.add(1);
          doorStatus.add(1);
          cardStatus.add(1);
          

五、自定义报警

本章节介绍使用SpringBoot作为Alertmanager的receiver接受者,方便使用Java实现报警操作

具体实现方式是编写一个Controller接口,用来接收alertmanager传来的POST请求

//参考代码
package com.lulu.housealert.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

/**
 * 监控接口-对接alertmanager
 *
 * @author lulu
 * @date 2022/7/11 10:01
 */
@Slf4j
@RestController
@RequestMapping("/alert")
public class AlertController {

    /**
     * 接受Alertmanager发送的POST请求
     *
     * @修改时间 2022/7/11
     * @param json
     * @return
     */
    @PostMapping(value = "/demo", produces = "application/json;charset=UTF-8")
    public String demo(@RequestBody String json) {

        log.error("---1.接收alertmanager传入数据---");

        Map<String, Object> result = new HashMap<>();
        result.put("msg", "报警失败");
        result.put("code", 0);

        if (StringUtils.isBlank(json)) {
            log.error("---2.传入数据为空,报警失败---");
            return JSON.toJSONString(result);
        }

        log.error("---2.报警数据传入---", json);
        
        JSONObject jo = JSON.parseObject(json);

        JSONObject commonAnnotations = jo.getJSONObject("commonAnnotations");
        String status = jo.getString("status");
        if (commonAnnotations == null) {
            return JSON.toJSONString(result);
        }

        String subject = commonAnnotations.getString("summary");
        String content = commonAnnotations.getString("description");

        result.put("subject", subject);
        result.put("content", content);

        log.info("---3.打印告警信息", result);
        System.out.println(result+"-----");

        return result.toString();
    }
}
//alertmanager发送JSON参考,可以按需拿取需要的数据
{
    "receiver":"webhook",
    "status":"firing",
    "alerts":[
        {
            "status":"firing",
            "labels":{
                "alertname":"hostMemUsageAlert",
                "instance":"192.168.1.81:9182",
                "job":"windows",
                "owner":"b",
                "severity":"page"
            },
            "annotations":{
                "description":"实例 192.168.1.81:9182 内存使用率 85% (当前值为: 84.79673195230218)",
                "summary":"实例 192.168.1.81:9182 内存使用率过高"
            },
            "startsAt":"2022-07-22T01:57:46.438Z",
            "endsAt":"0001-01-01T00:00:00Z",
            "generatorURL":"http://DESKTOP-T7LHFKQ:9090/graph?g0.expr=100+-+100+%2A+windows_os_physical_memory_free_bytes%7Bjob%3D~%22windows%22%7D+%2F+windows_cs_physical_memory_bytes%7Bjob%3D~%22windows%22%7D+%3E+0.1\u0026g0.tab=1",
            "fingerprint":"6a6fb2477f9f7793"
        }
    ],
    "groupLabels":{
        "alertname":"hostMemUsageAlert"
    },
    "commonLabels":{
        "alertname":"hostMemUsageAlert",
        "instance":"192.168.1.81:9182",
        "job":"windows",
        "owner":"b",
        "severity":"page"
    },
    "commonAnnotations":{
        "description":"实例 192.168.1.81:9182 内存使用率 85% (当前值为: 84.79673195230218)",
        "summary":"实例 192.168.1.81:9182 内存使用率过高"
    },
    "externalURL":"http://DESKTOP-T7LHFKQ:9093",
    "version":"4",
    "groupKey":"{}:{alertname=\"hostMemUsageAlert\"}",
    "truncatedAlerts":0
}

使用时请改成自身测试环境

参考资料:

Prometheus - 其他监控方案

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值