Today, let's take a look at how fast the native executable built by Quarkus can be faster than the Spring application. The maturity of ecology is not discussed here.
TLDR
First, let's draw a conclusion and make a comparison with the Spring Web application with only one Controller.
Application start time: 0.012s vs 2.294s
Image size: 49MB vs 237 MB
The Spring application image uses openjdk:11.0-jre-slim as the base image, with a size of 220MB.
docker images REPOSITORY TAG IMAGE ID CREATED SIZE spring/spring-getting-started latest 5f47030c5c3f 6 minutes ago 237MB quarkus/quarkus-getting-started distroless2 fe973c5ac172 24 minutes ago 49MB quarkus/quarkus-getting-started distroless 6fe27dd44e86 31 minutes ago 51MB quarkus/quarkus-getting-started ubi 8f86f5915715 58 minutes ago 132MB
The dilemma of Java application containerization
In the cloud native world, application containerization is a significant feature. When Java applications are containerized, they face the following problems:
- Slow application startup: in fact, this is the problem of Java applications. Java applications occupy more memory; When starting a JVM virtual machine, you need to initialize the environment, preload a large number of classes, initialize threads, and so on. The startup time can take several seconds or even minutes depending on the application. The longer start-up time also inhibits horizontal scalability. Even in Serverless scenarios where the response time is not high, it will be rejected.
- Too large image: in fact, the layered design of image is used, which is a common spring cloud application ̈ The BER jar package may have 7 or 80MB.
- Space occupation: Although image layering is used, a little makes a mickle, which will also increase the storage cost.
Quarkus and native image
Quarkus's development follows the principle of container first:
- Graal/SubstrateVM support
- Process metadata at build time
- Reduce the use of reflections
- Native image pre boot
Native image is a technology that compiles java code into executable files (called native image) in advance. The executable includes application classes, classes in their dependencies, runtime classes, and statically linked native code in the JDK. It does not run on the Java VM, but includes necessary components, such as memory management, thread scheduling, etc. these components come from another runtime system "substrate VM". "Substrate VM" is the name of the runtime component (e.g. de optimizer, garbage collector, thread scheduling, etc.). Compared with the JVM, the generated program has faster startup time and lower runtime memory overhead.
How to build a native image
Refer to the previous article for environment configuration. You can download the source code directly from here.
Configuring GraalVM
Previously, we used sdkman to install GraalVM. Set graavm_ Home environment variable:
export GRAALVM_HOME=`sdk home java 21.0.0.2.r11-grl`
To install native image using gu:
${GRAALVM_HOME}/bin/gu install native-image
Build native executable
In the source POM In XML, we can see the following profile:
<profiles> <profile> <id>native</id> <properties> <quarkus.package.type>native</quarkus.package.type> </properties> </profile> </profiles>
We use this profile to build the native executable. The whole construction takes several minutes.
./mvnw package -Pnative
Partial build log:
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ quarkus-getting-started --- [INFO] [INFO] --- quarkus-maven-plugin:1.13.0.Final:build (default) @ quarkus-getting-started --- [INFO] [org.jboss.threads] JBoss Threads version 3.2.0.Final [INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /Users/addo/Workspaces/private_w/quarkus-getting-started/target/quarkus-getting-started-1.0.0-SNAPSHOT-native-image-source-jar/quarkus-getting-started-1.0.0-SNAPSHOT-runner.jar [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /Users/addo/Workspaces/private_w/quarkus-getting-started/target/quarkus-getting-started-1.0.0-SNAPSHOT-native-image-source-jar/quarkus-getting-started-1.0.0-SNAPSHOT-runner.jar [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner] Using docker to run the native image builder [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner] Checking image status quay.io/quarkus/ubi-quarkus-native-image:21.0.0-java11 21.0.0-java11: Pulling from quarkus/ubi-quarkus-native-image Digest: sha256:becf08de869e707beaa5e57444b533ef93ebef15aad90c92ac660ddf7cea2b11 Status: Image is up to date for quay.io/quarkus/ubi-quarkus-native-image:21.0.0-java11 quay.io/quarkus/ubi-quarkus-native-image:21.0.0-java11 [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 21.0.0 (Java Version 11.0.10+8-jvmci-21.0-b06) [INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] docker run --env LANG=C --rm -v /Users/addo/Workspaces/private_w/quarkus-getting-started/target/quarkus-getting-started-1.0.0-SNAPSHOT-native-image-source-jar:/project:z quay.io/quarkus/ubi-quarkus-native-image:21.0.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-Duser.country=CN -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy\$BySpaceAndTime -H:+JNI -H:+AllowFoldMethods -jar quarkus-getting-started-1.0.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -J-Xmx5g -H:-AddAllCharsets -H:EnableURLProtocols=http --no-server -H:-UseServiceLoaderFeature -H:+StackTrace quarkus-getting-started-1.0.0-SNAPSHOT-runner [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] classlist: 5,859.24 ms, 0.96 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (cap): 633.34 ms, 0.94 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] setup: 2,468.19 ms, 0.94 GB 00:06:00,437 INFO [org.jbo.threads] JBoss Threads version 3.2.0.Final [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (clinit): 516.65 ms, 2.23 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (typeflow): 12,642.02 ms, 2.23 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (objects): 11,340.37 ms, 2.23 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (features): 525.87 ms, 2.23 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] analysis: 26,032.67 ms, 2.23 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] universe: 1,394.06 ms, 2.16 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (parse): 2,690.38 ms, 2.16 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (inline): 4,336.77 ms, 2.73 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] (compile): 17,580.03 ms, 2.71 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] compile: 26,152.06 ms, 2.71 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] image: 3,288.43 ms, 2.70 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] write: 1,904.64 ms, 2.70 GB [quarkus-getting-started-1.0.0-SNAPSHOT-runner:25] [total]: 67,414.16 ms, 2.70 GB [WARNING] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] objcopy executable not found in PATH. Debug symbols will not be separated from executable. [WARNING] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] That will result in a larger native image with debug symbols embedded in it. [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 74739ms [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 01:21 min [INFO] Finished at: 2021-04-17T08:06:47+08:00 [INFO] ------------------------------------------------------------------------
If something like caused by: Java occurs during construction lang.RuntimeException: Image generation failed. Exit code was 137 which indicates an out of memory error. Consider increasing the Xmx value for native image generation by setting the "quarkus. Native. Native image Xmx" property. You need to adjust the settings of Docker, such as the Mac OS used by the author, open Docker desktop > preference > resource > advanced, and increase the memory from the default 2GB, such as 8GB.
It can be seen from the build log that the build process is in quay Completed in the container of IO / quarkus / UBI quarkus native image. Although the exception prompt adjusts "quarkus. Native. Native image Xmx", it is actually caused by the small memory of the container.
After successful construction, you can find quarkus-getting-started-1.0.0-SNAPSHOT-runner in target. This is an executable file with a size of 28MB.
Trying to execute the file, received Zsh: exec format error:/ Target / quarkus-getting-started-1.0.0-snapshot-runner error. Because this is a Linux executable, we need to run it in the container.
Build native image
In the src/main/docker directory of the source file, we can find dockerfile native :
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3 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"]
Run mirror
Run it locally, and you can see that it only takes 0.013s to start.
docker run --rm -p 8080:8080 quarkus/quarkus-getting-started:latest __ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ --\___\_\____/_/ |_/_/|_/_/|_|\____/___/ 2021-04-17 00:22:27,146 INFO [io.quarkus] (main) quarkus-getting-started 1.0.0-SNAPSHOT native (powered by Quarkus 1.13.0.Final) started in 0.013s. Listening on: http://0.0.0.0:8080 2021-04-17 00:22:27,147 INFO [io.quarkus] (main) Profile prod activated. 2021-04-17 00:22:27,147 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
Test the endpoint:
http :8080/hello/greeting/quarkus HTTP/1.1 200 OK Content-Length: 14 Content-Type: text/plain;charset=UTF-8 Hello, quarkus
Take a look at the image information. The size is 132MB, of which the base image UBI minimal accounts for 103 MB. It still feels a little big. Do you want to continue to simplify it?
docker images REPOSITORY TAG IMAGE ID CREATED SIZE quarkus/quarkus-getting-started latest 8f86f5915715 4 minutes ago 132MB registry.access.redhat.com/ubi8/ubi-minimal 8.3 604ddd554fec 2 weeks ago 103MB
Mirror slimming
There is also a file named Dockerfile in src/main/docker The Dockerfile of native destroy uses quay IO / quarkus / quarkus destroy image: 1.0 as base image
Using this Dockerfile to build, the image is much smaller, only 51MB:
docker images REPOSITORY TAG IMAGE ID CREATED SIZE quarkus/quarkus-getting-started distroless 6fe27dd44e86 33 seconds ago 51MB quarkus/quarkus-getting-started ubi 8f86f5915715 27 minutes ago 132MB quay.io/quarkus/quarkus-distroless-image 1.0 062663862a83 6 days ago 21.3MB registry.access.redhat.com/ubi8/ubi-minimal 8.3 604ddd554fec 2 weeks ago 103MB
Run successfully:
docker run --rm -p 8080:8080 quarkus/quarkus-getting-started:distroless __ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ --\___\_\____/_/ |_/_/|_/_/|_|\____/___/ 2021-04-17 00:51:26,070 INFO [io.quarkus] (main) quarkus-getting-started 1.0.0-SNAPSHOT native (powered by Quarkus 1.13.0.Final) started in 0.013s. Listening on: http://0.0.0.0:8080 2021-04-17 00:51:26,071 INFO [io.quarkus] (main) Profile prod activated. 2021-04-17 00:51:26,071 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]
Extreme slimming, refer to here, we create dockerfile native-distroless2 .
The size of the final image is 49MB, which is 2MB smaller than the official disaster base image.
docker images REPOSITORY TAG IMAGE ID CREATED SIZE quarkus/quarkus-getting-started distroless2 fe973c5ac172 3 seconds ago 49MB
In the previous comparison, the base image openjdk:11.0-jre-slim used to build Spring applications has 220MB, which does not count the size of the application. Even openjdk: 17-alpine3 182 MB, too.
Original link: https://atbug.com/quarkus-build-native-executable-file/
If you think this article is helpful to you, you can pay attention to my official account and reply to the key words [interview] to get a Java core knowledge and arrange an interview gift! There are more technical dry goods articles and related materials to share. Let's learn and make progress together!