Personal blog of Francesco Guardiani, Software Engineer
0%
Vert.x container images with jlink
Posted onEdited on
In this blog post I'm gonna show you how I managed to reduce the container image size of an Eclipse Vert.x application, creating a smaller JDK with jdeps and jlink.
In this blog post I’m gonna show you how I managed to reduce the container image size of an Eclipse Vert.x application, creating a smaller JDK with jdeps and jlink.
The application is the data plane of Knative Eventing Kafka Broker, an implementation of Knative Broker tailored on Kafka.
Fat-jar
We didn’t handwritten nor generated a module-info.java for our application, we just ship a fat-jar. To generate a fat-jar, configure the maven-shade-plugin:
jdeps is the tool you must use to figure which JDK modules you depend on. If you run jdeps just with the fat-jar as argument, you’ll get a list of all the packages in your jar and the JDK modules it depends on:
This is the list of jdk modules we depend on. Doing a quick check, I’ve found that:
java.base it’s the module that contains all the core features of the jdk
java.compiler contains the compiler types. It’s brought in by Guava and Vert.x CLI feature.
java.naming contains some JNDI types, to perform names lookups. This is required to perform DNS queries by Vert.x
java.security.jgss and java.security.sasl contains some security protocols implementation. They’re used by the Java Kafka client.
java.sql contains JDBC. Jackson databind and Google Gson (that we import transitively via Protobuf json) depends on it because they provide marshallers/unmarshallers for JDBC types.
jdk.management contains some interfaces to manage the JDK. This is used by Micrometer to instrument the JVM and collect metrics.
jdk.unsupported contains sun.misc.Unsafe. Netty and Protobuf use it to perform off-heap allocations.
Zero days since it was DNS
Because some reflection is happening behind the hood to choose the Vert.x DNS resolver, jdeps doesn’t discover the module jdk.naming.dns, that you need in your Vert.x application to enable the DNS.
To resolve the deps and add the dns module:
script
1 2 3 4
MODS=$(jdeps -q --print-module-deps --ignore-missing-deps receiver/target/receiver-1.0-SNAPSHOT.jar) echo "Computed mods = '$MODS'" # Patch adding the dns MODS="$MODS,jdk.naming.dns"
Create the JDK
Now you just need to invoke jlink to generate your custom JDK:
Providers: java.base provides java.nio.file.spi.FileSystemProvider used by java.base java.naming provides java.security.Provider used by java.base java.security.jgss provides java.security.Provider used by java.base java.security.sasl provides java.security.Provider used by java.base jdk.naming.dns provides javax.naming.spi.InitialContextFactory used by java.naming java.management provides javax.security.auth.spi.LoginModule used by java.base java.logging provides jdk.internal.logger.DefaultLoggerFinder used by java.base jdk.management provides sun.management.spi.PlatformMBeanProvider used by java.management
This command will grab the modules you provided from your local machine and will create a JDK without manual, header files, debug symbols:
Using jdeps and jlink our container image size is 2.5x smaller, which is a great achievement for us. We also plan to reduce it even further, in fact we want to:
Investigate some modules we depend on, but that we probably don’t need: java.compiler and java.sql are two good candidates.
Rebase our base image on alpine, which uses musl as libc. Look at Project Portola for more details.