Small Alpine Linux containers with Java
Tags: linux, java, docker, devops
Published: 2019-04-24 20:14:48

---
title: Small Alpine Linux containers with Java
published: true
description: Using the Java module system and Musl libc library to create small
tags: 
  - Linux
  - Java
  - Docker
  - DevOps
---

<a id="intro"></a>
## 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](#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](https://openjdk.java.net/projects/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.

<a id="creating-containers"></a>
## [Creating Containers](#creating-containers)

First, we just create a container that has a new small size JDK.


```Dockerfile
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:

```bash
[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.

```bash
[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:

```bash
[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)
```

<a id="building-a-hello-world-app"></a>
## [Building a HelloWorld application](#building-a-hello-world-app)

Now we have a base container that we can use to create one with a Java app. Lets use a simple HelloWorld.java.

```Java
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World");
  }
}
```

Compile the Java code:

```Bash
javac HelloWorld.java
```

Having another Dockerfile for the app container:

```Dockerfile
FROM jdk-13-musl/jdk-version:v1
ADD HelloWorld.class /
CMD ["/opt/jdk/bin/java", "HelloWorld"]
```

Building container:

```bash
sudo docker build .
```

After tagging we can run HelloWorld:

```bash
sudo docker run jdk-13-musl/hello-world:v1
Hello, World
```

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