Spring Boot Admin 监控系统

SpringBoot实现微服务化是很多项目的技术选型,那么对SpringBoot项目的健康度、运行指标、日志信息、线程状况等进行的监控尤为重要。

一、简介

    如何对SpringBoot项目进行监控呢?比如健康度、运行指标、日志信息、线程状况等信息,本文介绍的是利用 Spring Boot Admin 项目进行监控。

    Spring Boot Admin 这个项目是在Spring原生的监控插件Actuator的基础上进行了一些些改动,尤其是改变了不太美观的 UI,所以说这是一套成熟的监控系统,而且几乎可以拆箱即用。

    小插曲,这个项目是德国的一家叫做 codecentric 的软件公司写的,这家公司写了蛮多的 Spring Boot 周边插件,值得一看,地址:https://github.com/codecentric

    最后,附上文档地址,文档写得很直观、易懂:http://codecentric.github.io/spring-boot-admin/1.5.7/#_what_is_spring_boot_admin

    本文的 server 端代码地址:https://github.com/goldenJet/SpringBootAdminServer.git。</p>

二、应用

    1)整体架构

        由一个服务端和多个客户端组成。

        服务端就是我们单独构建的一个 Spring Boot Admin 项目,用于监控我们的需要被监控的项目。

        客户端就是我们自己的 SpringBoot 项目,即需要被监控的项目。

        我们只需要在客户端内配置好服务端的地址,然后客户端向服务端发送被监控的请求,之后服务端便会定时请求客户端的接口获得客户端的应用状态信息。

    2)服务端实现

        ① 新建一个 SpringBoot 项目,引入 Spring Boot Admin starter Server 的包,

1
2
3
4
<dependency>
&nbsp;&nbsp;&nbsp;&nbsp;<groupId>de.codecentric</groupId>
&nbsp;&nbsp;&nbsp;&nbsp;<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

        你进入源码会发现,这一个 starter 其实是引入了两个重要的包:

1
2
3
4
5
6
7
8
9
10
<dependency>
&nbsp;&nbsp;&nbsp;<groupId>de.codecentric</groupId>
&nbsp;&nbsp;&nbsp;<artifactId>spring-boot-admin-server</artifactId>
&nbsp;&nbsp;&nbsp;<version>1.3.2</version>
</dependency>
<dependency>
&nbsp;&nbsp;&nbsp;<groupId>de.codecentric</groupId>
&nbsp;&nbsp;&nbsp;<artifactId>spring-boot-admin-server-ui</artifactId>
&nbsp;&nbsp;&nbsp;<version>1.3.2</version>
</dependency>

        ② 需要在启动类上面增加注解 @EnableAdminServer

1
2
3
4
5
6
7
@SpringBootApplication
@EnableAdminServer
public&nbsp;class&nbsp;SpringBootAdminDemoApplication&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SpringApplication.run(SpringBootAdminDemoApplication.class,&nbsp;args);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

        ③ 在application.properties 中指定端口

1
2
&nbsp;&nbsp;&nbsp;&nbsp;server.port=8081
&nbsp;&nbsp;&nbsp;&nbsp;endpoints.sensitive=false

        其实经过①②两步的代码逻辑,监控server就已经写好了,启动项目,访问 localhost:8081即可访问监控server的UI。

            springBootAdmin01.png


    3)客户端实现

        服务端已经部署好了,接下来配置客户端。

        ① 客户端引入 client 包:

1
2
3
4
5
<dependency>
&nbsp;&nbsp;&nbsp;&nbsp;<groupId>de.codecentric</groupId>
&nbsp;&nbsp;&nbsp;&nbsp;<artifactId>spring-boot-admin-starter-client</artifactId>
&nbsp;&nbsp;&nbsp;&nbsp;<version>1.5.7</version>
</dependency>

        ② 修改配置:

1
2
spring.boot.admin.url=http://localhost:8080&nbsp;&nbsp;
management.security.enabled=false

            说明:

                配置第一行交代了服务端的项目访问地址,

                第二行:由于自Spring1.5开始,所有的 Endpoint都是默认安全的,即关闭的,所以要手动设置成 false,

            官方说明文档:

            springBootAdmin02.png

            到此,其实客户端也已经简单配置好了,启动项目,重新访问服务端网页,发现有服务已经注册进来了,然后我们便可以查看该服务的各项基本信息:

springBootAdmin03.png

    4)服务端开启安全校验

        注意,此时的服务端是不需要进行登录的,所以如果我们想添加一个登录页面和登录按钮,则需要增加一些额外的配置,效果如图:

        springBootAdmin04.png

        实现方式:

        ① 添加登录的ui包和 Spring Security 包:

1
2
3
4
5
6
7
8
9
10
<dependency>
&nbsp;&nbsp;&nbsp;&nbsp;<groupId>de.codecentric</groupId>
&nbsp;&nbsp;&nbsp;&nbsp;<artifactId>spring-boot-admin-server-ui-login</artifactId>
&nbsp;&nbsp;&nbsp;&nbsp;<version>1.5.7</version>
</dependency>
<dependency>
&nbsp;&nbsp;&nbsp;&nbsp;<groupId>org.springframework.boot</groupId>
&nbsp;&nbsp;&nbsp;&nbsp;<artifactId>spring-boot-starter-security</artifactId>
&nbsp;&nbsp;&nbsp;&nbsp;<version>1.4.5.RELEASE</version>
</dependency>

        ② 在配置文件中添加默认的登录用户名和密码:

1
2
security.user.name=admin
security.user.password=admin

        ③ 添加 SpringSecurity 的配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
&nbsp;*&nbsp;@Author:&nbsp;Jet
&nbsp;*&nbsp;@Description:&nbsp;配置HTTPBASIC权限验证
&nbsp;*&nbsp;@Date:&nbsp;2018/5/21&nbsp;22:07
&nbsp;*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled&nbsp;=&nbsp;true,&nbsp;prePostEnabled&nbsp;=&nbsp;true,&nbsp;proxyTargetClass&nbsp;=&nbsp;true)
public&nbsp;class&nbsp;WebSecurityConfig&nbsp;extends&nbsp;WebSecurityConfigurerAdapter&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;configure(WebSecurity&nbsp;web)&nbsp;throws&nbsp;Exception&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//忽略css.jq.img等文件
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;web.ignoring().antMatchers("/**.html",&nbsp;"/**.css",&nbsp;"/img/**",&nbsp;"/**.js",&nbsp;"/third-party/**");
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;protected&nbsp;void&nbsp;configure(HttpSecurity&nbsp;http)&nbsp;throws&nbsp;Exception&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;http.csrf().disable()&nbsp;//HTTP&nbsp;with&nbsp;Disable&nbsp;CSRF
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.authorizeRequests()&nbsp;//Authorize&nbsp;Request&nbsp;Configuration
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.antMatchers("/login",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/api/**",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/heapdump",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/loggers",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/liquibase",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/logfile",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/flyway",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/auditevents",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/**/jolokia",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/health/**",&nbsp;"/info/**").permitAll()&nbsp;//放开"/api/**":为了给被监控端免登录注册并解决Log与Logger冲突
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.and()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.authorizeRequests()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.antMatchers("/**").hasRole("USER")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.antMatchers("/**").authenticated()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.and()&nbsp;//Login&nbsp;Form&nbsp;configuration&nbsp;for&nbsp;all&nbsp;others
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.formLogin()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.loginPage("/login.html")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.loginProcessingUrl("/login").permitAll()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.defaultSuccessUrl("/")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.and()&nbsp;//Logout&nbsp;Form&nbsp;configuration
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.logout()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.deleteCookies("remove")
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.logoutSuccessUrl("/login.html").permitAll()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.and()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.httpBasic();
&nbsp;&nbsp;&nbsp;&nbsp;}
}

            搞定!


三、注意点

    以下都是一些配置的小问题,在本文开头提供的文档地址内都能找到对应的解决方案。

    ① 如果 client 端有类似 Spring Security 之类的权限控制程序,则会造成 /health/ 等接口的访问被拒绝,因为服务端监控客户端应用的状态是调用如下接口来查看信息的,所以这些接口的限制需要放开,如SpringSecurity 则可以进行如下的配置:

        springBootAdmin05.png

        springBootAdmin06.png

    ② client 端服务的注册,默认是以当前系统的用户名来注册的,即会以 hostname:port 来注册

        所以要改成以 IP 地址的形式来注册,只需要在 client 端添加主配置:spring.boot.admin.client.prefer-ip=true 即可。

        但是如果以 IP 地址的形式访问,由于 IP 的获取时通过 InetAddress.getLocalHost() 来获取的,所以有时会出现 IP地址是 127.0.0.1 的情况,从而导致server 端无法请求到 client 端的程序。

        终极解决办法:

        在 client 端使用如下配置指定项目的地址:spring.boot.admin.client.service-base-url=http://192.168.154.77:9090


    ③ 我们也可以在 client 端命名该客户端

        spring.boot.admin.client.name=adminClient

            




------ 本文结束 感谢阅读 ------
0%