It is recommended not to use a plain JDK (or much better a JRE) at production stage. You get all the stuff you did not need and also some critical vulnerabilities. As there are often CVEs in the desktop/client parts of the JVM you can simple remove them.
This is a example how to create a custom JVM with jlink for a application build with maven. In this sample we are using spring boot, but this does not matter and works also with Quarkus or plain Java programs.
Extend Maven with a plugin to get a list or all maven java dependencies.
Use the maven-dependency-plugin
in the build section:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dependency</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
This will copy all JARs in the given folder.
With the tool jdeps
it is possible to create a list of Java modules which are needed from the
application:
jdeps --ignore-missing-deps -q --recursive --multi-release 17 --print-module-deps --class-path 'target/dependency/*' target/*.jar > target/deps.info
After getting a complete list of the used Java modules the tool jlink
can create a custom JVM
containing only the needed Java modules:
jlink --add-modules "$(cat target/deps.info)" --strip-debug --compress 2 --no-header-files --no-man-pages --output target/jvm
After this the JVM is placed in target/jvm
and can be used.
Important
The JVM is created for the system/architecture it is running for!
The script buildJvm.sh
contains all steps to create the custom JVM. It also uses the command
strip
to shrink the size of the created libraries to address a bug
in some jlink implementations which still exists.
In Dockerfile
is shown how to create a custom JVM in a multistage docker build
(this should worked in all environments) and use a very small distroless
image at runtime.