In this article, you can read more about a Quarkus code guide I tried out, related to the following topic:
- Packaging the native executable in a container
In a next article, you can read more about another Quarkus code guide I tried out, related to the following topic:
- The ability to automatically generate Kubernetes resources by Quarkus
Quarkus guide “Building a Native Executable”
From the Quarkus guide “Creating Your First Application, part “What’s next?”, I clicked on the link “building a native executable guide”.
[https://quarkus.io/guides/getting-started#whats-next]
Building a native executable requires using a distribution of GraalVM.
In my previous article I already covered some of the topics in this code guide. So, I will now continue with the topic related to “Creating a container”.
[https://quarkus.io/guides/building-native-image#creating-a-container]
Remember, for compiling the application to a native executable, I described the following two ways:
- By using a local GraalVM installation
- By leveraging a container runtime such as Docker and using a Docker container that embeds GraalVM
This guide continues with the code in the existing “getting-started” Maven project.
Creating a container manually
You can run the application in a container using the JAR produced by the Quarkus Maven Plugin or by using the produced native executable.
Quarkus provides Dockerfile’s for both options. In this article I will explore these options further.
Remark:
The difference between the Dockerfile.fast-jar and Dockerfile.jvm (both used to build a container that runs the Quarkus application in JVM mode) is the use of four distinct layers in the fast variant:
# We make four distinct layers so if there are application changes the library layers can be re-used COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ COPY --chown=1001 target/quarkus-app/*.jar /deployments/ COPY --chown=1001 target/quarkus-app/app/ /deployments/app/ COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/
If you need more information about Docker, you can have a look at the “Dockerfile reference” and the “Docker Cheat Sheet”.
[https://www.docker.com/]
The provided Dockerfiles use UBI (Universal Base Image) as parent image. This base image has been tailored to work perfectly in containers. The Dockerfiles use the minimal version of the base image to reduce the size of the produced image.
[https://quarkus.io/guides/building-native-image#manually]
For more information please see: https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image
Build a container that runs the Quarkus application in JVM mode
Below, you can see the content of the src/main/docker/Dockerfile.jvm Dockerfile, provided by the Quarkus project generation:
#### # This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode # # Before building the docker image run: # # mvn package # # Then, build the image with: # # docker build -f src/main/docker/Dockerfile.jvm -t quarkus/getting-started-jvm . # # Then run the container using: # # docker run -i --rm -p 8080:8080 quarkus/getting-started-jvm # # If you want to include the debug port into your docker image # you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 # # Then run the container using : # # docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/getting-started-jvm # ### FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 ARG JAVA_PACKAGE=java-11-openjdk-headless ARG RUN_JAVA_VERSION=1.3.8 ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' # Install java and the run-java script # Also set up permissions for user `1001` RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ && microdnf update \ && microdnf clean all \ && mkdir /deployments \ && chown 1001 /deployments \ && chmod "g+rwX" /deployments \ && chown 1001:root /deployments \ && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ && chown 1001 /deployments/run-java.sh \ && chmod 540 /deployments/run-java.sh \ && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security # Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" COPY target/lib/* /deployments/lib/ COPY target/*-runner.jar /deployments/app.jar EXPOSE 8080 USER 1001 ENTRYPOINT [ "/deployments/run-java.sh" ]
As you can see, this Dockerfile uses the following application resources:
- target/lib/*
- target/*-runner.jar (in our case this is: getting-started-1.0-SNAPSHOT-runner.jar)
Next, I followed the instructions in the Dockerfile.
I used vagrant ssh to connect into the running VM. Next, in order to build the image, I used the following command on the Linux Command Prompt:
cd /vagrant/applications/getting-started mvn package docker build -f src/main/docker/Dockerfile.jvm -t quarkus/getting-started-jvm .
With the following output (only showing the part about the image build):
Sending build context to Docker daemon 49.57MB Step 1/11 : FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 8.1: Pulling from ubi8/ubi-minimal b26afdf22be4: Pull complete 218f593046ab: Pull complete Digest: sha256:df6f9e5d689e4a0b295ff12abc6e2ae2932a1f3e479ae1124ab76cf40c3a8cdd Status: Downloaded newer image for registry.access.redhat.com/ubi8/ubi-minimal:8.1 ---> 91d23a64fdf2 Step 2/11 : ARG JAVA_PACKAGE=java-11-openjdk-headless ---> Running in 24697477d296 Removing intermediate container 24697477d296 ---> 3818465cc8fa Step 3/11 : ARG RUN_JAVA_VERSION=1.3.8 ---> Running in e1f40a97995f Removing intermediate container e1f40a97995f ---> c45c7ada9216 Step 4/11 : ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' ---> Running in 40779cf018dd Removing intermediate container 40779cf018dd ---> eea2c4e04c6c Step 5/11 : RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} && microdnf update && microdnf clean all && mkdir /deployments && chown 1001 /deployments && chmod "g+rwX" /deployments && chown 1001:root /deployments && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh && chown 1001 /deployments/run-java.sh && chmod 540 /deployments/run-java.sh && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security ---> Running in adfa712e3908 (process:6): librhsm-WARNING **: 09:38:48.162: Found 0 entitlement certificates (process:6): librhsm-WARNING **: 09:38:48.170: Found 0 entitlement certificates (process:6): libdnf-WARNING **: 09:38:48.176: Loading "/etc/dnf/dnf.conf": IniParser: Can't open file Downloading metadata... Downloading metadata... Downloading metadata... Package Repository Size Installing: alsa-lib-1.2.1.2-3.el8.x86_64 ubi-8-appstream 451.2 kB avahi-libs-0.7-19.el8.x86_64 ubi-8-baseos 64.6 kB copy-jdk-configs-3.7-1.el8.noarch ubi-8-appstream 27.3 kB cups-libs-1:2.2.6-33.el8.x86_64 ubi-8-baseos 442.0 kB dbus-libs-1:1.12.8-10.el8_2.x86_64 ubi-8-baseos 187.6 kB freetype-2.9.1-4.el8.x86_64 ubi-8-baseos 402.7 kB java-11-openjdk-headless-1:11.0.8.10-0.el8_2.x86_64 ubi-8-appstream 41.6 MB javapackages-filesystem-5.3.0-1.module+el8+2447+6f56d9a6.noarch ubi-8-appstream 31.1 kB lcms2-2.9-2.el8.x86_64 ubi-8-appstream 168.6 kB libjpeg-turbo-1.5.3-10.el8.x86_64 ubi-8-appstream 159.2 kB libpng-2:1.6.34-5.el8.x86_64 ubi-8-baseos 129.0 kB lksctp-tools-1.0.18-3.el8.x86_64 ubi-8-baseos 101.9 kB lua-5.3.4-11.el8.x86_64 ubi-8-appstream 197.4 kB tzdata-java-2020a-1.el8.noarch ubi-8-appstream 193.4 kB Transaction Summary: Installing: 14 packages Reinstalling: 0 packages Upgrading: 0 packages Removing: 0 packages Downgrading: 0 packages Downloading packages... Running transaction test... Installing: tzdata-java;2020a-1.el8;noarch;ubi-8-appstream Installing: alsa-lib;1.2.1.2-3.el8;x86_64;ubi-8-appstream Installing: lcms2;2.9-2.el8;x86_64;ubi-8-appstream Installing: lua;5.3.4-11.el8;x86_64;ubi-8-appstream Installing: copy-jdk-configs;3.7-1.el8;noarch;ubi-8-appstream Installing: libjpeg-turbo;1.5.3-10.el8;x86_64;ubi-8-appstream Installing: javapackages-filesystem;5.3.0-1.module+el8+2447+6f56d9a6;noarch;ubi-8-appstream Installing: lksctp-tools;1.0.18-3.el8;x86_64;ubi-8-baseos Installing: (null) Installing: libpng;2:1.6.34-5.el8;x86_64;ubi-8-baseos Installing: (null) Installing: freetype;2.9.1-4.el8;x86_64;ubi-8-baseos Installing: (null) Installing: dbus-libs;1:1.12.8-10.el8_2;x86_64;ubi-8-baseos Installing: (null) Installing: avahi-libs;0.7-19.el8;x86_64;ubi-8-baseos Installing: (null) Installing: cups-libs;1:2.2.6-33.el8;x86_64;ubi-8-baseos Installing: (null) Installing: java-11-openjdk-headless;1:11.0.8.10-0.el8_2;x86_64;ubi-8-appstream Installing: (null) Installing: (null) Complete. (process:88): librhsm-WARNING **: 09:39:48.421: Found 0 entitlement certificates (process:88): librhsm-WARNING **: 09:39:48.436: Found 0 entitlement certificates (process:88): libdnf-WARNING **: 09:39:48.439: Loading "/etc/dnf/dnf.conf": IniParser: Can't open file Downloading metadata... Downloading metadata... Downloading metadata... Package Repository Size Installing: acl-2.2.53-1.el8.x86_64 ubi-8-baseos 83.0 kB cracklib-2.9.6-15.el8.x86_64 ubi-8-baseos 95.5 kB cracklib-dicts-2.9.6-15.el8.x86_64 ubi-8-baseos 4.1 MB cryptsetup-libs-2.2.2-1.el8.x86_64 ubi-8-baseos 438.7 kB … Cleanup: tzdata;2019c-1.el8;noarch;installed Updating: (null) Cleanup: libgcc;8.3.1-4.5.el8;x86_64;installed Updating: (null) Updating: (null) Complete. (microdnf:428): librhsm-WARNING **: 09:41:07.115: Found 0 entitlement certificates (microdnf:428): librhsm-WARNING **: 09:41:07.121: Found 0 entitlement certificates (microdnf:428): libdnf-WARNING **: 09:41:07.124: Loading "/etc/dnf/dnf.conf": IniParser: Can't open file Complete. % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 20218 100 20218 0 0 44047 0 --:--:-- --:--:-- --:--:-- 43952 Removing intermediate container adfa712e3908 ---> eedc98908837 Step 6/11 : ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" ---> Running in b6ca56407d27 Removing intermediate container b6ca56407d27 ---> ef0d7a761730 Step 7/11 : COPY target/lib/* /deployments/lib/ ---> 05b7920f320e Step 8/11 : COPY target/*-runner.jar /deployments/app.jar ---> 91c87499f38b Step 9/11 : EXPOSE 8080 ---> Running in 59a65a8d0320 Removing intermediate container 59a65a8d0320 ---> ff0941ccfcbd Step 10/11 : USER 1001 ---> Running in f0a9e7464e88 Removing intermediate container f0a9e7464e88 ---> 5a06df536e73 Step 11/11 : ENTRYPOINT [ "/deployments/run-java.sh" ] ---> Running in 799381d98a8b Removing intermediate container 799381d98a8b ---> b41bf47f82ac Successfully built b41bf47f82ac Successfully tagged quarkus/getting-started-jvm:latest vagrant@ubuntu-bionic:/vagrant/applications/getting-started$
In order to get a list of all the docker images, I used the following command on the Linux Command Prompt:
docker image ls
With the following output:
Here you can see that image quarkus/getting-started-jvm:latest is created.
Remark:
See the Dockerfile and the option -t used in the docker build command (docker build -f src/main/docker/Dockerfile.jvm -t quarkus/getting-started-jvm .).
–tag , -t: Name and optionally a tag in the ‘name:tag’ format
[https://docs.docker.com/engine/reference/commandline/build/]
Next, in order to run the container, I used the following command on the Linux Command Prompt:
docker run -i --rm -p 8090:8090 quarkus/getting-started-jvm
Remark:
In my previous article I described how I changed the application HTTP port from 8080 to 8090, via src/main/resources/application.properties.
[https://technology.amis.nl/2020/09/01/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part1/]
So, I needed to expose port 8090 externally (instead of 8080), mapped to port 8090 inside the container.
See the Dockerfile and the option -p used in the docker run command (docker run -i –rm -p 8080:8080 quarkus/ quarkus/getting-started-jvm .).
–publish , -p: Publish a container’s port(s) to the host
[https://docs.docker.com/engine/reference/commandline/run/]
With the following output:
exec java -Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager -XX:+ExitOnOutOfMemoryError -cp . -jar /deployments/app.jar
Powered by Quarkus 1.7.0.Final 2020-08-30 10:22:11,001 INFO [io.quarkus] (main) getting-started 1.0-SNAPSHOT on JVM (powered by Quarkus 1.7.0.Final) started in 6.542s. Listening on: http://0.0.0.0:8090 2020-08-30 10:22:11,382 INFO [io.quarkus] (main) Profile prod activated. 2020-08-30 10:22:11,384 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
Then, in the Web Browser on my Windows laptop, I entered the URL: http://localhost:8090/hello
And I got the following result:
So, running the application in a container using the JAR produced by the Quarkus Maven Plugin worked.
Remark:
Remember that on my Windows laptop, I could call port 8090 of my guest operating system because via port forwarding this was made possible.
[https://technology.amis.nl/2020/08/17/quarkus-supersonic-subatomic-java-setting-up-a-demo-environment-using-vagrant-and-oracle-virtualbox/]
Build a container that runs the Quarkus application in native (no JVM) mode
When using a local GraalVM installation, the native executable targets your local operating system (Linux, macOS, Windows etc). However, as a container may not use the same executable format as the one produced by your operating system, you can instruct the Maven build to produce an executable by leveraging a container runtime (as I described in my previous article).
[https://technology.amis.nl/2020/09/01/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part1/]
The produced executable will be a 64 bit Linux executable, so depending on your operating system it may no longer be runnable. However, it’s not an issue as we are going to copy it to a container.
[https://quarkus.io/guides/building-native-image#manually]
Below, you can see the content of the src/main/docker/Dockerfile.native Dockerfile, provided by the Quarkus project generation:
#### # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode # # Before building the docker image run: # # mvn package -Pnative -Dquarkus.native.container-build=true # # Then, build the image with: # # docker build -f src/main/docker/Dockerfile.native -t quarkus/getting-started . # # Then run the container using: # # docker run -i --rm -p 8080:8080 quarkus/getting-started # ### FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 WORKDIR /work/ RUN chown 1001 /work \ && chmod "g+rwX" /work \ && chown 1001:root /work COPY --chown=1001:root target/*-runner /work/application EXPOSE 8080 USER 1001 CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
As you can see, this Dockerfile uses the following application resources:
- target/*-runner (in our case this is: getting-started-1.0-SNAPSHOT-runner)
Next, I followed the instructions in the Dockerfile. By the way, the same steps are also described in the code guide.
[https://quarkus.io/guides/building-native-image#manually]
In order to build the native executable, I used the following command on the Linux Command Prompt:
mvn package -Pnative -Dquarkus.native.container-build=true
With the following output:
[INFO] Scanning for projects... [INFO] [INFO] ----------------------< org.acme:getting-started >---------------------- [INFO] Building getting-started 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:prepare (default) @ getting-started --- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ getting-started --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 4 resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ getting-started --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:prepare-tests (default) @ getting-started --- [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ getting-started --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /vagrant/applications/getting-started/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ getting-started --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:3.0.0-M5:test (default-test) @ getting-started --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.acme.getting.started.GreetingResourceTest 2020-08-30 10:49:52,067 INFO [io.quarkus] (main) Quarkus 1.7.0.Final on JVM started in 11.922s. Listening on: http://0.0.0.0:8081 2020-08-30 10:49:52,265 INFO [io.quarkus] (main) Profile test activated. 2020-08-30 10:49:52,266 INFO [io.quarkus] (main) Installed features: [cdi, resteasy] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 23.889 s - in org.acme.getting.started.GreetingResourceTest 2020-08-30 10:50:01,782 INFO [io.quarkus] (main) Quarkus stopped in 0.109s [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ getting-started --- [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:build (default) @ getting-started --- [INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final [INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Checking image status quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 20.1.0-java11: Pulling from quarkus/ubi-quarkus-native-image 57de4da701b5: Already exists cf0f3ebe9f53: Already exists 78512332cf2d: Pull complete Digest: sha256:0b5f515bd2ac0a5d42534bc3332820d0226b0eae8b8f35d87fd225abd9a3ab23 Status: Downloaded newer image for quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7) [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] docker run -v /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar:/project:z --env LANG=C --user 1000:1000 --rm quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http --no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner [getting-started-1.0-SNAPSHOT-runner:19] classlist: 11,794.35 ms, 0.96 GB [getting-started-1.0-SNAPSHOT-runner:19] (cap): 1,753.72 ms, 0.94 GB [getting-started-1.0-SNAPSHOT-runner:19] setup: 6,447.66 ms, 0.94 GB 10:54:14,423 INFO [org.jbo.threads] JBoss Threads version 3.1.1.Final [getting-started-1.0-SNAPSHOT-runner:19] (clinit): 1,676.22 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (typeflow): 53,897.19 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (objects): 92,484.87 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (features): 1,433.55 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] analysis: 152,230.05 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] universe: 4,297.09 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (parse): 18,281.48 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (inline): 17,095.20 ms, 2.85 GB [getting-started-1.0-SNAPSHOT-runner:19] (compile): 98,601.71 ms, 2.64 GB [getting-started-1.0-SNAPSHOT-runner:19] compile: 137,707.60 ms, 2.64 GB [getting-started-1.0-SNAPSHOT-runner:19] image: 7,393.06 ms, 2.87 GB [getting-started-1.0-SNAPSHOT-runner:19] write: 1,300.51 ms, 2.87 GB [getting-started-1.0-SNAPSHOT-runner:19] [total]: 322,056.71 ms, 2.87 GB [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Execute [objcopy, --strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner] [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 499945ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 09:09 min [INFO] Finished at: 2020-08-30T10:58:24Z [INFO] ------------------------------------------------------------------------ vagrant@ubuntu-bionic:/vagrant/applications/getting-started$
In order to build the image, I used the following command on the Linux Command Prompt:
docker build -f src/main/docker/Dockerfile.native -t quarkus/getting-started .
With the following output:
Sending build context to Docker daemon 49.56MB Step 1/7 : FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 ---> 91d23a64fdf2 Step 2/7 : WORKDIR /work/ ---> Running in 5b16a3ec287a Removing intermediate container 5b16a3ec287a ---> 9798e17519ed Step 3/7 : RUN chown 1001 /work && chmod "g+rwX" /work && chown 1001:root /work ---> Running in c8e6d0d90e80 Removing intermediate container c8e6d0d90e80 ---> d044aa8c49c1 Step 4/7 : COPY --chown=1001:root target/*-runner /work/application ---> d01372ad5bce Step 5/7 : EXPOSE 8080 ---> Running in 0ab79da4d9d6 Removing intermediate container 0ab79da4d9d6 ---> 17d0a05e924f Step 6/7 : USER 1001 ---> Running in 735b1fcd6f2f Removing intermediate container 735b1fcd6f2f ---> 5a4c711603b1 Step 7/7 : CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] ---> Running in 13c2ce17e6f0 Removing intermediate container 13c2ce17e6f0 ---> 7f29ba864d85 Successfully built 7f29ba864d85 Successfully tagged quarkus/getting-started:latest vagrant@ubuntu-bionic:/vagrant/applications/getting-started$
In order to get a list of all the docker images, I used the following command on the Linux Command Prompt:
docker image ls
With the following output:
Here you can see that image quarkus/getting-started:latest is created.
Remark:
See the Dockerfile and the option -t used in the docker build command (docker build -f src/main/docker/Dockerfile.native -t quarkus/getting-started .).
–tag , -t: Name and optionally a tag in the ‘name:tag’ format
[https://docs.docker.com/engine/reference/commandline/build/]
Next, in order to run the container, I used the following command on the Linux Command Prompt:
docker run -i --rm -p 8090:8090 quarkus/getting-started
Remark:
See the remark about using application HTTP port 8090 earlier in this article.
With the following output:
Then, in the Web Browser on my Windows laptop, I entered the URL: http://localhost:8090/hello
And I got the following result:
So, running the application in a container using the produced native executable worked.
Creating a container using the container-image extensions
By far the easiest way to create a container-image from your Quarkus application is to leverage one of the container-image extensions.
If one of those extensions is present, then creating a container image for the native executable is essentially a matter of executing a single command:
./mvnw package -Pnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
Remark:
- quarkus.native.container-build=true allows for creating a Linux executable without GraalVM being installed (and is only necessary if you don’t have GraalVM installed locally or your local operating system is not Linux)
- quarkus.container-image.build=true instructs Quarkus to create a container-image using the final application artifact (which is the native executable in this case)
[https://quarkus.io/guides/building-native-image#using-the-container-image-extensions]
Remember from my previous article, the build is done using a container runtime. If the Quarkus config property quarkus.native.container-build is set, Docker will be used by default, unless container-runtime (by using property quarkus.native.container-runtime) is also set. In my demo environment I had Docker installed, so this is fine by me.
[https://technology.amis.nl/2020/09/01/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part1/]
Quarkus provides extensions for building (and pushing) container images. Currently it supports:
- Jib
- Docker
- S2I
Please see the documentation for more information about these different extensions.
[https://quarkus.io/guides/container-image]
Using the Docker container-image extension
Because in my demo environment I had Docker installed, I focused on the Docker container-image extension.
The extension quarkus-container-image-docker is using the Docker binary and the generated Dockerfiles under src/main/docker in order to perform Docker builds.
[https://quarkus.io/guides/container-image]
To use this feature, add the extension to your project. So, in order to add it, I used the following command on the Linux Command Prompt:
./mvnw quarkus:add-extension -Dextensions="container-image-docker"
With the following output:
In the pom.xml this added the following dependency:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-container-image-docker</artifactId> </dependency>
In order to get a list of all the docker images, I used the following command on the Linux Command Prompt:
docker image ls
With the following output:
In order to build the Docker image, I used the following command (mentioned earlier) on the Linux Command Prompt:
./mvnw package -Pnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
With the following output:
[INFO] Scanning for projects... [INFO] [INFO] ----------------------< org.acme:getting-started >---------------------- [INFO] Building getting-started 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker/1.7.0.Final/quarkus-container-image-docker-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker/1.7.0.Final/quarkus-container-image-docker-1.7.0.Final.pom (1.4 kB at 701 B/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker-parent/1.7.0.Final/quarkus-container-image-docker-parent-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker-parent/1.7.0.Final/quarkus-container-image-docker-parent-1.7.0.Final.pom (741 B at 6.6 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-parent/1.7.0.Final/quarkus-container-image-parent-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-parent/1.7.0.Final/quarkus-container-image-parent-1.7.0.Final.pom (933 B at 6.3 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker/1.7.0.Final/quarkus-container-image-docker-1.7.0.Final.jar Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker/1.7.0.Final/quarkus-container-image-docker-1.7.0.Final.jar (3.0 kB at 23 kB/s) [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:prepare (default) @ getting-started --- Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker-deployment/1.7.0.Final/quarkus-container-image-docker-deployment-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker-deployment/1.7.0.Final/quarkus-container-image-docker-deployment-1.7.0.Final.pom (1.6 kB at 2.7 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-deployment/1.7.0.Final/quarkus-container-image-deployment-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-deployment/1.7.0.Final/quarkus-container-image-deployment-1.7.0.Final.pom (1.9 kB at 17 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-spi/1.7.0.Final/quarkus-container-image-spi-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-spi/1.7.0.Final/quarkus-container-image-spi-1.7.0.Final.pom (871 B at 7.4 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-util/1.7.0.Final/quarkus-container-image-util-1.7.0.Final.pom Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-util/1.7.0.Final/quarkus-container-image-util-1.7.0.Final.pom (642 B at 6.1 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-spi/1.7.0.Final/quarkus-container-image-spi-1.7.0.Final.jar Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-util/1.7.0.Final/quarkus-container-image-util-1.7.0.Final.jar Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-deployment/1.7.0.Final/quarkus-container-image-deployment-1.7.0.Final.jar Downloading from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker-deployment/1.7.0.Final/quarkus-container-image-docker-deployment-1.7.0.Final.jar Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-util/1.7.0.Final/quarkus-container-image-util-1.7.0.Final.jar (3.6 kB at 7.7 kB/s) Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-spi/1.7.0.Final/quarkus-container-image-spi-1.7.0.Final.jar (6.5 kB at 8.9 kB/s) Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-deployment/1.7.0.Final/quarkus-container-image-deployment-1.7.0.Final.jar (11 kB at 15 kB/s) Downloaded from central: https://repo.maven.apache.org/maven2/io/quarkus/quarkus-container-image-docker-deployment/1.7.0.Final/quarkus-container-image-docker-deployment-1.7.0.Final.jar (21 kB at 27 kB/s) [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ getting-started --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 4 resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ getting-started --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:prepare-tests (default) @ getting-started --- [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ getting-started --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /vagrant/applications/getting-started/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ getting-started --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:3.0.0-M5:test (default-test) @ getting-started --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running org.acme.getting.started.GreetingResourceTest 2020-09-01 18:50:03,249 INFO [io.quarkus] (main) Quarkus 1.7.0.Final on JVM started in 11.691s. Listening on: http://0.0.0.0:8081 2020-09-01 18:50:03,261 INFO [io.quarkus] (main) Profile test activated. 2020-09-01 18:50:03,262 INFO [io.quarkus] (main) Installed features: [cdi, resteasy] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 33.676 s - in org.acme.getting.started.GreetingResourceTest 2020-09-01 18:50:13,067 INFO [io.quarkus] (main) Quarkus stopped in 0.103s [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ getting-started --- [INFO] Building jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT.jar [INFO] [INFO] --- quarkus-maven-plugin:1.7.0.Final:build (default) @ getting-started --- [INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final [INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Checking image status quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 20.1.0-java11: Pulling from quarkus/ubi-quarkus-native-image Digest: sha256:0b5f515bd2ac0a5d42534bc3332820d0226b0eae8b8f35d87fd225abd9a3ab23 Status: Image is up to date for quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7) [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] docker run -v /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar:/project:z --env LANG=C --user 1000:1000 --rm quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http --no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner [getting-started-1.0-SNAPSHOT-runner:19] classlist: 29,007.23 ms, 0.96 GB [getting-started-1.0-SNAPSHOT-runner:19] (cap): 4,912.59 ms, 0.94 GB [getting-started-1.0-SNAPSHOT-runner:19] setup: 18,197.04 ms, 0.94 GB 18:54:16,535 INFO [org.jbo.threads] JBoss Threads version 3.1.1.Final [getting-started-1.0-SNAPSHOT-runner:19] (clinit): 3,579.00 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (typeflow): 150,265.81 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (objects): 249,250.13 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (features): 3,833.43 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] analysis: 413,141.58 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] universe: 10,326.31 ms, 2.69 GB [getting-started-1.0-SNAPSHOT-runner:19] (parse): 61,713.93 ms, 2.71 GB [getting-started-1.0-SNAPSHOT-runner:19] (inline): 26,986.36 ms, 2.78 GB [getting-started-1.0-SNAPSHOT-runner:19] (compile): 284,875.97 ms, 3.25 GB [getting-started-1.0-SNAPSHOT-runner:19] compile: 382,876.95 ms, 3.25 GB [getting-started-1.0-SNAPSHOT-runner:19] image: 20,813.61 ms, 3.25 GB [getting-started-1.0-SNAPSHOT-runner:19] write: 2,789.28 ms, 3.25 GB [getting-started-1.0-SNAPSHOT-runner:19] [total]: 879,723.32 ms, 3.25 GB [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Execute [objcopy, --strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner] [INFO] [io.quarkus.container.image.docker.deployment.DockerWorking] Docker daemon found. Version:'19.03.12' [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Starting docker image build [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Executing the following command to build docker image: 'docker build -f /vagrant/applications/getting-started/src/main/docker/Dockerfile.native -t vagrant/getting-started:1.0-SNAPSHOT /vagrant/applications/getting-started' [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Sending build context to Docker daemon 50.82MB [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 1/7 : FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> 91d23a64fdf2 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 2/7 : WORKDIR /work/ [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> Using cache [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> 9798e17519ed [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 3/7 : RUN chown 1001 /work && chmod "g+rwX" /work && chown 1001:root /work [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> Using cache [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> d044aa8c49c1 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 4/7 : COPY --chown=1001:root target/*-runner /work/application [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> 9051879c623f [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 5/7 : EXPOSE 8080 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> Running in b80af8776a44 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Removing intermediate container b80af8776a44 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> ff10cb14076e [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 6/7 : USER 1001 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> Running in 9f6ff323e89c [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Removing intermediate container 9f6ff323e89c [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> 44d8ed57f354 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Step 7/7 : CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> Running in 30e6130446d4 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Removing intermediate container 30e6130446d4 [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] ---> 9b131cd3181c [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Successfully built 9b131cd3181c [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Successfully tagged vagrant/getting-started:1.0-SNAPSHOT [INFO] [io.quarkus.container.image.docker.deployment.DockerProcessor] Built container image vagrant/getting-started:1.0-SNAPSHOT (9b131cd3181c) [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 917108ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 16:23 min [INFO] Finished at: 2020-09-01T19:05:32Z [INFO] ------------------------------------------------------------------------ vagrant@ubuntu-bionic:/vagrant/applications/getting-started$
For your and my convenience, in the table below I summarized the Quarkus deployment steps (io.quarkus.deployment.pkg.steps) from the output above:
[in bold, I highlighted the changes against the previous summarization]
As you can see in the output above, for creating the native executable, the following docker image is used:
quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11
In order to get a list of all the docker images, I used the following command on the Linux Command Prompt:
docker image ls
With the following output:
Here you can see that image vagrant/getting-started:1.0-SNAPSHOT is created.
Remark:
This name is different from the one we got before, while executing these steps manually (via: docker build -f src/main/docker/Dockerfile.native -t quarkus/getting-started .).
You can see, from the output above that the following command was used to build the docker image:
So, let’s figure out why this image name and tag was used.
If you want to customize the container image build process, you can use several properties. Please see the documentation.
[https://quarkus.io/guides/container-image#customizing]
With regard to the image name and tag, the following properties are used:
- quarkus.container-image.group The group the container image will be part of. If not set defaults to: ${user.name} (being the the system user name)
- quarkus.container-image.name The name of the container image. If not set defaults to: ${quarkus.application.name:unset}
- quarkus.container-image.tag The tag of the container image. If not set defaults to: ${quarkus.application.version:latest}
Remark:
In addition to the generic container image options, the container-image-docker also provides some options. Please see the documentation.
[https://quarkus.io/guides/container-image#customizing]
In the example application.properties, I created earlier (described in my previous article), I could see that the image name defaults to the name of the project and the version defaults to the version of the project.
[https://technology.amis.nl/2020/09/01/quarkus-supersonic-subatomic-java-trying-out-some-quarkus-code-guides-part1/]
# # The name of the application. # If not set, defaults to the name of the project (except for tests where it is not set at all). # #quarkus.application.name= # # The version of the application. # If not set, defaults to the version of the project (except for tests where it is not set at all). # #quarkus.application.version=
Remember the contents of pom.xml is:
<?xml version="1.0"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>org.acme</groupId> <artifactId>getting-started</artifactId> <version>1.0-SNAPSHOT</version> ... </project>
So, putting it all together: <user>/<name of the project>:<version of the project >
This explains the image name and tag: vagrant/getting-started:1.0-SNAPSHOT
Remark:
A Docker image name can be made up of slash-separated name components.
So, I conclude this article. I shared with you the steps I took trying out the Quarkus code guide “Building a Native Executable”, and more specific the steps related to the topic “Creating a container”. I described how you can create a container manually, using the JAR produced by the Quarkus Maven Plugin or by using a produced native executable.
By far the easiest way to create a container-image from your Quarkus application is to leverage one of the container-image extensions, like the one I used, the Docker container-image extension.
In a next article, you can read more about another Quarkus code guide I tried out, related to the following topic:
- The ability to automatically generate Kubernetes resources by Quarkus