In this tutorial, we are going to learn how to build and run our Spring Boot application using Docker. First, we will pre-build our application to generate the fat jar file, and Docker will later utilize it for deployment. Later, we will deep dive into a multi-stage build.
Before we move forward, you can follow some of our other tutorials.
Adding Spring profile for Docker
We are deploying the application with Docker, so first, we will configure the Docker profile for different configurations.
Inside
application.yml filejava
server.port: 8001
logging:
level:
root: INFO
com.csbyte: DEBUG
---
spring.config.activate.on-profile: docker
server.port: 8080Here, we are separating the local environment to docker environment by dashes(---). And set the profile to
docker. This is a simple configuration that only has a different port for the
docker profile.Build The Changes
Move to the project directory and build the project
bash
./gradlew buildIt will create the fat jar file inside
build/libs Build the Docker image
Create Docker File
Let's create a Dockerfile inside the root directory in your project.
Note: the Docker file must not contain any extension.
bash
FROM eclipse-temurin:17-jdk-alpine
EXPOSE 8080
ADD ./build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]Here, we are using JDK 17 if you want the latest version of Java used accordingly. The setup for the JDK 25 will be as follows.
bash
FROM eclipse-temurin:25-jre-alpine
EXPOSE 8080
ADD ./build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]- The Docker image for JDK 17 is used
- Port 8080 is exposed to the other Docker container or the internet.
- The fat Jar file will be added from the directory
build/libsthat we built previously. ENTRYPOINTcommand is used by Docker to start the container based on the Docker image i.ejava -jar /app.jar
Here are some disadvantages of using these methods.
- When the Docker container starts up, the fat JAR will take time to unpack.
- Since the fat JAR is big, if we want to deploy repeated changes to the code, running the Docker deployment layer is not optimal.
This leads to the multi-stage build.
Multi-Stage Build
Let's create the multi-stage build Docker file.
bash
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR extracted
ADD ./build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:17-jdk-alpine
WORKDIR app
COPY --from=builder extracted/dependencies/ ./
COPY --from=builder extracted/spring-boot-loader/ ./
COPY --from=builder extracted/snapshot-dependencies/ ./
COPY --from=builder extracted/application/ ./
EXPOSE 8080
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]This makes it possible to extract the fat JAR content into a number of folders.
FROM eclipse-temurin:17-jdk-alpine AS builderit will pull the JDK image from Temurin and named asbuilder.- We have created the working directory as
extractedand added the fat JAR file to this directory. RUN java -Djarmode=layertools -jar app.jar extractthe build stage, run this command to perform the extraction of the fat JAR file into its working directory.FROM eclipse-temurin:17-jdk-alpineit will use the same Docker image and copy the files from the builder stage folders.--from=builder extracted/dependencies/third-party libraries like Hibernate, which change less frequently. So they are copied first. Docker cache this layer--from=builder extracted/spring-boot-loader/handling launching the application. This contains the classes that are responsible for launching the application.--from=builder extracted/snapshot-dependencies/contains snapshot dependencies, if any.--from=builder extracted/application/our Spring Boot classes like controller, services, configuration classes. Changes in the app code will invalidate and build other heavy-lifting dependencies that are not required to build each time.loader.launch.JarLauncherInstead ofjava -jar app.jar(which forces Java to uncompress files at startup), it directly invokes Spring Boot's JarLauncher. Resulting in faster container startup times and slightly lower memory consumption
Build Image
bash
docker build -t product-service .Make sure to use your own app instead of
product-service .The output looks something like this.
bash
[+] Building 0.6s (14/14) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 522B 0.0s
=> [internal] load metadata for docker.io/library/eclipse-temurin:17-jdk-alpine 0.4s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B Run the Docker image
bash
docker run -d --name product-service -p8080:8080 -e SPRING_PROFILES_ACTIVE=docker product-serviceThis will run the application. You can test the endpoints of your application.
In this tutorial, we learn how to set up the multi-stage build for a Spring Boot application. By doing so, we are able to create a high-performance, faster container startup application deployment.
