I am sure you have already heard about Docker and also know what this is and used for. However, I will do a quick recap on Docker before going to the topic of this article to refresh your memory.

Docker is a platform for developing, deploying and running applications in containers. Containers are lightweight, standalone, and executable software packages that include everything needed to execute a piece of software, including the executable code, runtime, dependent libraries, and system tools. Containers provide a consistent and isolated environment, ensuring that an application runs the same way across different environments, such as development, testing, and production which may run on different hardware and operating system.

Docker is widely used in software development and deployment because it simplifies the process of building, packaging, and distributing applications. It promotes a “write once, run anywhere” philosophy, making it easier to manage and scale applications across different environments. Additionally, Docker facilitates the use of microservices architecture, where applications are composed of small, independent, and loosely coupled services running in containers.

In this blog post we will discuss about few commonly used scenarios or recipes using Docker commands in the form of shell scripts. These scripts can be executed to achieve the goals set out for each recipe. We have used Java web application using Spring Boot framework and My SQL database for the examples. Let’s begin.

Create Docker Image for Java Spring Boot Web App

Purpose of this recipe is to create docker file for a Spring Boot web application from a WAR file already build using build tools like Maven or Gradle in local folder. Comments written above each shell command is self explanatory. However, I will explain in more detail. The contents specified below needs to be saved in a file named ‘Dockerfile’ without any extension. Docker file contains the instruction/steps needed to build the docker image.

Generally, docker images are build on top of a base image. Selection of the base image depends on the runtime environment that the application need. In this case we need Java run time environment to run the application. Therefore, the base image used is official OpenJDK.

You can think of each container as a self contained environment with it’s own environment variables, directory structure, port etc. Here we have specified ‘/app’ as the working directory or root directory.

Next we are copying the WAR file of our application from local machine to ‘/app’ folder within the container. Docker allows us to set a port which can be used to connect to the application running within the container. Here we have used port 5000.

Finally, we have specified the command used to run the web application within the container. When the docker image is run as docker container instance, this command will start the web application. As this is a spring boot application with embedded Tomcat server, we can just run this as simple java application without need for separate application server.

# Use the official OpenJDK 18 as the base image
FROM openjdk:18.0-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the compiled Spring Boot application WAR file into the container
COPY target/expensemanager-0.0.1-SNAPSHOT.war app.war

# Expose the port that your Spring Boot application will run on
EXPOSE 5000

# Define the command to run your Spring Boot application
CMD ["java", "-jar", "app.war"]

Create Docker Image for Java Spring Boot Web App with Clean Build within the Docker Container

This recipe is similar to the previous one with the difference that the application is built within the docker container. To build the WAR or JAR file within docker, we would need Maven available as part of container runtime environment. Therefore, we have selected a different base image here which has both Java and Maven available.

Next step is to move the source files of the project from local drive to the container folder. You would notice that the files are divided in groups and COPY command has been used multiple times. This is done to create multiple layers within the docker image. The advantage of having multiple layers is that, when there is any code change the image is rebuilt only the layers with changed files will be rebuilt.

We don’t want Maven to download all dependencies again during each build unless there is any change to pom.xml which will not be frequent. So, if there is no change in pom.xml then that layer will be skipped and only the actual source codes will be built again. This will significantly reduce subsequent build times.

Rest of the steps are similar to the previous script.

FROM maven:3.8.6-openjdk-18-slim AS build 
WORKDIR /home/app 
COPY ./pom.xml /home/app/pom.xml 
COPY ./src/main/java/com/programmingtochange/expensemanager/ExpensemanagerApplication.java / /home/app/src/main/java/com/programmingtochange/expensemanager/ExpensemanagerApplication.java 
RUN mvn -f /home/app/pom.xml clean package 
COPY . /home/app 
RUN mvn -f /home/app/pom.xml clean package 
FROM openjdk:18.0-slim 
EXPOSE 5000 
COPY --from=build /home/app/target/*.jar app.war 
ENTRYPOINT [ "sh", "-c", "java -jar /app.war" ]

Create a Docker Image from Dockerfile

Now that we have created the docker file for our application, next step is to build the docker image by using the Dockerfile. The command is specified below. The Dockerfile must be placed in the same folder where you are running the command. Local file system paths specified in the Dockerfile should also be with respect to this directory where Dockerfile is located.

You may think of docker image as the blueprint or master copy of your target runtime that can be used to create as many docker containers as you want which are replicas of the image.

docker build -t programmingtochange/expensemanagerweb:v0.0.1 .

Run a Docker image of Java Application in a container

Now that we have created the docker image for our application, this is time to actually start a container which is actually the running application with all dependencies ready to be used. Docker run command creates the container and also starts the container. From next time onwards you can just stop and start the container already created.

Unless there is any code change for your application and you want to deploy a new version of your application there is no need to build the image. If you need another instance of the app you can just spin off another instance on a different port using docker run. In this command:

  • expensemanager-web-app – is the name of the container
  • 5000 – external port number assigned to the container. When anyone want to access the app from outside the container, this port should be used to access the app.
  • 8080 – internal port number in which the web app is listening within the container. This port number is configured in Sprint application properties file as server port.
  • programmingtochange/expensemanagerweb:v0.0.1 – this is the docker image used to create the container.
docker run --name expensemanager-web-app -d -p 5000:8080 programmingtochange/expensemanagerweb:v0.0.1

Stop/Start a Docker container

As mentioned in the previous section, most of the time we would just need to start and stop the container and we will use the following commands.

#Stop the web-app container
docker stop expensemanager-web-app

#Start the web-app container
docker start expensemanager-web-app

Run a MySQL Docker Image to Create a Container running a Database

In previous steps, we have successfully run a Spring Boot web application in Docker container. But, every large scale application uses some kind of database to persist business data. Therefore, we would also need a database server running in a docker container which our web application can connect to.

In the example below I have used MySQL database. However, docker image is available for all popular databases. The command used to run docker image for MySQL database is similar to running image of spring boot application with some additional parameters described below which are very important:

  • -e MYSQL_ROOT_PASSWORD=db_password – this is the password of the MySQL database instance.
  • -v mysql-expensemanager-data-volume:/var/lib/mysql – this is the data volume attached to the container. We want the data persisted in the database to live between container restarts and even removal and rerun. Attaching a data volume is like attaching a small hard drive to the container so that the data is persisted outside the container.
docker run --name expensemanager-mysql-container -e MYSQL_ROOT_PASSWORD=db_password -d -p 3307:3306 -v mysql-expensemanager-data-volume:/var/lib/mysql mysql:latest

Run a MySQL Docker Image with Custom Config File

We can go one step further and use a custom configuration file for our MySQL database running in the container. We only need to add the following additional parameter to achieve that:

-v /Users/aranichatterjee/Documents/Developement/Database/MySQL/Config:/etc/mysql/conf.d

docker run --name expensemanager-mysql-container -e MYSQL_ROOT_PASSWORD=db_password -d -v /Users/aranichatterjee/Documents/Developement/Database/MySQL/Config:/etc/mysql/conf.d -p 3307:3306 -v mysql-expensemanager-data-volume:/var/lib/mysql mysql:latest

That’s all for this article. Hopefully, you have understood how docker can be easily used to run a Java web application with MySQL database in container. The beauty of containerising your application is that you can simply deploy the image in any other machine or even in cloud like AWS and the application is expected to run without any additional configuration in the target server. See you in the next article. Happy Dockering!

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.