How to Dockerize Java Application
In this guide, we will provide a step-by-step tutorial on how to Dockerize a Java Application. We will also cover best practices for using the Docker image in a production environment.
Prerequisites
To build a Java Docker image, you need to have the following prerequisites:
- Docker installed on your system.
- Java application JAR file that you want to Dockerize.
Build the Java JAR
If you already have your own Java application or JAR file, you can proceed to the next section.
If you are new to Java or need assistance in building a JAR file from a Spring Boot application, you can refer to our blog on building Java applications.
Alternatively, you can use the Java Petclinic Spring Boot application for testing purposes.
git clone https://github.com/techiescamp/java-spring-petclinic
cd java-spring-petclinic
mvn clean install -Dmaven.test.skip=true
Creating a Docker Image for a Java Application
To create a Docker image for a Java application, you first need to create a Dockerfile.
For this example, we will be using the base image techiescamp/jre-17:1.0.0
.
Note: If you are unable to access the public Docker Hub registry, replace the base images with JRE base images provided by your organization’s private container registry.
Usually, the Dockerfile is placed in the application code folder. In this case, the application jar file would be located under the /target
folder. If your jar file is in a different location, replace /target/*.jar
with the actual path in the Dockerfile.
For example, the following tree shows that I have created the Dockerfile inside the application code folder, where I have the target folder containing the jar file.
.
├── Dockerfile
├── LICENSE.txt
├── pom.xml
├── readme.md
├── src
└──target
Now, create a Dockerfile in your application folder with the following content.
FROM techiescamp/jre-17:1.0.0
WORKDIR /app
# Copy the JAR file (/app)
COPY /target/*.jar ./java.jar
# Expose the port the app runs on
EXPOSE 8080
# Run the jar file
CMD ["java", "-jar", "java.jar"]
Here is an explanation of the Dockerfile:
- The
./app.jar
instruction copies the JAR file(s) from the /target directory on the host machine to the/app
directory inside the container. The JAR file(s) is then renamed to app.jar. - This Dockerfile creates a Docker image using the eclipse-temurin:17-jre image as its base. It also copies the JAR file into the Docker image’s /app working directory.
- Additionally, it exposes port 8080 and uses the CMD instruction to run the JAR file.
Now, let’s build the Java application Docker image using the following command:
docker build -t java-application:1.0.0 .
Use the docker images command to list your Docker images, as shown below:
docker images
Once your Docker image build is finished, run the Docker image using the following command:
docker run -d -p 8080:8080 java-application:1.0.0
This command will run your Java application in a container. Make sure to change the port if you are using a different port.
Use the docker ps
command to list the running containers, as shown below:
Now, check if your application is accessible in the browser using localhost:8080
. I exposed my application on port 8080, so make sure to use the correct port number that you exposed.
Using Distroless Java Base Image
Here is an example Dockerfile that uses the Distroless Java base image from Google.
# Use the distroless Java base image
FROM gcr.io/distroless/java17-debian12
# Set the working directory in the container
WORKDIR /app
# Copy the JAR file into the container at /app
COPY target/*.jar java.jar
# Expose the port your app runs on
EXPOSE 8080
# Run the jar file
CMD ["java.jar"]
For the distroless Java image, the Entrypoint is set to java -jar
by default. Therefore, we can directly use java.jar
with the CMD as shown above.
Using Docker Multistage Build for a Java Application
With multistage build, you can build the Java application and dockerize it using a single Dockerfile.
Create a Dockerfile with the following content:
FROM techiescamp/jdk-17:1.0.0 AS build
# Copy the Java Application source code
COPY . /usr/src/
# Build Java Application
RUN mvn -f /usr/src/pom.xml clean install -DskipTests
FROM techiescamp/jre-17:1.0.0
WORKDIR /app
# Copy the JAR file from the build stage (/app)
COPY --from=build /usr/src/target/*.jar ./java.jar
# Expose the port the app runs on
EXPOSE 8080
# Run the jar file
CMD ["java", "-jar", "java.jar"]
- The above Dockerfile consists of two stages. The first stage is responsible for building the application, while the second stage runs the application.
- The first stage uses the base image
techiescamp/jdk-17:1.0.0
. - The
techiescamp/jdk-17:1.0.0
image includes JDK-17 and Maven-3.9.4, which are required for building the Java application. - The Java application source code is copied and pasted into the
/usr/src/
directory. Make sure to specify the path of your Java application source code. - The application is then built using the
mvn clean install
command. - The second stage uses
techiescamp/jre-17:1.0.0
as its base image. It copies the JAR file from the build stage directory/usr/src/target/
to the current working directory/app
with the namejava.jar
. - Port 8080 is exposed, and the JAR file is executed using the
java -jar
command.
Now, build the image and run the container using the following command:
docker build -t java-application:2.0.0 .
Use the docker images
command to list your docker image as given below
Once your Docker image build has been finished, run the Docker image using the command below
docker run -d -p 8080:8080 java-application:2.0.0
To run your Java application in a container, use the following command. If you are exposed to a different port, make sure to change the port accordingly.
To list the running containers, use the docker ps
command.
Now, check if your application is running on the browser by accessing localhost:8080
. In this example, I have exposed my application on port 8080, so make sure to use the port number you have exposed.
Best Practices
When working with Java application Docker images for production workloads, it is important to follow the Docker image best practices outlined below.
Lint Dockerfile
To check your Dockerfile for possible errors, security vulnerabilities, and performance problems, it is recommended to use Hadolint. Install Hadolint and run the following command to lint your Dockerfile:
hadolint Dockerfile
Ensure that you run this command in the same directory where your Dockerfile is located.
Scan for Vulnerabilities
Use tools like Trivy to scan Docker images for vulnerabilities. Install Trivy on your system and use the following command to scan your Docker image:
trivy image java-application:2.0.0
Replace java-application:2.0.0
with your Java Docker image.
Optimize Java Image Size
Optimizing Docker images is crucial in project environments.
In addition to following Dockerfile best practices and using distroless minimal images, you can also utilize the SlimToolkit to decrease the size of your Java Docker image.
To begin, create a preserved-paths.txt
file that contains the names or paths of files you want to exclude from being removed during the slim run.
/app
/usr/bin/
In my txt
file, I have specified the path to my application and the path where Java is installed. Now, run the following command to reduce the size of the Docker image using the txt
file.
slim build --http-probe=false --preserve-path-file preserved-paths.txt java-application:2.0.0
This command will reduce the size of Docker images without affecting the file or file path mentioned in the txt
file. If you check the size of the Docker image after executing this command, you will notice a significant reduction.
Versioning Docker Image
When tagging Docker images, it is recommended to use Semantic Versioning to ensure clarity and consistency in versioning practices. An example of a semantic version is 2.0.0
.
java-application:2.0.0
You can also see that I used 1.0.0
for the first example image and 2.0.0
for the second example image.
Conclusion
I have covered most of the information and provided practical examples for Dockerizing a Java application.
In project environments, make sure to lint and run vulnerability scans before pushing to the registry. Usually, these tasks are taken care of by automated CI/CD pipelines.
Additionally, the choice of base image depends on the project requirements. It is recommended to use minimal base images whenever possible.
Reference
https://devopscube.com/dockerize-java-application/