Create Smaller Docker Images For Spring Boot Using JLink
Jlink

Create Smaller Docker Images For Spring Boot Using JLink

Containers revolutionise software development and deployment by providing unparalleled flexibility and agility, fundamentally reshaping the landscape of application development and scalability. However, this transformative technology brings with it a new vulnerability landscape that malicious actors can exploit. A compromised container not only jeopardizes its integrity but also opens pathways for attackers to infiltrate other containers and potentially compromise the host system.

Mitigating this risk requires adopting practices such as utilizing smaller images containing only essential artifacts, which effectively reduces the attack surface. Additionally, employing compact application containers minimises resource consumption, enhancing security while optimising runtime performance, by providing shorter startup duration and smaller size for jar files.

In this article we will study in-depth utilisation of JLink to optimise docker image size of any spring boot or java application, to enhance application security and usage of only required modules and dependencies.

Project Jigsaw

Project jigsaw was introduced in JDK 9 with primary goals of

  • Make it easier for developers to construct and maintain libraries and large applications.
  • Improve security and maintainability of Java SE platform and JDK.
  • Improve application performance
  • Enable java SE platform and JDK to scale down for use in small applications.

Introduction of JLink

Java is one of the most used programming language specially for enterprise applications, but one biggest challenge developer faced are size of docker images while deploying java application in container. One of the way to solve this problem is to use JLink.

JLink was introduced as the part of project Jigsaw. JLink (Java linker) is a command line tool that assemble and optimise a set of modules and their dependencies into a custom runtime image. It means that JLink create a minimum java runtime with only required modules and their dependencies for project.

$ jlink --module-path $JAVA_HOME/jmods:testLib --add-modules test.module --output testRunTime         

In above example, test.module are module we want to add and testRunTime is runtime image which jLink will create.

Small Docker Images Using JLink

When we write docker images for java applications, size of image is concerning, specially spring boot applications which come with plenty of dependencies and modules. A large docker image can lead latency during start up, increased storage costs and slower development process.

In older version of java (priori to JDK 11) java development kit comes with a java runtime environment (JRE). Only JRE was needed to run java artefacts (jars). When we used to create docker file then we only need JRE base image to run our java jars in our containers. Onwards JDK 11, JRE is no longer offered but only JDK (embedded with all runtime required to run java artefacts including some additional tools like compiler, executables and binaries). Now when we create docker images we required to use JDK which cause larger runtimes.

JLink enable us to create a minimal java runtime with only necessary modules and its dependencies. By doing this, it significantly reduces the size of our docker image, for example a standard java runtime environment might be 150 MB - 200 MB while using JLink reduce it to 20 MB to 50 MB.

JLink for Spring Boot

Spring boot create a fat jar for our applications containing all dependencies required by spring boot. We wanted to use only those dependencies and modules which are needed by our application. Imagine our application is a very simple CRUD application so our application only needed modules required for creating end points and CRUD functionalities.

We need to determine which modules the application need along with its dependencies.

Jdeps Usage

Jdeps a Java 8 tool that helps us to determine all package and class level dependencies and modules of an application. First we will figure out all dependencies and modules using Jdeps and will pass those to our JLink to create concise and smaller runtime for our application by using only required dependencies.

we can use jdeps to print a summary of the dependencies.

jdeps -cp 'mydeps/lib/*' -recursive --multi-release 17 -s target/Websocket.jar         

Similarly, we can use jdeps to print all module dependencies recursively for the Spring Boot application and the dependencies.

jdeps --ignore-missing-deps -q  --recursive  --multi-release 17  --print-module-deps  --class-path 'mydeps/lib/*'  target/Websocket.jar         

The output generated by jdeps enables JLink to create a Java Runtime that only contains the modules needed for this Spring-Boot application.

We store information of dependencies and modules in a file before passing this to JLink.

RUN jdeps --ignore-missing-deps -q \ --recursive \ --multi-release 17 \ --print-module-deps \ --class-path 'BOOT-INF/lib/*' \ target/Websocket.jar > deps.info        

Building Docker Image

Let’s combine Jdeps and JLink to build a custom Java Runtime. With this runtime, we can create a perfect, minimal Docker image specifically for a Spring Boot application.

FROM maven:3-eclipse-temurin-17 as build
RUN mkdir /usr/src/project
COPY . /usr/src/project
WORKDIR /usr/src/project
RUN mvn package -DskipTests
RUN jar xf target/Websocket.jar
RUN jdeps --ignore-missing-deps -q  \
    --recursive  \
    --multi-release 17  \
    --print-module-deps  \
    --class-path 'BOOT-INF/lib/*'  \
    target/Websocket.jar > deps.info
RUN jlink \
    --add-modules $(cat deps.info) \
    --strip-debug \
    --compress 2 \
    --no-header-files \
    --no-man-pages \
    --output /myjre
FROM debian:bookworm-slim
ENV JAVA_HOME /user/java/jdk17
ENV PATH $JAVA_HOME/bin:$PATH
COPY --from=build /myjre $JAVA_HOME
RUN mkdir /project
COPY --from=build /usr/src/project/target/Websocket.jar /project/
WORKDIR /project
ENTRYPOINT java -jar Websocket.jar        

In above example, we created a multistage docker build. The initial building stage based on an eclipse-temurin JDK 17 image containing Maven. This stage is used to:

  1. Create a java artefact using maven by creating executable JAR file.
  2. Use Jdeps to get all necessary dependencies and modules and store list to a file deps.info
  3. Run Jlink to create a custom runtime using deps.info and store it in /myjre.
  4. Set the JAVA_HOME to the path and copy myjre to, and add JAVA_HOME to the PATH.
  5. Copy the Java Runtime created by JLink. Reference the first stage and copy the custom Java Runtime to the location defined as JAVA_HOME.
  6. Making directory project and copy jar file to project directory.
  7. Set Entry-point

Conclusion

As in this article we study how we can utilise JLink from project jigsaw to create small, light weight docker image by provide custom runtime environment having only those modules and dependencies required by application. To determine our application dependencies and modules we use Jdeps from JDK 8. Smaller docker images lead faster development and reduced storage cost. Beside that it also provide faster startup and reduce the attack surface of application by providing only necessary modules.

ZARA MASOOD

Artificial Intelligence | Python |Machine Learning | Deep Learning | Exlporatory Data Analysis

7 个月

Excellent ??

回复

要查看或添加评论,请登录

Aqib Javed的更多文章

  • Air Traffic Coordination Algorithm

    Air Traffic Coordination Algorithm

    The closest pair of points problem is a computational geometry problem. Given n points in a metric (2D space), find a…

  • Quagmire of N Queens Problem

    Quagmire of N Queens Problem

    The N-Queens problem is a prototypical backtracking problem that involves placing N queens on an N × N chessboard in…

  • Graphs in Java

    Graphs in Java

    The prerequisite of this article is an Introduction To Graph. Please go through that article first before proceeding…

    4 条评论
  • Introduction To Graphs

    Introduction To Graphs

    Graphs are one of the fundamental data structures in computer science. They play a vital role in solving complex…

    2 条评论
  • Vector vs ArrayList

    Vector vs ArrayList

    Introduction Arrays are one of the fundamental components of data structures. They play a vital role in solving…

    2 条评论
  • Finding Median Of Two Sorted Arrays

    Finding Median Of Two Sorted Arrays

    As part of our series on algorithms and data structures, today we are going to solve a very interesting problem that…

  • Hashtable vs HashMap

    Hashtable vs HashMap

    Introduction One of the most frequently asked interview questions at the entry-level is about HashMaps and Hashtables:…

    1 条评论
  • Bit Operations in Java

    Bit Operations in Java

    No doubt Java is a strong language, with its great architecture, providing write once and run anywhere with object…

  • Microservices using spring cloud

    Microservices using spring cloud

    Microservice architecture, with its great advantages and flexibilities, besides all agility support also bring huge…

  • Monolithic vs. SOA vs. Microservices

    Monolithic vs. SOA vs. Microservices

    Creating a new project is always a pain and its success is always a question that how can we make it, maintain it and…