Quarkus构建native-image遇到的问题及解决

本机构建 native-image

如果你本地安装了 Graal VM 的话,可以在项目目录下直接执行:

mvn clean package -Pnative

构建的时间比较长,构建完成后就会在 ./target 目录下生成一个二进制执行文件,一般名字是 quarkus-demo-1.0-runner,直接执行这个二进制文件就可以运行项目了。

➜  target: ./quarkus-demo-1.0-runner 
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-07-09 16:54:10,812 INFO  [io.quarkus] (main) quarkus-demo 1.0 native (powered by Quarkus 2.0.1.Final) started in 0.121s. Listening on: http://0.0.0.0:8080
2021-07-09 16:54:11,041 INFO  [io.quarkus] (main) Profile prod activated. 
2021-07-09 16:54:11,041 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, smallrye-context-propagation]
^C2021-07-09 16:55:12,904 INFO  [io.quarkus] (Shutdown thread) quarkus-demo stopped in 0.008s

容器构建 native-image

如果你本地没有安装 Graal VM 的话,Quarkus 官方还提供了一个构建的基础镜像:quay.io/quarkus/ubi-quarkus-native-image,我们可以直接执行以下命令进行构建:

mvn clean package -Pnative -Dquarkus.native.container-build=true

容器构建遇到的问题

1. 构建时内存不足

在使用容器构建 native-image 的时候,可以会报如下错误:

[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:2.0.1.Final:build (default) on project quarkus-demo: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR]         [error]: Build step io.quarkus.deployment.pkg.steps.NativeImageBuildStep#build threw an exception: java.lang.RuntimeException: Failed to build native image
[ERROR]         at io.quarkus.deployment.pkg.steps.NativeImageBuildStep.build(NativeImageBuildStep.java:223)
[ERROR]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[ERROR]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[ERROR]         at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[ERROR]         at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[ERROR]         at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:820)
[ERROR]         at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
[ERROR]         at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
[ERROR]         at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2442)
[ERROR]         at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1476)
[ERROR]         at java.base/java.lang.Thread.run(Thread.java:829)
[ERROR]         at org.jboss.threads.JBossThread.run(JBossThread.java:501)
[ERROR] 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
[ERROR]         at io.quarkus.deployment.pkg.steps.NativeImageBuildStep.imageGenerationFailed(NativeImageBuildStep.java:360)
[ERROR]         at io.quarkus.deployment.pkg.steps.NativeImageBuildStep.build(NativeImageBuildStep.java:200)
[ERROR]         ... 11 more
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

错误提示时内存不足,可以尝试设置 quarkus.native.native-image-xmx 参数,但是设置了这个参数还是报这个错。在查阅资料后,在一个 stackoverflow 的回答中看到了这样一句话:

 Pay attention it has to be less than the memory you set in your docker daemon.

他说必须小于您在 docker 守护进程中设置的内存。

然后我查看 docker 的官方文档时发现,在 Mac 和 Windows 中默认的内存是 2GB。

所以只要调高这个值就可以了,推荐是 8GB,各系统设置方法:

Mac:https://docs.docker.com/docker-for-mac/#resources

Windows:https://docs.docker.com/docker-for-windows/#resources

2. 容器构建后的二进制文件无法运行

容器构建 native-image 完成后生成的二进制文件不能执行,提示 zsh: exec format error: ./quarkus-demo-1.0-runner

这是因为我本机是 Mac 系统,但是 Quarkus 提供的构建镜像是 Linux 系统,但是 Graal VM 目前好像并不支持交叉编译,所以在 Linux 系统里构建的二进制文件只能在 Linux 系统里执行。

所以我们可以直接用容器来运行这个构建好的二进制文件。