A guide to building Java microservices. Part 2
Welcome back to developing cloud-native applications based on microservice architecture. In the first part of the series, we built sample Spring Boot microservices and prepared them for deployment in the cloud. Now it’s time to containerize our Spring application with the help of Docker, a platform for building, deploying, replicating, and shifting container images.
As always, you can use convenient copy-and-paste code snippets below to follow along. And remember that if your company needs expert advice on cost-efficient deployment of Java applications in the cloud, you can always turn to BellSoft engineers for help.
Set up cloud-native infrastructure
The beauty of microservice architecture is that we can create services in different programming languages. However, multilingual projects are not a golden standard, so for the sake of enhanced stability and performance it is better to standardize your application and use a unified stack of technologies.
With that in mind, we are going to concentrate on Java as the primary language for our microservices. To learn more on the topic why Java is perfect for creating cloud-native microservices, refer to Introduction to Cloud-Native Java, a DZone Refcard by BellSoft. And we are moving on.
To deploy our Spring application to the cloud, we need to build infrastructure, which includes Docker and Kubernetes. Docker allows you to to package an application with all of its dependencies into a container and share it across various environments, while preserving security of an isolated system. Other advantages of Docker:
- The possibility to update certain parts of code without the need to take down the whole application;
- Layered images with layers that can be reused for creating new containers;
- Automated deployment;
- The possibility to roll back to the previous version of an image.
Spring Boot offers Cloud-Native Buildpack that generates a Docker Image from a Spring Boot project. Spring Boot also uses BellSoft Liberica JDK as a default JVM for Java applications. Docker enables the deployment into the cloud.
One way to deploy a Spring Boot application or a single microservice in the AWS cloud is by using Docker to create containers with the OCI image format, Amazon ECR as a managed container registry (Docker repository), and Amazon EKS as a managed Kubernetes service. Another way for this is going with AWS Elastic Beanstalk, which offers a Platform as a Service (PaaS) to deploy web applications in the AWS cloud in a managed, auto-scalable way. But this post won’t touch on it.
Unfortunately, for our purposes of a quick demo, we cannot elaborate on other aspects of cloud computing, such as continuous integration/continuous delivery, the classical Java EE way of thinking realized in MicroProfile, building a complex distributed system, and so on. So for now let us focus on the task at hand, namely containerization.
Containerize an application using Docker
First, make sure that Docker is up and running. For details on installing and setting up Docker on your PC, refer to the documentation on the official website.
We are going to use the Spring Boot application we created in the first part of the series. However, if you didn’t follow the series, feel free to visit Spring Initializr and create your own demo application for the purposes of this article. Choose project type, Java version, and add Spring Web dependency as shown in the screenshot below:
Configure your SpringBootDockerApplication in the following way:
@SpringBootApplication
@RestController
public class SpringBootDockerApplication {
@RequestMapping("/")
public String home() {
return "Hello from Docker!";
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDockerApplication.class, args);
}
}
Now, there are two ways of building Docker images. You may build a Docker image by defining a Dockerfile where the various layers of the Docker image need to be defined. The downside of this approach is that creating a Dockerfile needs a good understanding of the Docker technology. To empower the containerization, Cloud Native Buildpacks by Pivotal transform your source code into an OCI (Open Container Initiative) compatible Docker image without the need of a Dockerfile. The Docker container image can then be deployed in any modern cloud.
Paketo.io is a Cloud Foundry project and one of the most popular implementations of Cloud Native Buildpacks. It can transform the source code of major programming languages into a container image.
Spring Boot natively supports the buildpacks that create an image with BellSoft Liberica JDK. It also looks at the build.gradle
file and the Spring configuration file to build the Docker image.
Go to the Customer or your Spring demo application directory and run
./gradlew bootBuildImage
If you built your app using Maven, run
./mvnw spring-boot:build-image
The command will build a Docker image. For our demo, the Customer microservice container image looks as such: docker.io/library/microservice-customer:1.0.0
Then we create one for the Order microservice going through the similar step: docker.io/library/microservice-order:1.0.0
We can run the microservice with a Docker command — here’s one for the Customer microservice:
docker run -d -p 8080:8080 docker.io/library/microservice-customer:1.0.0
And here is one for your Spring demo app:
docker run -d -p 8080:8080 springio/gs-spring-boot-docker
Visit the http://localhost:8080 to make sure that your application is running. Although Docker Desktop for Apple Silicon is available as a GA release, you need to install Rosetta to run Docker images. For further information on running Java applications in Docker on Apple Silicon, read our article dedicated to this topic.
If the Docker container started successfully, you’ll see the container ID as follows (in your case, it will be different as it is a unique ID):
c1c3925595047ff5744507dd0abc05b1063f7dc884c3e2a4c9864633a2780d2b
Now, the following lines will appear in the Docker container log file:
> docker logs c1c3925595047ff5744507dd0abc05b1063f7dc884c3e2a4c9864633a2780d2b
2021-03-03 22:01:21.116 INFO 1 --- [ngodb.net:27017] org.mongodb.driver.cluster : Setting max set version to 2 from replica set primary clustermicroservice-shard-00-01.fzatn.mongodb.net:27017
2021-03-03 22:01:21.116 INFO 1 --- [ngodb.net:27017] org.mongodb.driver.cluster : Discovered replica set primary clustermicroservice-shard-00-01.fzatn.mongodb.net:27017
2021-03-03 22:01:21.184 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-03-03 22:01:21.240 INFO 1 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]
2021-03-03 22:01:21.383 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer
Please note that we are using the port forwarding (-p 8080) so that we can connect with the containerized microservice from outside (e.g., from a local machine).
It is possible to check whether the Docker container is running by sending a GET request to its liveness or “health” endpoint as described in detail in Part 1:
curl --location --request GET 'http://localhost:8080/customer/api/v1/health'
It returns the following response:
< {"status":"UP"}
To shut the Docker container, use docker stop
and the container ID, for example:
docker stop c1c3925595047ff5744507dd0abc05b1063f7dc884c3e2a4c9864633a2780d2b
Conclusion
In this part of our series on building Java microservices, we learned how to containerize our Spring Boot application using Docker. In the next part, we will take advantage of Amazon Web Services, which is the number one public cloud provider with a 32% market share, and learn how to deploy Docker containers into the cloud. See you soon!