This article is in continuation with our last article Spring Boot Actuator. We discussed actuators in great detail for application managing and monitoring with spring security integrated.
In this article, we will discuss Spring Boot Admin that utilizes those Actuator endpoints to provide a clean UI to visualize our application metrics and state. First, we will create a simple client and server app with a monolithic approach that will help to understand the Spring Boot Admin core concepts and then we will extend it to support monitoring of microservices-based architecture apps in cloud.
What is Spring Boot Admin
Spring Boot Admin is a web app developed by codecentric which provides an admin console for the managing and monitoring of spring boot applications by consuming Actuator REST endpoints.
There are mainly 2 starter projects for this - server and client. The server project is the web app itself that provides the admin console whereas client apps are all the microservices running for which we want to set up the monitoring.
We configure the server location in the client app and the client app registers itself to the server app and then the server app utilizes client's Actuator endpoints to fetch all the app metrics.
We can also configure Server app to discover client apps from a discovery server assuming all the client apps register themselves to a central discovery server. This is typically a distributed system setup.
Setting Up Spring Boot Admin Server
Head over to start.spring.io to generate our sample server project with required dependencies including Spring Boot Admin Server artifacts.
Let us check what are the maven dependencies that we got in our pom.xml.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-dependencies</artifactId> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Now, we can import it into our IDE and we can annotate our spring boot main class with @EnableAdminServer
to enable the spring boot admin server.
Noe, let us run our SpringBootAdminServerApplication.java to see spring boot admin server in action.
@EnableAdminServer @SpringBootApplication public class SpringBootAdminServerApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAdminServerApplication.class, args); } }application.properties
server.port=8085
With this much of configuration, our admin server is now running at port 8085.
As we can see there are no applications registred as we have not registerd any clients and hence the console is empty. Let us create our client app.
Setting Up Spring Boot Admin Client
Head over to start.spring.io to generate our sample server project with required dependencies including Spring Boot Admin client artifcat and Actuator artifact.
Spring boot actuator will expose more then 16 endpoints to manage and monitor our application and these REST endpoints will be consumed by our admin server to show the different metrics of our client app in the admin console.
Now, let us checkout our pom.xml in case you only want to add dependencies in your existing project.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-dependencies</artifactId> <version>${spring-boot-admin.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
With default condifuration of Actuator, all the endpoints are enabled except /shutdown
but only 2 endpoints are exposed and they are /health
and /info
. To expose all these actuator endpoints we have to add below configuration on our application.properties. The details of it can be found in my last actuator article here.
spring.application.name=devglan-demo spring.boot.admin.client.url=http://localhost:8085 management.endpoints.web.exposure.include=* management.endpoint.health.show-details=always info.app.developer=Dhiraj info.app.java.source=1.8 info.app.java.target=1.8
spring.boot.admin.client.url is the URL of our admin server app which is running at port 8085.
The application name that we configured in our properties file will be shown in our admin console to uniquely identify multiple running applications. By default, the value for show-details is never and we can configure its value as always. We have also added some custom info about our app.
With this much of configuration let us run our client app and restart server app and check the admin console.
@SpringBootApplication public class SpringBootAdminClientApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAdminClientApplication.class, args); } }
Now, we can check out http://localhost:8085/wallboard to see our client application registered in the admin console and we can see different metrics of the client app and manage and monitor it through the admin console.
Service Discovery of Client Apps
Till this point, we have our spring boot admin server and client are running perfectly fine but we have a manual hardcoded configuration of the admin server URL in our client app. The admin server also provides the service discovery of the client app and let us configure the same in our admin server app which would be essential in a microservice-based architecture application.
You can refer this to discover all the concepts of microservices that we have discussed so far.
We will be using the Netflix Eureka server for our service discovery. We have already discovered the concepts and configuration behind this Spring Boot Eureka Server in my previous article and hence let's directly jump into code implementation.
First, let us run our service discovery instance. Head over to start.spring.io and generate a project for our discovery server and import it into our IDE.
Below are the dependencies included in pom.xml. Here, the spring cloud version is Hoxton.SR6.
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Below is our configuration parameters for discovery server.
spring: application: name: discovery-service eureka: client: eureka-server-connect-timeout-seconds: 5 enabled: true fetch-registry: false register-with-eureka: false log-delta-diff: false instance: prefer-ip-address: true server: enable-self-preservation: true server: port: 8761
Let us annotate the SpringCloudEurekaServerApplication.java with the annotation @EnableEurekaServer and start it as a Spring Boot app. Our central discovery server is up now at http://localhost:8761. Let us configure the admin server and client app to make use of it.
Enabling Service Discovery in Admin Server App
We need to add below maven dependency in our server app pom.xml file to enable the service discovery.
<spring-cloud.version>Hoxton.SR6</spring-cloud.version> <dependencies> ... <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependencies> <dependencyManagement> ... <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>application.yml
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka register-with-eureka: false registry-fetch-interval-seconds: 5 instance: preferIpAddress: true spring: application: name: admin-server server: port: 8085
Now, let us enable our spring boot admin client to register itself to discovery server. For this purpose, let us add the same dependency that we added for admin server app and below is the yml file. This yml file has register-with-eureka set to true so that the admin client app registers itself to the discovery server.
Once our client app registers itself to the discovery server then the admin server project will pick this server info from the discovery server and start showing its metrics in the admin console.
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka register-with-eureka: false registry-fetch-interval-seconds: 5 instance: preferIpAddress: true
This is pretty much the configuration of spring Boot Admin in a microservice based apps. Let us start our client app and check the admin console.
Configuring Notifications in Spring Boot Admin
There are various notification options that we can configure in our admin server app. There are some out of the box notifications support provided by the admin server which we can configure in our app whereas we can also create custom notifications as per the requirement.
Below are the notifiers which can be configured in no time with simple application.properties entries.
- slack
- pagerduty
- HipChat
- OpsGenie
- LetsChat
- Microsoft Teams
- Telegram
- Discord
We can also add custom notifications apart from these default ones that we will discuss later.
Email Notification Configuration
Email notification can be used to trigger email notifications whenever the state of our application changes such as UP or DOWN. To do so first we need to configure mailing system in our admin server project.
Let us add spring boot starter mail artifact in our pom.xml and configure to use Gmail as our mail client.
pom.xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
We need to make below mail related entries in application.properties. This will send email to admin@gmail.com on app status change.
spring.mail.host=smtp.gmail.com spring.mail.port=587 spring.mail.username=username spring.mail.password=password spring.boot.admin.notify.mail.to=admin@gmail.com spring.mail.properties.mail.smtp.starttls.enable=true
Custom Notifications in Spring Boot Admin
Spring boot admin works based on event-sourcing. Hence, whenever any of the state changes, an event is triggered and we can create our custom Notifier Bean to listen to those event and can send our custom notifications.
Below is an example code from the official source of Spring Boot Admin for custom notifications.
The RemindingNotifier sends reminders for down/offline applications and delegates the sending of notifications to another notifier.
@Configuration public class NotifierConfig { private final InstanceRepository repository; private final ObjectProvider> otherNotifiers; public NotifierConfig(InstanceRepository repository, ObjectProvider
> otherNotifiers) { this.repository = repository; this.otherNotifiers = otherNotifiers; } @Bean public FilteringNotifier filteringNotifier() { CompositeNotifier delegate = new CompositeNotifier(this.otherNotifiers.getIfAvailable(Collections::emptyList)); return new FilteringNotifier(delegate, this.repository); } @Primary @Bean(initMethod = "start", destroyMethod = "stop") public RemindingNotifier remindingNotifier() { RemindingNotifier notifier = new RemindingNotifier(filteringNotifier(), this.repository); notifier.setReminderPeriod(Duration.ofMinutes(10)); notifier.setCheckReminderInverval(Duration.ofSeconds(10)); return notifier; } }
Apart from this we can also add our custom notifier by extending AbstractEventNotifier or AbstractStatusChangeNotifier.
public class CustomNotifier extends AbstractEventNotifier { private static final Logger LOGGER = LoggerFactory.getLogger(LoggingNotifier.class); public CustomNotifier(InstanceRepository repository) { super(repository); } @Override protected MonodoNotify(InstanceEvent event, Instance instance) { return Mono.fromRunnable(() -> { if (event instanceof InstanceStatusChangedEvent) { LOGGER.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus()); } else { LOGGER.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(), event.getType()); } }); } }
Securing Spring Boot Admin with Spring Security
Spring Boot admin provides easy integration with spring security to secure our admin console. Adding a basic authentication to the admin console only requires some properties update but it is not only limited to basic authentication as it provides integration with OAUTH2, JWT or traditional session-based authentication.
We have discussed securing spring boot admin server and client in detail in my next article here.
Conclusion
In this article, we discussed about Spring Boot Admin and created separate projects for sample admin server and client and performed the client discovery through Netflix Eureka.