◀Table of Contents
Containerise a Native Executable and Run in a Docker Container
Docker containers provide the flexibility of development environments to match a production environment, to help isolate your application, and to minimize overhead. For self-contained executables, generated with GraalVM Native Image, containers are an obvious deployment scenario.
To support container-based development, there are several GraalVM container images available, depending on the platform, the architecture, the Java version, and the edition:
- GraalVM Enterprise container images are in Oracle Container Registry
- GraalVM Community container images in GitHub Container Registry
This guide shows how to containerise a Java application with Docker on macOS.
You will use ghcr.io/graalvm/jdk:ol8-java17
which is a size compact GraalVM Community container image with the GraalVM JDK pre-installed.
The Dockerfile will be provided.
Prerequisites
- Docker-API compatible container runtime like Rancher Desktop or Docker installed to run MySQL and to run tests using Testcontainers.
Note on a Sample Application
For the demo you will use the Spring Boot 3 Native Image Microservice example.
-
Clone the GraalVM Demos repository and enter the application directory:
git clone https://github.com/graalvm/graalvm-demos cd spring-native-image
-
Build a native executable and run the application:
mvn -Pnative native:compile
The
-Pnative
profile is used to turn on building a native executable with Maven.This will create a binary executable
target/benchmark-jibber
. Start it to see the application running:./target/benchmark-jibber & curl http://localhost:8080/jibber fg
Now that you have a native executable version of the sample application (target/jibber
) and seen it working, you can proceed to the next steps.
Containerise a Native Executable
The output of a native executable is platform-dependent. If you use a Mac or Windows, to build a Docker image containing your native executable, you build a native executable within a Docker container - so you need a container with a JDK distribution. If you are a Linux user, you can just pass a native executable to Docker and use the simplest slim or distroless container, depending on static libraries your application is linked against. For example:
FROM gcr.io/distroless/base
ARG APP_FILE
EXPOSE 8080
COPY target/${APP_FILE} app
ENTRYPOINT ["/jibber"]
For user’s convenience, Dockerfiles are provided with the sample application.
-
From application root folder, run this command to create a native executable within a container and then build a Docker image containing that native executable:
docker build -f Dockerfiles/Dockerfile \ --build-arg APP_FILE=./target/jibber \ -t localhost/jibber:native.01 .
It will take several minutes to set up Maven in the container and do rest of the job.
- Query Docker to look at your newly built image:
docker images | head -n2
You should see a new image listed.
-
Run the image as follows:
docker run --rm --name native -d -p 8080:8080 localhost/jibber:native.01
-
Then call the endpoint using the
curl
command in the same console window:curl http://localhost:8080/jibber
You should receive a nonsense verse in the style of the poem Jabberwocky.
You can take a look at how long the application took to startup by looking at the logs:
docker logs <CONTAINER ID>
You can also query Docker to get the size of the produced container:
docker images localhost/jibber:native.01
The difference will be more visible if you build a Docker image of the same Spring Boot application containing a JAR file instead of a native executable, and compare images startup times and file sizes.
On Linux, you can shrink your container size even more. With GraalVM Native Image you have the ability to build a statically linked native executable by packaging the native executable directly into an empty Docker image, also known as a scratch container. Continue to Build a Static or Mostly-Static Native Executable guide to learn more.