Cloud native application made easy: part one, Spring boot
Updated: Apr 13, 2020
This is a series of articles for learning how to develop cloud-native applications with different tools and technics. The series will cover a lot of modern frameworks and production-ready tools for building and deploying microservices such as Spring Boot, Docker, Istio, Kubernetes/OpenShift, QuarKus, etc.

I always believe the best way to learn something new is to jump right in and start doing a simple example to play around it. Once you have experimented with the simple application, you get the necessary knowledge and the most common things that needed to use the particular technology. The objective of this series is to mitigate the theory as much as possible, because, a lot of articles and books already written about Microservices, Docker, OpenShift, etc. If you are an impatient developer like me, the series is for you. The series of posts is intended for programmers who want to get started using Spring Boot, Docker, OpenShift, Istio as quickly as possible, but also want to understand a little bit of what they are doing.
So, in this part of this article, I am going from scratch: developing a very simple microservice application with Spring boot; deploy the service into Docker. In the next part of this series, we will complicate our application and discover how to deploy the service into OpenShift or MiniShift; using service mesh like Istio for adding additional capabilities like routing, resilience, load balancing and so on. Stay tuned.
The complete source code of the article is available in the GitHub repository.
As a developer, you might already have some of these tools described below, but for
completeness, here are the tools that we are going to use:
Step 1. Build a Basic microservice
Get started quickly with Spring Boot by creating a new project that uses the Spring Initializr. Use the following project metadata to generate a Maven project with all the necessary dependencies.
Group: com.blu.std
Artifact: springboot-hello-world
Note that, you will use these above values throughout the rest of this example.
Download generated the project and unarchive it somewhere in your file system. Alternatively, you can download the complete project from the GitHub repository and start modifying it.
Step 2. Maven docker plugin
Add the following Maven plugin to build and deploy a Docker image from the project.
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.33.0</version>
<configuration>
<images>
<image>
<name>spring-boot-helloworld-docker</name>
<build>
<contextDir>${project.basedir}</contextDir>
</build>
<run>
<ports>
<port>8080:8080</port>
</ports>
</run>
</image>
</images>
</configuration>
</plugin>
I'm using the plugin version 0.33.0 and the port 8080 which will be exposed to invoke the REST API outside the container.
Step 3. Add a web controller
In the Spring MVC framework, an HTTP request is handled by a controller. You can add a controller by using the @Controller annotation. Create a new Java class named "DemoController" in the com.blu.std package as follows:
@RestController
public class DemoController {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoController.class);
@RequestMapping("/")
public String index() {
return "Spring Boot Hello-world application for demo!";
}
@RequestMapping("/getQuote")
public String getQuote() {
final String[] todaysQuote = new String[]{
"Today you are you!",
"Today was good.",
"Today is the only day.",
"What is not started today is never finished tomorrow."
};
final int rnd = new Random().nextInt(todaysQuote.length);
return todaysQuote[rnd];
}
}
Here, @RestController is a special type of web @Controller. Spring consider this annotation when handling incoming web request.
The @RequestMapping annotation provides routing information for the web application. It informs Spring that any HTTP request with / path should be routed to the home() method. Accordingly, HTTP request with /getQuote path would be routed to the getQuote() method. The above getQuote() method returns a random today's quote from the array of a string.
Step 4. Build and test the Spring Boot application.
To build the project, run the following command from your favorite terminal.
mvn clean install
At this point, the project is ready to run. Execute the following command from the project home directory.
mvn spring-boot:run
You should see output similar to the following:
21:28:26.030 [main] INFO com.blu.std.App - Spring Boot Hello-World app runs!
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2020-04-12 21:28:26.627 INFO 2673 --- [ main] com.blu.std.App : Starting App on shamim.local with PID 2673 (/Users/shamim/Development/workshop/github/Microservices-nutsAndbolts/CloudNative-application/springboot-hello-world/target/classes started by shamim in /Users/shamim/Development/workshop/github/Microservices-nutsAndbolts/CloudNative-application/springboot-hello-world)
2020-04-12 21:28:26.628 INFO 2673 --- [ main] com.blu.std.App : No active profile set, falling back to default profiles: default
2020-04-12 21:28:27.489 INFO 2673 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-04-12 21:28:27.501 INFO 2673 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-04-12 21:28:27.501 INFO 2673 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.29]
2020-04-12 21:28:27.571 INFO 2673 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-04-12 21:28:27.571 INFO 2673 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 903 ms
2020-04-12 21:28:27.811 INFO 2673 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-12 21:28:27.946 INFO 2673 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2020-04-12 21:28:28.004 INFO 2673 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-12 21:28:28.008 INFO 2673 --- [ main] com.blu.std.App : Started App in 1.883 seconds (JVM running for 2.247)
Open another terminal and run the following URL. Alternatively, you can open a web browser to http://localhost:8080/getQuote.
curl http://localhost:8080/getQuote ;echo
You should see the following output:
What is not started today is never finished tomorrow.
Step 5. Run the Spring Boot application in the native Java way.
You can use Java -jar command to run the application as follows:
java -jar ./target/springboot-hello-world-1.0-SNAPSHOT.jar
To exit the application, press crtl-c.
Step 6. Building docker images
Create a new file with the name Dockerfile and place the file into the home directory of the project. Add the following content into it.
FROM openjdk:8-jre-alpine
RUN mkdir /app
COPY ${project.artifactId}.jar /app
CMD ["java", "-jar", "/app/${project.artifactId}.jar"]
I start my image from a base image called openjdk:8-jre-alpine. You can start your images from any valid images that you pull from the public docker registry. Then I create a directory called app. The next instruction copy the resulted jar file into the /app directory. The final line of the Dockerfile executes the jar file with Java -jar command.
To build the docker image, run the following command:
mvn docker:build
The above command will build and push the image spring-boot-helloworld-docker into the docker local repository. You can check the newly created image in the Docker. Execute the following docker command to list all the top-level images as follows:
docker images
You should see the following output.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
spring-boot-helloworld-docker latest 9ac5a67d5334 2 minutes ago 104MB
yandex/clickhouse-server1 latest 53c7fa8c94e2 2 weeks ago 493MB
yandex/clickhouse-server latest f62e485ae47b 3 weeks ago 492MB
yandex/clickhouse-client latest bf3b797512b0 3 weeks ago 484MB
openjdk 8-jre-alpine f7a292bbb70c 11 months ago 84.9MB
spoonest/clickhouse-tabix-web-client latest e872c1a905d9 23 months ago 245MB
Now that we have our image built, it's time to create a docker container and run the application. Open a new terminal and execute the following command:
docker run -d -p 8080:8080 spring-boot-helloworld-docker
Here, we create a new container from the image and exposed the port 8080 to the outside of the container. Check the container status as follows:
docker ps -a
The output of the above command should be something similar as shown below:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19738d945307 spring-boot-helloworld-docker "java -jar /app/spri…" 5 seconds ago Up 4 seconds 0.0.0.0:8080->8080/tcp friendly_chaum
43c7d05de244 spoonest/clickhouse-tabix-web-client "/bin/sh -c ./start.…" 13 days ago Exited (137) 13 days ago epic_rosalind
d151ce364eee yandex/clickhouse-server1 "/entrypoint.sh" 2 weeks ago Exited (137) 13 days ago some-clickhouse-server1
9adf5b1925f0 yandex/clickhouse-server "/entrypoint.sh" 2 weeks ago Exited (137) 2 weeks ago some-clickhouse-server
Congratulations! You have just finished the first part of the article. Browse to the REST endpoint and you should see the following response.
Today you are you!
Summary
In this blog post, we learned how to build and deploy a basic Spring boot application. First, we developed an application and run on a standalone mode. Later, we built a Docker image through a Maven plugin and deploy it into the docker and run a container based on the image.
Homework.
1. Add a new method into the DemoController Java class which will return an n-th Fibonacci Number from a given number.
2. Build a Docker image with the native Docker command.