As a Java Developer I am sure you must have heard of spring boot and probably Docker as well.
Docker is phenomenon that has taken DevOps world by a storm.
Every new project and old one’s too are migrating to containers.
In this tutorial I will cover brief description of Docker containers and 5 essential tips that will help you get most out of Docker with Sprin Boot.
Source code of the demo project is at
https://github.com/avinash10584/spring-boot-docker-demo
So what are these containers ?
Many view containers as virtual machines. They’re not. Well, kind of not.
A container is a virtual walled environment for your application. It’s literally a ‘container’ inside the host OS.
Thus your application works like it is in its own self contained environment, but it’s actually sharing operating system resources of the host computer. Because of this, containers are more resource efficient than full-blown virtual machines.
Clearly, this is more efficient computing than running a full blown Virtual Machine.
Docker is an open-source project for automating the deployment of applications as portable, self-sufficient containers that can run on the cloud or on-premises.
Because containers require far fewer resources (for example, they don’t need a full OS), they’re easy to deploy and they start fast. This allows you to have higher density, meaning that it allows you to run more services on the same hardware unit, thereby reducing costs.
This efficient mechanism is used by most cloud providers to save them of resources.
Google, Amazon and Microsoft deploy millions of containers every day.
Let’s begin this tutorial of deploying spring boot app on docker and 5 awesome tips for your docker build,
Step 1: Create a Hello World App Spring Boot application
Our first step is to create a demo hello world application.
I will be using below git repository for the spring boot app.
https://github.com/avinash10584/spring-boot-docker-demo.git
If you are interested in learning about creating a beginner’s spring boot app have a look at this article.
https://codingfullstack.com/java/spring-boot/spring-boot-microservice-cloud-mongodb/part-1/
Step 2 : Adding Dockerfile
If you have downloaded github repo above, you would notice that we are using Spring Boot 2.3.0
Spring Boot 2.3.0 comes with several changes in it’s support for docker.
The most important one is using the layered design of docker to avoid large size of spring boot apps. We will look at that in tips section of this tutorial.
Let’s add a sample dockerfile
Run mvn package
from your IDE or terminal to generate the spring boot fat jar.
Make sure you have spring boot maven plugin added to generate the fat jar.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Now let’s run below docker command to build the image,
docker build -t learnings/spring-boot-docker-demo .
This will create an image in Docker registry,
Let’s run this Docker image as a container,
docker run -p 3050:3050 --name spring-boot-docker-demo -t learnings/spring-boot-docker-demo
We have our basic docker container running, your can verify by going to browser at http://localhost:3050
We can also see list of running containers docker ps
We have created a very basic setup of our spring boot project.
Let’s look at the Tips and Tricks part of the article now and improve our docker support in the app,
Tip 1 # : Using Spring profile
Spring profiles is a great feature of spring, it allows you to manage individual resources in application.yml
for different environments.
With Docker you can simply pass the spring profile in command line,
I have added the resource application-prod.properties
to the project.
app:
message: Hello World Production
Let’s start docker container now with below command, notice we are passing spring profile.
docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 3050:3050 -t learnings/spring-boot-docker-demo
Verify that you see the message from application-prod.properties
at http://localhost:3050/
Tip 2 # : Save memory with small footprint using Docker layers
Spring Boot 2.3.0 comes with the support for building layers in your spring boot far jar file.
These layers allow you to reduce your image sizes as docker will cache each layer.
To add this to our spring boot project, add the below in your spring boot maven plugin,
<configuration>
<layers>
<enabled>true</enabled>
<includeLayerTools>true</includeLayerTools>
</layers>
</configuration>
Only works in Spring Boot version >= 2.3.0
Now let’s see our jar content,
java -Djarmode=layertools -jar .\spring-boot-docker-demo-0.0.1-SNAPSHOT.jar list
Now lets extract them and add to our docker file,
You should see something like below in output,
Docker layers allows us to keep our build images small in size as we make changes to our application.
If our application code is changed then only application/application layer is affected and other layers stay cached.
You can verify it by making changes to the RestController and running the docker build.
docker build -t learnings/spring-boot-docker-demo .
Check the history of your image to notice that the newly created images are in KB’s,
docker history learnings/docker-spring-boot-demo
Tip 3 # : Build docker image from Maven Wrapper
Our docker build seems to be working but it is missing one key part,
We are creating our fat jar files manually and then going to terminal to run docker build
This will not work with your CI/CD pipelines as you would need to do your maven build in your CI/CD tool.
We can combine these two steps in either direction,
We can use a maven plugin to generate the docker image during maven build
Or,
We can use maven wrapper to call maven build from inside our Dockerfile.
I prefer using maven wrapper so I will use that here,
Make sure you have Maven before below steps,
Go to your project directory and run mvn -N io.takari:maven:wrapper
This will install the maven wrapper in your project. Maven 3.7 plans to integrate this to Maven by default.
Add maven wrapper instructions to Dockerfile and build again
No need to run maven build anymore before bjuilding your docker images.
Tip 4 # : Use a separete docker user
Docker runs it’s command in container as a root user,
Just as in classic VM-deployments, processes should not be run with root permissions. Instead the image should contain a non-root user that runs the app.
In a Dockerfile, this can be achieved by adding another layer that adds a (system) user and group, then set it as the current user (instead of the default, root):
RUN addgroup demogroup && adduser --ingroup demogroup --disabled-password demo
USER demo
Tip 5 # : Caching build process using Docker build cache
We have added maven build process to our Dockerfile but there is one peformance concern.
Each time we run docker bulid
, it runs and downloads the maven packages as it creates a new container every time.
Docker has an experimental feature to avoid this,
You can add below as your first line in Dockerfile to enable experimental features.
# syntax=docker/dockerfile:experimental
We need to also add below to specify that we want to cache the mount before we run maven build
RUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests
Our final Dockerfile loos like below,
If you run it you will probably see the error,
Error response from daemon: Dockerfile parse error line 13: Unknown flag: mount
We need to run the docker build with a special command to enable experimental features,
DOCKER_BUILDKIT=1 docker build -t learnings/spring-boot-docker-demo .
First time I ran this, my maven build step took almost a minute,
=> [bulid 7/10] COPY src src 0.2s
=> [bulid 8/10] RUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests **57.3s**
=> [bulid 9/10] RUN cp /application/target/*.jar app.jar
On the next run I could see the builds running almost instantaneously,
=> CACHED [bulid 8/10] RUN --mount=type=cache,target=/root/.m2 ./mvnw install -DskipTests 0.0s
=> CACHED [bulid 9/10] RUN cp /application/target/*.jar app.jar 0.0s
=> CACHED [bulid 10/10] RUN java -Djarmode=layertools -jar app.jar extract 0.0s
0.0s
We have successfully reduced build time in our docker images.
That’s all folks, as you can see we have implemented a spring boot app from scratch and learned the tips that will help ypu take your build with Docker to next level.
If you have any questions or feedback don’t hesitate to write your thoughts in the comments/responses section.
For issues related to code, feel free please create an issue directly in GitHub repository.
https://github.com/avinash10584/spring-boot-docker-demo
Thank you for reading! If you enjoyed it, please clap 👏 for it.