Existem várias formas de se monitorar sistemas distribuídos e uma que me agrada bastante pela simplicidade de configuração e confiabilidade é o Spring Boot Admin.
Nesse tutorial criaremos duas aplicações utilizando o Spring Boot, uma que será o servidor de monitoramento e a outra, o cliente que deverá se registrar para ser monitorado.
Também vamos aproveitar para implementar uma camada de segurança utilizando o Spring Security e com o #Maven, poderemos fazer o build das aplicações para serem executadas individualmente.
Versões utilizadas
- Spring Boot: 2.7.10
- Spring Boot Admin Server: 2.7.10
- Spring Boot Admin Client: 2.7.10
Configurando o Servidor
Crie um projeto utilizando o Spring Initilizr com as seguintes dependências:
- Starter Web
- Starter Security
- Admin Starter Server
Confira se as dependências relacionadas ao Spring Boot Admin foram adicionadas:
...
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
</dependency>
...
Adicione a anotação @EnableAdminServer em alguma classe de configuração:
...
@EnableAdminServer
@SpringBootApplication
public class SpringBootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminServerApplication.class, args);
}
}
Configure o comportamento da aplicação utilizando o arquivo application.yaml. Como adicionamos uma camada de segurança, devemos criar um nome de usuário e senha para logarmos no servidor e também será necessário configurar o login para comunicação entre servidor e cliente.
server:
port: 8081
servlet:
context-path: /admin-console
spring:
security:
user:
# Configura o login do servidor.
name: ${SBA_SERVER_USERNAME}
password: ${SBA_SERVER_PASSWORD}
boot:
admin:
client:
# Necessários para que o cliente possa se registrar na api do servidor protegido.
username: ${SBA_SERVER_USERNAME}
password: ${SBA_SERVER_PASSWORD}
instance:
metadata:
user:
# Necessários para que o servidor possa acessar os endpoints protegidos do cliente.
name: ${SBA_CLIENT_USERNAME}
password: ${SBA_CLIENT_PASSWORD}
# LOG
logging:
file:
name: ${user.home}/logs/admin/sba-server.log
level:
root: info
web: info
dev.marksduarte: info
org.springframework: info
charset:
file: utf-8
Agora vamos configurar o Spring Security criando uma classe e desabilitando o proxy dos beans na anotação @Configuration(proxyBeanMethods = false), pois como vamos trabalhar com @Bean autocontido, podemos evitar o processamento da subclasse CGLIB.
Também vamos permitir os acessos às rotas de login e assets e desabilitar a proteção CSRF paras o métodos HTTP POST e HTTP DELETE nas instâncias dos clientes.
package dev.marksduarte.sbaserver;
...
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {
private final AdminServerProperties adminServer;
public SecurityConfig(AdminServerProperties adminServer) {
this.adminServer = adminServer;
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/assets/**")))
.permitAll()
.requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/login")))
.permitAll()
.anyRequest()
.authenticated())
.formLogin(formLogin -> formLogin.loginPage(this.adminServer.path("/login"))
.successHandler(successHandler))
.logout(logout -> logout.logoutUrl(this.adminServer.path("/logout")))
.httpBasic(Customizer.withDefaults())
.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
new AntPathRequestMatcher(this.adminServer.path("/instances"), HttpMethod.POST.toString()),
new AntPathRequestMatcher(this.adminServer.path("/instances/*"), HttpMethod.DELETE.toString()),
new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))));
return http.build();
}
}
Pronto, agora é só rodar a aplicação e conferir se o Admin Server está acessível através do endereço http://localhost:8081/admin-console e logar com o usuário e senha informados na configuração.