Docker allows us to build thinner images by dividing the image-building process into multiple stages, or in nutshell using MultiStage-Builds.This feature allows us to reuse artifacts produced in one stage by another stage.
The main advantage of the multi-stage build is that it can help to reduce the image size.
Here we're deploying a Spring boot application and as with any java application, the build phase typically involves building the app and packaging the generated artifact into an image. We're using maven as the build tool, which means it will download all the required dependencies and keep them in the image. The number of JARs in the repository could be significant depending upon the number of dependencies in the pom.xml, causing unnecessary bloat in the image size.
Using Multi-Stage Build Approach:
In the multi-stage build, The Dockerfile can contain multiple FROM statements and each stage begins with a new FROM statement and a fresh context. We can copy artifacts from stage to stage and the artifacts not copied over are discarded. This allows to keep the final image as small as possible and only include the needed artifacts.
The Dockerfile for my application looks like this:
FROM maven:3.6.3 AS build
COPY ./appCode /opt/application/backend/
RUN mvn -f /opt/application/backend/pom.xml clean install -Dmaven.test.skip=true
FROM amazoncorretto:11
COPY --from=build /opt/application/backend/target/app-0.0.1-SNAPSHOT.jar /opt/app/app-0.0.1-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/opt/app/app-0.0.1-SNAPSHOT.jar"]
Notice that there are two FROM instructions meaning this is a two-stage build.
The maven:3.6.3 stage is the base image for the first build, It is named build. This is used to build the jar file for the application.
As for the amazoncorretto:11, it's the second and the final base image for the build. The JAR file generated in the first stage is copied over to this stage using COPY --from syntax.
This has the great benefit of decreasing the overall size of the image, by allowing us to accordingly choose the base image for the final image to meet the needs. Additionally, the un-needed dependencies from build time are discarded during intermediate stages.
Final Thoughts:
There are certainly many other ways to improve the build cycle, but if we are using Dockerfile to build the artifact, then we should seriously consider multi-stage builds.