In this article, you’ll learn how to build a docker image for running a spring boot application. I’ll first give you a brief idea of docker, then we’ll create a docker image for our spring boot application, and run it locally. Finally, we’ll push the docker image to docker hub.
So let’s get started!
A Quick introduction to Docker
Docker is a software platform that enables developers to develop, ship, and run applications anywhere with the help of containers.
Now what the heck is a container and what problem does docker solve with containers?
Well, Let’s understand that by asking a few questions. Does any of the following sound familiar to you?
“It runs on my machine!!”
“I think your Tomcat version is outdated!
“I don’t want to install 10 different libraries and tools before being able to run your application. Can’t it come in a packaged form with all the libraries and tools it needs?”
“We have applications written in different languages, tools, system libraries, and environments. Is there a way we can run them independently on the same infrastructure?”
I bet some of them does.
Docker solves these problems by creating a lightweight, standalone, executable package of your application that includes everything needed to run it including the code, the runtime, the libraries, tools, environments, and configurations.
These standalone executable packages are called docker images. And a running instance of a docker image is called a docker container.
Now, these container images can be shared, shipped and run anywhere in any environment. They will behave exactly the same regardless of the environment they run in.
Moreover, you can run multiple containers of completely different configurations on the same infrastructure. All containers are completely isolated and run independently from each other.
Cool, isn’t it? Well, let’s now learn how to run a spring boot application inside a docker container. But before that, go ahead and install docker community edition on your platform.
Also Read: What is a Container and What is the difference between Containers and Virtual Machines
Spring Boot with Docker: Dockerizing a Spring Boot application
1. Downloading the application to Dockerize
In this article, we’ll dockerize a web socket based group chat application built with spring boot. You can download the application from github -
$ git clone https://github.com/callicoder/spring-boot-websocket-chat-demo
There is a live demo of this application hosted on Heroku. Go check that out if you’re curious. You can also read the tutorial to learn how to build it from scratch.
All right! Let’s now learn how to create a docker image of this spring boot application.
2. Defining a docker image with Dockerfile
Go to the application’s root directory and create a new file named Dockerfile
.
$ cd spring-boot-websocket-chat-demo
$ touch Dockerfile
Dockerfile is where we define the docker image and specify all the configurations required to run the app. Following is the Dockerfile for our spring boot application -
# Start with a base image containing Java runtime
FROM openjdk:11
# Add Maintainer Info
LABEL maintainer="callicoder@gmail.com"
# Add a volume pointing to /tmp
VOLUME /tmp
# Make port 8080 available to the world outside this container
EXPOSE 8080
# The application's jar file
ARG JAR_FILE=target/websocket-demo-0.0.1-SNAPSHOT.jar
# Add the application's jar to the container
ADD ${JAR_FILE} websocket-demo.jar
# Run the jar file
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/websocket-demo.jar"]
The Dockerfile is very simple and declarative. Let’s go through each line of the Dockerfile and understand the details.
FROM: A docker image can use another image available in the docker registry as its base or parent image. In the above example, we use the
openjdk:11
image as our base image.LABEL: The
LABEL
instruction is used to add metadata to the image. In the above Dockerfile, we have added some info about themaintainer
of the image throughLABEL
instruction.VOLUME: Volumes are a mechanism to persist data generated by the container on the Host OS, and share directories from the Host OS with the container.
The
VOLUME
instruction creates a mount point on the container with the specified path. When you run the container, you can specify the directory on the Hot OS to which the given mount point will be mapped to. After that, anything that the container writes to the mounted path is written to the mapped directory on the Host OS.One of the most common use cases of volumes is to store the log files generated by the container on the Host OS. For example, Let’s say that your application writes log files to a location
/var/log/app.log
.You can mount a
VOLUME
with path/var/log
in the Dockerfile, and then specify the directory on the Host OS to which this mount point will be mapped to while running the container. After that, you’ll be able to access the logs from the mapped directory on the Host OS.In the above
Dockerfile
, we created a mount point with path/tmp
because this is where the spring boot application creates working directories for Tomcat by default. Although it’s not required for this spring boot application because who cares about tomcat directories. But if you want to store stuff like tomcat access logs, then VOLUMES are very useful.You can learn more about Volumes from the official documentation.
EXPOSE: As the name suggests, this instruction allows you to expose a certain port to the outside world.
ARG: The
ARG
instruction defines a variable with a default value. You can override the default value of the variable by passing it at build time.ARG <name>[=<default value>]
Once defined, the variable can be used by the instructions following it.
ADD: The
ADD
instruction is used to copy new files and directories to the docker image.ENTRYPOINT: This is where you configure how the application is executed inside the container.
3. Building the Docker image
Now that we have defined the Dockerfile
, let’s build a docker image for our application.
Before building the docker image, you need to make sure that you’ve packaged the application in the form of a jar file using maven. You can type the following command from the root directory of the project to package it -
$ mvn clean package
The above command creates a jar file in the target
directory of the project.
Let’s now build the docker image by typing the following command -
$ docker build -t spring-boot-websocket-chat-demo .
That’s it. You can now see the list of all the docker images on your system using the following command -
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
spring-boot-websocket-chat-demo latest 30ad8958ac67 22 hours ago 126MB
openjdk 8-jdk-alpine 224765a6bdbe 3 months ago 102MB
This should display our newly built docker image.
4. Running the docker image
Once you have a docker image, you can run it using docker run
command like so -
$ docker run -p 5000:8080 spring-boot-websocket-chat-demo
In the run
command, we have specified that the port 8080
on the container should be mapped to the port 5000
on the Host OS.
Once the application is started, you should be able to access it at http://localhost:5000
.
The container runs in the foreground, and pressing CTRL + C
will stop it. Let’s now see how to run the container in the background.
Running the docker image in the background, in detached mode.
You can use the -d
option in docker run
command to run the container in the background -
$ docker run -d -p 5000:8080 spring-boot-websocket-chat-demo
1c3528715862a8a8efb712c85bc8ab61f3419c04eb6dc613af76c89846d316e0
The above command starts the container in the background and gives you the container ID. You can see the list of all containers running in your system using the following command -
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1c3528715862 spring-boot-websocket-chat-demo "java -Djava.securit…" About a minute ago Up About a minute 8080/tcp, 0.0.0.0:4000->80/tcp vigorous_stallman
5. Pushing the docker image to docker hub
Now let’s push the docker image to docker hub so that other people can download and consume our image.
Login with your Docker Id
$ docker login Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username (callicoder): callicoder Password: Login Succeeded
Tag the image
To push a local image to docker registry, you need to associate the local image with a repository on the docker registry. The notation for the repository on docker registry is `username/repository:tag`. To tag an image, we use the `docker tag` command - ```bash $ docker tag image username/repository:tag ``` For example, Here is how we can tag the local image of our spring boot application - ```bash $ docker tag spring-boot-websocket-chat-demo callicoder/spring-boot-websocket-chat-demo:0.0.1-SNAPSHOT ``` Make sure to replace my username `callicoder` with your docker id in the above command. Once the tagging is done, you can type `docker image ls` in the terminal to see the newly tagged image - ```bash $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE
callicoder/spring-boot-websocket-chat-demo 0.0.1-SNAPSHOT 30ad8958ac67 23 hours ago 126MB spring-boot-websocket-chat-demo latest 30ad8958ac67 23 hours ago 126MB openjdk 8-jdk-alpine 224765a6bdbe 3 months ago 102MB
```
Push the image to docker hub
Finally, use the
docker push
command to push the tagged image to docker hub like so -$ docker push callicoder/spring-boot-websocket-chat-demo:0.0.1-SNAPSHOT
Again, don’t forget to replace the username
callicoder
with your docker id.And That’s all! The image is now published on the docker hub at the following link - https://hub.docker.com/r/callicoder/spring-boot-websocket-chat-demo/
6. Pulling the image from docker hub and running it
After we publish the image to docker hub, anyone can pull this image and run it in their environment. Type the following command to pull and run the image on your machine that we just pushed to docker hub -
$ docker run -p 5000:8080 callicoder/spring-boot-websocket-chat-demo:0.0.1-SNAPSHOT
The docker run
command pulls the image from docker hub if it is not available locally, and then runs it.
You see how easy it is to share your image with others. People don’t need to install anything whatsoever to run your application. They just need to pull the image and run it with docker.
Automating the Docker image creation and publishing using dockerfile-maven-plugin
You can automate everything from building the docker image to publishing it on docker hub using dockerfile-maven-plugin
.
Add the plugin to the pom.xml
file with the following configurations -
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<!-- replace `callicoder` with your docker id-->
<repository>callicoder/spring-boot-websocket-chat-demo</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
<executions>
<execution>
<id>default</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
The plugin’s configuration includes the remote repository name and the repository tag. Please replace the username callicoder
with your docker Id in the <repository>
element.
We’re also passing the JAR_FILE
argument inside <buildArgs>
element. Remember we had added ARG JAR_FILE
instruction to the Dockerfile? The argument passed here will override that value.
Here is how you can build the docker image using docker-file-maven
plugin -
$ mvn package dockerfile:build
The above command first packages the application in the form of a jar file, and then builds the docker image.
Finally, you can push the docker image to the docker registry using dockerfile:push
command -
$ mvn dockerfile:push
Now to automate it further, we have registered the dockerfile:build
and dockerfile:push
goals to the install
phase of maven build life cycle using the <executions>
tag.
So whenever you run mvn install
, the build
and push
goals of dockerfile-maven-plugin
are executed, and your docker image is built and pushed to docker hub.
The dockerfile-maven-plugin
uses the authentication information stored in any configuration files ~/.dockercfg
or ~/.docker/config.json
to push the docker image to your docker profile. These configuration files are created when you login to docker via docker login
.
You can also specify authentication details in maven settings.xml
or pom.xml
files. Check out the official Readme of the plugin for more information on that.
Conclusion
The application that we dockerized in this article is very simple. It doesn’t use any database or communicate with other services. In the next article, you’ll learn how to dockerize a complex service with a database using docker compose.
As always, Thanks for reading. Have a good day.