When working with Microservices, you need a way to monitor your services. The cloud providers like AWS have their proprietary solutions, which may be perfectly fine. However, in this post, we will build a Spring Boot Admin Server to monitor Spring Boot and Python applications. (The Admin Server cal also provides notifications.) We will package the server into a docker image for easy deployment. Let’s get started!
Head over to https://start.spring.io/ and add the dependency Codecentric’s Spring Boot Admin (Server). Choose a Maven project type and use the Spring Boot version 2.3.3. Import the project into SpringToolSuite or your favorite IDE. Call the artifact Admin if you want to follow this example. Now add @EnableAdminServer to the main method like this.
src/main/java/com/example/AdminApplication.java
package com.example.Admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
@EnableAdminServer
@SpringBootApplication
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
Let’s run our application on a different port by setting this application property.
src/main/resources/application.properties
server.port: 8411
Start it up and open a browser to http://localhost:8411. You will see the admin console.
Naturally, you will want to secure this endpoint in production. Don’t forget this step.
Head over to https://start.spring.io/ and add the dependencies Codecentric’s Spring Boot Admin (Client), Spring Web, and Spring Boot Actuator. Choose a Maven project type and use the Spring Boot version 2.3.3. Import the project into SpringToolSuite or your favorite IDE. Call the artifact WebApp if you want to follow this example.
Add a simple controller
src/main/java/com/example/WebApp/RootComtroller.java
package com.example.WebApp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class RootController {
@GetMapping("/")
@ResponseBody
public String helloWorld() {
return "Hello World";
}
}
Add some application properties.
src/main/resources/applications.properties
server.port: 8080
# Use a dirrerent port for acuator
management.server.port: 8081
# only allow local connections
management.server.address: 127.0.0.1
management.endpoints.web.exposure.exclude=
management.endpoints.web.exposure.include=*
spring.boot.admin.client.url=http://localhost:8411
Start our new WebApp application and our Admin application. The code from the repo uses a multi-module pom, so you will run them like this
mvn install
mvn spring-boot:run -pl admin
mvn spring-boot:run -pl webapp
In the example, we will build a Eureka Discovery server. Let’s head over to https://start.spring.io/ again and add the dependency Eureka Discovery Client. Choose a Maven project type and use the Spring Boot version 2.3.3. Import the project into SpringToolSuite or your favorite IDE. Call the artifact Eureka if you want to follow this example. Now add @EnableEurekaServer to the main method like this.
src/main/java/com/example/EuekaApplication.java
package com.example.eueka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EuekaApplication {
public static void main(String[] args) {
SpringApplication.run(EuekaApplication.class, args);
}
}
Now add these properties.
src/main/resources/application.properties
eureka.client.registerWithEureka = false
eureka.client.fetchRegistry = false
server.port = 8761
That’s all there is to it. We now have a Eureka discovery server.
Now we need to make some changes to the Admin Server and our simple test Web Application.
Let’s start with the Admin Server by adding a dependency to the Eureka Client.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath />
</parent>
<groupId>com.example</groupId>
<artifactId>Admin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Admin</name>
<description>Spring Boot Admin</description>
<properties>
<java.version>11</java.version>
<spring-boot-admin.version>2.3.0</spring-boot-admin.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Now enable the Eureka client.
src/main/java/com/example/AdminApplication.java
package com.example.Admin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
@EnableEurekaClient
@EnableAdminServer
@SpringBootApplication
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
And finally, update the application properties.
src/main/java/com/example/AdminApplication.java
server.port: 8411
spring.application.name=adminServer
eureka.client.serviceUrl.defaultZone=http://${registry.host:localhost}:${registry.port:8761}/eureka/
management.endpoints.web.exposure.exclude=
management.endpoints.web.exposure.include=*
Moving on to our test WebApp, we need to make the same changes.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>WebApp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>WebApp</name>
<description>Spring Boot Admin</description>
<properties>
<java.version>11</java.version>
<spring-boot-admin.version>2.3.0</spring-boot-admin.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Now enable the Eureka client.
src/main/java/com/example/WebAppApplication.java
package com.example.WebApp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class WebAppApplication {
public static void main(String[] args) {
SpringApplication.run(WebAppApplication.class, args);
}
}
And finally, update the application properties.
src/main/java/com/example/AdminApplication.java
server.port: 9000
spring.application.name=sampleAdminClient
# Use a dirrerent port for acuator
management.server.port: 9001
# only allow local connections
# management.server.address: 127.0.0.1
management.endpoints.web.exposure.exclude=
management.endpoints.web.exposure.include=*
eureka.client.serviceUrl.defaultZone=http://${registry.host:localhost}:${registry.port:8761}/eureka/
If you want to test it now, you could start all three servers but let’s go one step further and Dockerize the servers to run them all with docker-compose.
If you checked out the source repository, it contains a multi-module pom with all three projects. You can use Maven to create docker images of all three projects like this.
Note: you should replace jameskolean
with your docker hub ID.
mvn -pl admin spring-boot:build-image -Dspring-boot.build-image.imageName=jameskolean/admin-spring-boot
mvn -pl eureka spring-boot:build-image -Dspring-boot.build-image.imageName=jameskolean/eureka
mvn -pl webapp spring-boot:build-image -Dspring-boot.build-image.imageName=jameskolean/admin-spring-boot-webapp-client
This step is not necessary, but you could push these to the docker hub. I’ll push the latest to docker and make images public; these are the commands.
docker login
docker push jameskolean/eureka
docker push jameskolean/admin-client
docker push jameskolean/admin-spring-boot-webapp-client
I also pushed a version tagged image.
docker tag jameskolean/eureka jameskolean/eureka:0.1.0
docker tag jameskolean/admin-spring-boot jameskolean/admin-spring-boot:0.1.0
docker tag jameskolean/admin-spring-boot-webapp-client jameskolean/admin-spring-boot-webapp-client:0.1.0
docker push jameskolean/eureka:0.1.0
docker push jameskolean/admin-spring-boot:0.1.0
docker push jameskolean/admin-spring-boot-webapp-client:0.1.0
Create a docker-compose and run all three services.
docker-compose.yml
version: '3'
services:
eureka-serviceregistry:
image: jameskolean/eureka
container_name: eureka-1
ports:
- '8761:8761'
admin:
image: jameskolean/admin-spring-boot
ports:
- '8411:8411'
environment:
- eureka.client.serviceUrl.defaultZone=http://eureka-serviceregistry:8761/eureka
webapp:
image: jameskolean/admin-spring-boot-webapp-client
container_name: webapp-1
ports:
- '9000:9000'
- '9001:9001'
environment:
- eureka.client.serviceUrl.defaultZone=http://eureka-serviceregistry:8761/eureka
Run it with this command.
docker-compose up
use Ctrl-C to stop or docker-compose down
Browse to these URLs. It may take a minute for all the services to register, so be patient.