I did not plan to write a third part about my journey to small Docker images for OpenJDK. However, Java 16 will support musl-libc instead of glibc which is very interesting for our container world. So I did another experiment which I would like to share.
Java relies only on very few dependencies: zlib and libc. While zlib handles compression, libc is the foundation of a typical C program. It brings support for input and output, math, and many things more. It’s a bit like the provided classes of our Java runtime. A bit! The most common implementation of libc is gnu’s glibc. glibc is very mighty and in addition to io and math, it bring’s a ton of features that we normally don’t need. I.e. glibc brings support for several outdated 7 and 8 bit charsets. Musl-libc is a clean and minimalistic libc implementation from alpine linux. musl provides a comparison of the libc functionalities.
Java 16 comes with a JDK that is built against musl-libc (yes – it is a separate setup that must be downloaded). This is possible because Java actually doesn’t need most of the extra features from glibc. As Java developers, we see no difference. In Docker container we prefer small images. How small is glibc, how small is musl-libc?
Let’s build a musl based JRE base image and compare the sizes!
Like in the previous articles, we build the final image from scratch and copy only the dependencies we need. The following Dockerfile will look familiar:
Code-Sprache: Dockerfile (dockerfile)
FROM alpine AS jre RUN apk add binutils RUN \ mkdir -p /tmp/jdk /target/lib; \ for lib in musl zlib; do \ apk info -L $lib 2>/dev/null | tail -n +2 | head -n -1 | \ while read -r file; do \ cp "$file" "/target/$file" ; \ done \ done COPY jdk.tar.gz /tmp/jdk/jdk.tar.gz RUN tar -C /tmp/jdk --strip-components=1 \ -xzf /tmp/jdk/jdk.tar.gz \ && \ /tmp/jdk/bin/jlink \ --add-modules java.se \ --add-modules jdk.unsupported \ --output /target/jre \ --no-header-files \ --no-man-pages \ --strip-debug \ --compress=2 FROM scratch COPY --from=jre /target / USER 65534 ENTRYPOINT [ "/jre/bin/java" ]
(For this demo, I downloaded the „Alpine linux/x64“ JDK from https://jdk.java.net/16/. When Java 16 is released, there will be an official download. This also means that the final numbers may change a bit when Java 16 is released!)
The loop in the lines 5-12 only copies for files with a total size of
1.2 MiB. Isn’t this great? The final image is
53.8 MiB large,
52.6 MiB belong to Java. The glibc version of the Docker image is
68.3MiB large. By switching to a smaller libc, we saved more than
15 MiB (
I hope you enjoyed this as much as I did!