DEV Community

Istvan
Istvan

Posted on • Edited on

Small Alpine Linux containers with Java

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:

https://download.java.net/java/early_access/alpine/15/binaries/openjdk-14-ea+15_linux-x64-musl_bin.tar.gz

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"]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen 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");
  }
}
Enter fullscreen mode Exit fullscreen mode

Compile the Java code:

javac HelloWorld.java
Enter fullscreen mode Exit fullscreen mode

Having another Dockerfile for the app container:

FROM jdk-13-musl/jdk-version:v1
ADD HelloWorld.class /
CMD ["/opt/jdk/bin/java", "HelloWorld"]
Enter fullscreen mode Exit fullscreen mode

Building container:

sudo docker build .
Enter fullscreen mode Exit fullscreen mode

After tagging we can run HelloWorld:

sudo docker run jdk-13-musl/hello-world:v1
Hello, World
Enter fullscreen mode Exit fullscreen mode

The entire docker run takes around 600ms. Not bad for Java.

Top comments (6)

Collapse
 
k4ml profile image
Kamal Mustafa

For adding more modules:-

RUN ["/opt/jdk/jdk-14/bin/jlink", "--compress=2", \
     "--module-path", "/opt/jdk/jdk-14/jmods/", \
     "--add-modules", "java.base,java.instrument,java.xml", \
     "--output", "/jlinked"]

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.

Collapse
 
brunoborges profile image
Bruno Borges

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.

Collapse
 
l1x profile image
Istvan

This is exactly what I am doing:

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"]
Collapse
 
brunoborges profile image
Bruno Borges

Hhmmm.... Thanks. I missed that. But unclear how the image is still at 41mb.

Collapse
 
dawand profile image
Dawand Sulaiman

The JDK13 is not available anymore. The link should be updated to the JDK14-ea version: download.java.net/java/early_acces...

Collapse
 
l1x profile image
Istvan

Updated to JDK 14