Creating Docker Images

This guide describes how to create a Docker image for your Helidon application, using a Java 8 base image or a custom Java 11 JRE built with jlink.

What You Need

About 10 minutes
Helidon Prerequisites
You’ll also need Java 11 if you want to create custom JRE’s using jlink

Java Packaging

The Helidon team recommends setting-up the class-path inside the META-INF/MANIFEST.MF file with the Class-Path entry pointing to dependency jar files in a co-located lib directory. See the Maven and Gradle examples for more details on how to package your Helidon application.

This approach is a good fit for Docker images:

  • the application code and dependencies can be separate image layers.

  • the image layer containing your dependencies is re-built only when dependencies are updated.

  • the deployment environment(s) pull the images layer containing your dependencies only when changed.

Why no fat jars?

Fat Jars are jar files that contain the application and its dependencies ; they are not optimal for Docker images as it results in a single image layer.

Creating a Java 8 Based Docker Image

This section describes the Dockerfile provided by the Helidon SE and MP quickstarts.

The Dockerfile is located at ./Dockerfile and contains the following:

# Multistage Docker build. 
# 1st stage, build the app 
FROM maven:3.5.4-jdk-9 as build

WORKDIR /helidon

# Create a first layer to cache the "Maven World" in the local repository.
# Incremental docker builds will always resume after that, unless you update
# the pom
ADD pom.xml .
RUN mvn package -DskipTests 

# Do the Maven build!
# Incremental docker builds will resume here when you change sources
ADD src src
RUN mvn package -DskipTests 

RUN echo "done!"

# 2nd stage, build the runtime image 
FROM openjdk:8-jre-slim 
WORKDIR /helidon

# Copy the binary built in the 1st stage
COPY --from=build /helidon/target/helidon-quickstart-se.jar ./
COPY --from=build /helidon/target/libs ./libs 

CMD ["java", "-jar", "helidon-quickstart-se.jar"] 
Copied
  • This is a multi-stage Docker build. See more info here
  • The first stage that creates the build artifacts
  • Create a layer, using just the pom.xml, that contains the Maven cache.
  • When resuming here, Maven won’t re-download the world.
  • The final stage that creates the image for our application.
  • Using a lightweight image.
  • The command to start the application.
Build the project
mvn package
Copied
Build the Docker image
docker build -t quickstart-se target
Copied
Run the docker container
docker run --rm -p 8080:8080 quickstart-se:latest
Copied
Ping the application
curl -X GET http://localhost:8080/greet
Copied

Creating a Docker Image with a Custom JRE

This section describes how to build an image with a custom Java 11 JRE using jlink.

Replace Dockerfile with the following:

# Multistage Docker build. 
# Stage 1: Build custom Java 11 JRE and put it in /var/tmp/myjre 
FROM openjdk:11-slim AS myjre
RUN ["jlink", "--compress=2", "--strip-debug", "--no-header-files", \
     "--add-modules", "java.base,java.logging,java.sql,java.desktop,java.management", \
     "--output", "/var/tmp/myjre"] 

# Work around for https://github.com/docker-library/openjdk/issues/217 
RUN [ "apt", "update"]
RUN [ "apt-get", "install", "-y", "binutils"]
RUN ["strip", "-p", "--strip-unneeded", "/var/tmp/myjre/lib/server/libjvm.so"]
# End work-around

# Stage 2: Build application image using JRE from Stage 1 
FROM debian:sid-slim 
COPY --from=myjre /var/tmp/myjre /opt/jre 
ENV PATH=$PATH:/opt/jre/bin

RUN mkdir /app
COPY libs /app/libs
COPY ${project.artifactId}.jar /app

CMD ["java", "-jar", "/app/${project.artifactId}.jar"]
Copied
  • This is a multi-stage Docker build. See more info here
  • The first stage that creates our custom JRE.
  • The modules listed in this example are for Helidon SE. See below for Helidon MP.
  • This is a work-around for https://github.com/docker-library/openjdk/issues/217.
  • The final stage that creates the image for our application.
  • Use debian:sid-slim to match the base image of openjdk:11-slim
  • Copy the JRE from the image of the first stage myjre
Process the new Dockerfile
mvn process-resources
Copied
Build the Docker image
docker build -t java11-quickstart-se target
Copied
Run the docker container
docker run --rm -p 8080:8080 java11-quickstart-se:latest
Copied
Ping the application
curl -X GET http://localhost:8080/greet
Copied
Take a look at the image size
docker images java11-quickstart-se:latest
Copied
REPOSITORY             TAG        IMAGE ID         CREATED             SIZE
java11-quickstart-se   latest     f07a7b8bda78     About a minute ago  136MB
Copied

~140MB is less than the pre-built OpenJDK slim JRE images. Results might differ a bit depending on your platform.

What about Helidon MP?

For Helidon MP you need to add a couple more modules to the jlink command:

RUN ["jlink", "--compress=2", "--strip-debug", "--no-header-files", \
     "--add-modules", \
     "java.base,java.logging,java.sql,java.desktop,java.management,java.naming,jdk.unsupported", \
     "--output", "/var/tmp/myjre"]
Copied