Today, let's take a look at how much faster the native executable built by Quarkus is than the Spring application, Ecological maturity is not discussed here .
TLDR
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
Spring application image usage openjdk:11.0-jre-slim As a base image, the size is 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 a problem with Java applications. Java applications occupy too much 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 is several seconds or even minutes depending on the application. 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 lot, 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 images are technologies that precompile java code into executables called native images. 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 for GraalVM installation. set up GRAALVM_HOME Environment variables:
export GRAALVM_HOME=`sdk home java 21.0.0.2.r11-grl`
use gu install native-image :
${GRAALVM_HOME}/bin/gu install native-image
Build native executable
In the source code pom.xml In, 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, which takes a long time A few 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 similar happens during construction Caused by:
java.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 This kind of error reporting. You need to adjust the Docker settings, 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.
As can be seen from the build log, the build process is
quay.io/quarkus/ubi-quarkus-native-image Completed in a container. Although the exception prompts adjustment“
Quarkus. Native. Native image Xmx "is actually caused by the small memory of the container.
After successful construction, you can target Found in
quarkus-getting-started-1.0.0-SNAPSHOT-runner . This is an executable with a size of 28MB.
Attempt to execute the file, received zsh: exec format error:
./target/quarkus-getting-started-1.0.0-SNAPSHOT-runner Wrong. Because this is a Linux executable, we need to run it in the container.
Building native images
In the source file src/main/docker In the catalog, 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. You can see that you only need to start it 0.013s .
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
Look at the image information. The size is 132MB, including base image ubi-minimal That's 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
stay src/main/docker There is another one named
Dockerfile.native-distroless Dockerfile, which uses
quay.io/quarkus/quarkus-distroless-image:1.0 Mirror as base
When you use this Dockerfile to build, the resulting 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
Compared with the above, it is used to build the base image of Spring applications openjdk:11.0-jre-slim There are already 220MB, which does not count the size of the application. even if it is openjdk:17-alpine3.13 It also has 182 MB.
Original link:
https://atbug.com/quarkus-build-native-executable-file/
If you think this article is helpful, you can pay attention to my official account, scan the code below, pay attention to "w programming diary" and get more Java data.