DEV Community

Alif Ruliarso
Alif Ruliarso

Posted on

Spring Boot Docker Images using JRE base

This is my attempt at creating a smaller Docker image for my Spring Boot application.

Development images

As a starting point, I use the Maven docker image based on Alpine Linux.

FROM maven:3.9.5-eclipse-temurin-21-alpine
RUN mkdir /app
WORKDIR /app

COPY pom.xml ./
RUN mvn dependency:go-offline

COPY docker-entrypoint-dev.sh ./
COPY src ./src
Enter fullscreen mode Exit fullscreen mode

docker-entrypoint-dev.sh

#!/bin/bash
export TERM=xterm
echo "wait 5s"
sleep 5

mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" &

while true; do
    watch -d -t -g "ls -lR . | sha1sum" && mvn compile
done
Enter fullscreen mode Exit fullscreen mode

The above script executes spring-boot:run, then watch for source code changes, and compiles the Java code.

  • The watch command executes a program periodically, showing output fullscreen
  • The -d (--difference) option to view the changing output. This option will highlight the changes.
  • The -t (--no-title) option is used to turn off the header showing the interval, command, and the current time
  • By default, the watch command keeps running until it is interrupted manually by the user (Ctrl+C). However, sometimes instead of highlighting the changes, you’d prefer the watch command to exit when a change is detected completely. We can set watch to exit when the output from the command changes by using the -g (--chgexit) option.
  • ls -lR . | sha1sum Produces an sha1sum file source code

Final Image

After building my development image, I want to make the final image for deploying into the server.
To make the image smaller, I use the layering feature and multi-stage build.

Dockerfile

FROM maven:3.9.5-eclipse-temurin-21-alpine AS java-build

WORKDIR /app/

COPY .mvn/ .mvn
COPY pom.xml ./
RUN mvn dependency:go-offline

COPY src src
RUN mvn package
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)

FROM eclipse-temurin:21-jre-alpine

COPY --from=java-build /etc/passwd /etc/shadow /etc/
ARG DEPENDENCY=/app/target/dependency
# dependencies
COPY --from=java-build ${DEPENDENCY}/BOOT-INF/lib /app/lib
# metadata
COPY --from=java-build ${DEPENDENCY}/META-INF /app/META-INF
# java classes
COPY --from=java-build ${DEPENDENCY}/BOOT-INF/classes /app

ENV _JAVA_OPTIONS "-XX:MaxRAMPercentage=90 -Djava.security.egd=file:/dev/./urandom -Djava.awt.headless=true -Dfile.encoding=UTF-8"
ENTRYPOINT ["java","-cp","app:app/lib/*","com.galapea.techblog.springboot.onlinesurvey.SpringbootOnlineSurveyApplication"]
Enter fullscreen mode Exit fullscreen mode
  • First stage: extract the content of our fat Jar.
  • Second stage: copy application layers into the final docker image which only contains jre
  • Finally, run Java with a classpath definition, instead of a link to a JAR.

After making small code changes, I rebuilt the docker image, the previous layer didn't change java-build 2 to 5/8, taking advantage of the Docker cache (=> CACHED).

[+] Building 34.1s (19/19) FINISHED                                                                                                                                                              docker:default
 => [survey-app internal] load build definition from Dockerfile                                                                                                                                            0.1s
 => => transferring dockerfile: 839B                                                                                                                                                                       0.0s
 => [survey-app internal] load .dockerignore                                                                                                                                                               0.0s
 => => transferring context: 177B                                                                                                                                                                          0.0s
 => [survey-app internal] load metadata for docker.io/library/maven:3.9.5-eclipse-temurin-21-alpine                                                                                                        1.1s
 => [survey-app internal] load metadata for docker.io/library/eclipse-temurin:21-jre-alpine                                                                                                                1.1s
 => [survey-app java-build 1/8] FROM docker.io/library/maven:3.9.5-eclipse-temurin-21-alpine@sha256:c0f2d9ba99a1a3a0c95bdd7b1888fff4183e06fe8c5c3c8cb0c19dc7d1ab893b                                       0.0s
 => [survey-app stage-1 1/5] FROM docker.io/library/eclipse-temurin:21-jre-alpine@sha256:2a4755c16fe3390e6a89daed9adfc6d9dc7be116dfce84497cf84f761b973311                                                  0.0s
 => [survey-app internal] load build context                                                                                                                                                               0.0s
 => => transferring context: 8.94kB                                                                                                                                                                        0.0s
 => CACHED [survey-app java-build 2/8] WORKDIR /app/                                                                                                                                                       0.0s
 => CACHED [survey-app java-build 3/8] COPY .mvn/ .mvn                                                                                                                                                     0.0s
 => CACHED [survey-app java-build 4/8] COPY pom.xml ./                                                                                                                                                     0.0s
 => CACHED [survey-app java-build 5/8] RUN mvn dependency:go-offline                                                                                                                                       0.0s
 => [survey-app java-build 6/8] COPY src src                                                                                                                                                               0.1s
 => [survey-app java-build 7/8] RUN mvn package                                                                                                                                                           30.5s
 => [survey-app java-build 8/8] RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)                                                                                                 1.6s
 => CACHED [survey-app stage-1 2/5] COPY --from=java-build /etc/passwd /etc/shadow /etc/                                                                                                                   0.0s
 => CACHED [survey-app stage-1 3/5] COPY --from=java-build /app/target/dependency/BOOT-INF/lib /app/lib                                                                                                    0.0s
 => CACHED [survey-app stage-1 4/5] COPY --from=java-build /app/target/dependency/META-INF /app/META-INF                                                                                                   0.0s
 => [survey-app stage-1 5/5] COPY --from=java-build /app/target/dependency/BOOT-INF/classes /app                                                                                                           0.1s
 => [survey-app] exporting to image                                                                                                                                                                        0.1s
Enter fullscreen mode Exit fullscreen mode

And the final docker image.

Image description

In the next iteration, I will try to use Jlink.

Top comments (0)