Update
2019.10.21
The original post was about Java 13. Oracle is changing JDK versions quite frequently. The newest version of JDK is 14:
Intro
Quite often I hear a complaint from developers that Java containers are too big and how much smaller this would be with Go or other languages. With this new project called Portola it is possible to make very small (~40MB) containers running Java applications. Alpine Linux became the de facto standard for small containers but until now it was a rather complex process to create a Java environment using it. This is not anymore the case. Let's see how we can leverage Project Portola to create these small containers.
Creating Containers
First, we just create a container that has a new small size JDK.
FROM alpine:latest as build
ADD https://download.java.net/java/early_access/alpine/15/binaries/openjdk-14-ea+15_linux-x64-musl_bin.tar.gz /opt/jdk/
RUN tar -xzvf /opt/jdk/openjdk-14-ea+15_linux-x64-musl_bin.tar.gz -C /opt/jdk/
RUN ["/opt/jdk/jdk-14/bin/jlink", "--compress=2", \
"--module-path", "/opt/jdk/jdk-14/jmods/", \
"--add-modules", "java.base", \
"--output", "/jlinked"]
FROM alpine:latest
COPY --from=build /jlinked /opt/jdk/
CMD ["/opt/jdk/bin/java", "--version"]
The rest of the article is not concerned with JDK version. Output from docker build might reflect earlier version.
We can start to build the container:
[v@alpine-java jdk14_v]$ sudo docker build .
Sending build context to Docker daemon 2.56kB
Step 1/8 : FROM alpine:latest as build
latest: Pulling from library/alpine
bdf0201b3a05: Pull complete
Digest: sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913
Status: Downloaded newer image for alpine:latest
---> cdf98d1859c1
Step 2/8 : ADD https://download.java.net/java/early_access/alpine/16/binaries/openjdk-13-ea+16_linux-x64-musl_bin.tar.gz /opt/jdk/
Downloading [==================================================>] 195.2MB/195.2MB
---> Using cache
---> b1a444e9dde9
Step 3/7 : RUN tar -xzvf /opt/jdk/openjdk-13-ea+16_linux-x64-musl_bin.tar.gz -C /opt/jdk/
---> Using cache
---> ce2721c75ea0
Step 4/7 : RUN ["/opt/jdk/jdk-13/bin/jlink", "--compress=2", "--module-path", "/opt/jdk/jdk-13/jmods/", "--add-modules", "java.base", "--output", "/jlinked"]
---> Using cache
---> d7b2793ed509
Step 5/7 : FROM alpine:latest
---> cdf98d1859c1
Step 6/7 : COPY --from=build /jlinked /opt/jdk/
---> Using cache
---> 993fb106f2c2
Step 7/7 : CMD ["/opt/jdk/bin/java", "--version"] - to check JDK version
---> Running in 8e1658f5f84d
Removing intermediate container 8e1658f5f84d
---> 350dd3a72a7d
Successfully built 350dd3a72a7d
Even though the JDK image is 195MB the build is only 41MB. We can tag the image.
[v@alpine-java jdk13_v]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 350dd3a72a7d 21 seconds ago 41.7MB
<none> <none> d7b2793ed509 25 minutes ago 565MB
alpine latest cdf98d1859c1 2 weeks ago 5.53MB
[v@alpine-java jdk13_v]$ sudo docker tag 350dd3a72a7d jdk-13-musl/jdk-version:v1
[v@alpine-java jdk13_v]$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jdk-13-musl/jdk-version v1 350dd3a72a7d About a minute ago 41.7MB
<none> <none> d7b2793ed509 27 minutes ago 565MB
alpine latest cdf98d1859c1 2 weeks ago 5.53MB
Running the container:
[v@alpine-java jdk13_v]$ sudo docker run jdk-13-musl/jdk-version:v1
openjdk 13-ea 2019-09-17
OpenJDK Runtime Environment (build 13-ea+16)
OpenJDK 64-Bit Server VM (build 13-ea+16, mixed mode)
Building a HelloWorld application
Now we have a base container that we can use to create one with a Java app. Lets use a simple HelloWorld.java.
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World");
}
}
Compile the Java code:
javac HelloWorld.java
Having another Dockerfile for the app container:
FROM jdk-13-musl/jdk-version:v1
ADD HelloWorld.class /
CMD ["/opt/jdk/bin/java", "HelloWorld"]
Building container:
sudo docker build .
After tagging we can run HelloWorld:
sudo docker run jdk-13-musl/hello-world:v1
Hello, World
The entire docker run takes around 600ms. Not bad for Java.
Top comments (6)
For adding more modules:-
Take notes there should not be any space between the modules. For example
java.base, java.instrument
will fail. Took me sometimes to figure out.You should look into modularity of the JVM and produce a custom Java runtime for your app (using jlink).
This will cut your runtime size by at least 50% in size.
This is exactly what I am doing:
Hhmmm.... Thanks. I missed that. But unclear how the image is still at 41mb.
The JDK13 is not available anymore. The link should be updated to the JDK14-ea version: download.java.net/java/early_acces...
Updated to JDK 14