The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

构建一个原生可执行文件

本指南包括:

  • 将应用程序编译为原生可执行文件

  • 将原生可执行文件打包到一个容器中

  • 调试原生可执行文件

本指南将 入门指南 中开发的应用程序作为输入。

准备工作

完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • JDK 17+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.9.6

  • A working container runtime (Docker or Podman)

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 可以安装Mandrel或者GraalVM,并正确配置

  • 一个 C语言工作开发环境

  • 入门指南 中开发的应用程序代码。

支持在C语言中进行原生编译

拥有一个C语言工作开发者环境意味着什么?

  • 在Linux上,你将需要GCC,以及glibc和zlib头文件。常见发行版的例子:

    # dnf (rpm-based)
    sudo dnf install gcc glibc-devel zlib-devel libstdc++-static
    # Debian-based distributions:
    sudo apt-get install build-essential libz-dev zlib1g-dev
  • XCode在macOS上提供了必要的依赖性:

    xcode-select --install
  • 在Windows上,你将需要安装 Visual Studio 2017 Visual C++构建工具

Background

构建一个原生可执行文件需要使用GraalVM的发行版。有三个发行版:Oracle GraalVM社区版(CE)、Oracle GraalVM企业版(EE)和Mandrel。Oracle和Mandrel发行版之间的区别如下:

  • Mandrel是Oracle GraalVM CE的一个下游发行版。Mandrel的主要目标是提供一种方法来构建专门为支持Quarkus而设计的原生可执行文件。

  • Mandrel版本的代码库来自于上游的Oracle GraalVM CE代码库,只做了一些小的改动,但也有一些重要的对于Quarkus本地应用程序来说是没有必要的排除项。它们支持与Oracle GraalVM CE相同的构建原生可执行文件的能力,在功能上没有重大变化。值得注意的是,它们不包括对多语言编程的支持。之所以排除这些功能,是为了给大多数Quarkus用户提供更好的支持水平。与Oracle GraalVM CE/EE相比,这些不包括的内容也意味着Mandrel发布的软件包大小大大减小。

  • Mandrel的构建方式与Oracle GraalVM CE略有不同,它使用标准的OpenJDK项目。这意味着,Oracle往OpenJDK增加了一些小增强功能,并用于构建Oracle自己的GraalVM,但Mandrel不能从中获益。这些增强功能被省略了,因为它上游的标准OpenJDK并不管理这些特性,也无法提供保障。这一点在涉及到一致性和安全性时尤其重要。

  • Mandrel被推荐用于构建针对Linux容器化环境的原生可执行文件。这意味着,我们鼓励Mandrel用户使用容器来构建他们的原生可执行文件。如果你要为macOS构建本地可执行文件,你应该考虑使用Oracle GraalVM,因为Mandrel目前不针对这个平台。直接在裸机Linux或Windows上构建原生可执行文件是可能的,详情可参见 Mandrel READMEMandrel发行版

配置GraalVM

This step is only required for generating native executables targeting non-Linux operating systems. For generating native executables targeting Linux, you can optionally skip this section and use a builder image instead.

If you cannot install GraalVM, you can use a multi-stage Docker build to run Maven inside a Docker container that embeds GraalVM. There is an explanation of how to do this at the end of this guide.

GraalVM for JDK 21 is required.

  1. 如果你还没有安装 GraalVM。那么你有几个选择:

  2. 配置运行环境。将 GRAALVM_HOME 环境变量设置为GraalVM的安装目录,例如:

    export GRAALVM_HOME=$HOME/Development/mandrel/

    在macOS上(Mandrel不支持),将该变量指向 Home 子目录:

    export GRAALVM_HOME=$HOME/Development/graalvm/Contents/Home/

    在Windows上,您将需要通过控制面板来设置环境变量。

    通过scoop安装将为你做到这一点。

  3. (可选)将 JAVA_HOME 环境变量设置为GraalVM的安装目录。

    export JAVA_HOME=${GRAALVM_HOME}
  4. (可选)将GraalVM bin 目录添加到路径中

    export PATH=${GRAALVM_HOME}/bin:$PATH
Issues using GraalVM with macOS

GraalVM binaries are not (yet) notarized for macOS as reported in this GraalVM issue. This means that you may see the following error when using native-image:

“native-image” cannot be opened because the developer cannot be verified

其中一种解决方法是,使用以下命令递归删除GraalVM安装目录上的 com.apple.quarantine 扩展属性:

xattr -r -d com.apple.quarantine ${GRAALVM_HOME}/../..

解决方案

我们建议您按照下面几节的说明,一步一步地打包应用。不过您还可以直接进入完成的例子。

克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或者下载 存档

该解决方案位于 getting-started 目录中。

制作一个原生可执行文件

我们应用程序的原生可执行文件将包含应用程序代码、所需的库、Java API和一个缩小版的虚拟机。较小的虚拟机基础提高了应用程序的启动时间和最小的磁盘占用。

创建原生可执行文件

如果你已经从前面的教程中生成了应用程序,你可以在 pom.xml ,找到以下 profile

<profiles>
    <profile>
        <id>native</id>
        <properties>
            <quarkus.package.type>native</quarkus.package.type>
        </properties>
    </profile>
</profiles>

你可以使用 <quarkus.native.additional-build-args> 属性为 native-image 命令提供自定义选项。多个选项可以用逗号隔开。

另一种做法是在你的 application.properties ,填写 quarkus.native.additional-build-args 配置属性。

你可以在下面的 配置原生可执行文件 部分找到关于如何配置原生镜像构建过程的更多信息。

我们使用profile是因为,你很快就会看到,打包原生可执行文件需要 few 分钟。你可以在命令行中把 -Dquarkus.package.type=native 作为一个属性,但是最好是使用一个profile,因为这可以使原生镜像测试也被运行。

使用以下方法创建一个原生可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native
在Windows上打包的问题

The Microsoft Native Tools for Visual Studio must first be initialized before packaging. You can do this by starting the x64 Native Tools Command Prompt that was installed with the Visual Studio Build Tools. At the x64 Native Tools Command Prompt, you can navigate to your project folder and run ./mvnw package -Dnative.

另一个解决方案是写一个脚本来完成:

cmd /c 'call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" && mvn package -Dnative'

除了常规文件外,该构建还产生了 target/getting-started-1.0.0-SNAPSHOT-runner 。你可以用以下方式运行它: ./target/getting-started-1.0.0-SNAPSHOT-runner

Java预览功能

依赖于预览功能的Java代码需要特别注意。为了产生一个原生可执行文件,这意味着需要将 --enable-preview 标志传递给底层的原生镜像调用。你可以这样做,用 -J 作为标志的前缀,并将其作为额外的原生构建参数传递:-Dquarkus.native.additional-build-args=-J—​enable-preview

构建纯静态原生可执行文件

纯静态原生可执行文件支持是试验性的。

On Linux it’s possible to package a native executable that doesn’t depend on any system shared library. There are some system requirements to be fulfilled and additional build arguments to be used along with the native-image invocation, a minimum is -Dquarkus.native.additional-build-args="--static","--libc=musl".

编译纯静态的二进制文件是通过静态链接 musl 而不是 glibc ,如果没有严格的测试,不应该在生产中使用。

测试原生可执行文件

制作一个原生可执行文件可能会导致一些问题,因此,建议针对以原生可执行文件运行的应用程序运行一些测试。具体原因在 测试指南 中有详细解释。

To see the GreetingResourceIT run against the native executable, use ./mvnw verify -Dnative:

$ ./mvnw verify -Dnative
...
Finished generating 'getting-started-1.0.0-SNAPSHOT-runner' in 22.0s.
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildRunner] docker run --env LANG=C --rm --user 1000:1000 -v /home/zakkak/code/quarkus-quickstarts/getting-started/target/getting-started-1.0.0-SNAPSHOT-native-image-source-jar:/project:z --entrypoint /bin/bash quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 -c objcopy --strip-debug getting-started-1.0.0-SNAPSHOT-runner
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 70686ms
[INFO]
[INFO] --- maven-failsafe-plugin:3.0.0-M7:integration-test (default) @ getting-started ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.acme.getting.started.GreetingResourceIT
Executing "/home/zakkak/code/quarkus-quickstarts/getting-started/target/getting-started-1.0.0-SNAPSHOT-runner -Dquarkus.http.port=8081 -Dquarkus.http.ssl-port=8444 -Dtest.url=http://localhost:8081 -Dquarkus.log.file.path=/home/zakkak/code/quarkus-quickstarts/getting-started/target/quarkus.log -Dquarkus.log.file.enable=true -Dquarkus.log.category."io.quarkus".level=INFO"
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-05-05 10:55:52,068 INFO  [io.quarkus] (main) getting-started 1.0.0-SNAPSHOT native (powered by Quarkus 3.0.2.Final) started in 0.009s. Listening on: http://0.0.0.0:8081
2023-05-05 10:55:52,069 INFO  [io.quarkus] (main) Profile prod activated.
2023-05-05 10:55:52,069 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy-reactive, smallrye-context-propagation, vertx]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.99 s - in org.acme.getting.started.GreetingResourceIT
...

By default, Quarkus waits for 60 seconds for the native image to start before automatically failing the native tests. This duration can be changed using the quarkus.test.wait-time system property. For example, to increase the duration to 300 seconds, use: ./mvnw verify -Dnative -Dquarkus.test.wait-time=300.

This procedure was formerly accomplished using the @NativeImageTest annotation. @NativeImageTest was replaced by @QuarkusIntegrationTest which provides a superset of the testing capabilities of @NativeImageTest. More information about @QuarkusIntegrationTest can be found in the Testing Guide.

配置文件

集成测试默认使用 prod 的配置(profile) 构建运行 原生可执行文件。

You can override the profile the executable runs with during the test using the quarkus.test.native-image-profile property. Either by adding it to application.properties or by appending it to the command line: ./mvnw verify -Dnative -Dquarkus.test.native-image-profile=test. Your %test. prefixed properties will be used at the test runtime.

You can override the profile the executable is built with and runs with using the quarkus.profile=test property, e.g. ./mvnw clean verify -Dnative -Dquarkus.profile=test. This might come handy if there are test specific resources to be processed, such as importing test data into the database.

quarkus.native.resources.includes=version.txt
%test.quarkus.native.resources.includes=version.txt,import-dev.sql
%test.quarkus.hibernate-orm.database.generation=drop-and-create
%test.quarkus.hibernate-orm.sql-load-script=import-dev.sql

在前面提到的 application.properties 例子中。在JVM模式测试运行期间和原生模式测试运行期间,Hibernate ORM管理的数据库将填充测试数据。生产可执行文件将只包含 version.txt 资源,没有多余的测试数据。

The executable built with -Dquarkus.profile=test is not suitable for production deployment. It contains your test resources files and settings. Once the testing is done, the executable would have to be built again, using the default, prod profile.

Alternatively, if you need to specify specific properties when running tests against the native executable built using the prod profile, an option is to put those properties in file src/test/resources/application-nativeit.yaml, and refer to it from the failsafe plugin configuration using the QUARKUS_CONFIG_LOCATIONS environment variable. For instance:

<plugin>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>${surefire-plugin.version}</version>
  <executions>
    <execution>
      <goals>
        <goal>integration-test</goal>
        <goal>verify</goal>
      </goals>
      <configuration>
        <systemPropertyVariables>
          <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
          <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
          <maven.home>${maven.home}</maven.home>
        </systemPropertyVariables>
        <environmentVariables>
          <QUARKUS_CONFIG_LOCATIONS>./src/test/resources/application-nativeit.yaml</QUARKUS_CONFIG_LOCATIONS>
        </environmentVariables>
      </configuration>
    </execution>
  </executions>
</plugin>

Java预览功能

Java预览功能

依赖于预览功能的Java代码需要特别注意。为了测试一个原生可执行文件,这意味着需要将 --enable-preview 标志传递给Surefire插件。将 <argLine>--enable-preview</argLine> 添加到其 configuration 片段是一种可行方法。

作为原生可执行文件运行时排除测试

When running tests this way, the only things that actually run natively are your application endpoints, which you can only test via HTTP calls. Your test code does not actually run natively, so if you are testing code that does not call your HTTP endpoints, it’s probably not a good idea to run them as part of native tests.

如果你像我们上面建议的那样,在JVM和原生执行之间共享测试类,你可以用 @DisabledOnNativeImage 注解标记某些测试,被标注的测试只在JVM上运行。

使用 @DisabledOnIntegrationTest 还将禁用所有集成测试实例中的测试,包括在JVM模式、容器镜像和原生镜像中测试应用程序。

测试一个现有的原生可执行文件

It is also possible to re-run the tests against a native executable that has already been built. To do this run ./mvnw test-compile failsafe:integration-test -Dnative. This will discover the existing native image and run the tests against it using failsafe.

如果进程由于某种原因找不到原生镜像,或者你想测试一个已经不在目标目录中的原生镜像,你可以用 -Dnative.image.path= 系统属性指定可执行文件。

在没有安装GraalVM的情况下创建一个Linux可执行文件

在进一步行动之前,请确保有一个工作的容器运行环境(Docker或podman)。如果你在Windows上使用Docker,你应该在Docker Desktop文件共享设置中共享你的项目的驱动器,并重新启动Docker Desktop。

很多时候,人们只需要为他们的Quarkus应用程序创建一个原生Linux可执行文件(例如,为了在容器化环境中运行),并希望避免安装适当的GraalVM版本来完成这项麻烦任务(例如,在CI环境中,通常的做法是尽可能少地安装软件)。

为此,Quarkus提供了一个非常方便的方法,通过利用容器运行时(如Docker或podman)来创建一个原生Linux可执行文件。完成这项任务的最简单方法是执行:

CLI
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
Maven
./mvnw install -Dnative -DskipTests -Dquarkus.native.container-build=true
Gradle
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true

默认情况下,Quarkus会自动检测容器的运行时。如果你想显式指定择容器的运行时,你可以通过以下方式实现:

对于Docker:

CLI
quarkus build --native -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=docker
Maven
./mvnw install -Dnative -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=docker
Gradle
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=docker

对于podman:

CLI
quarkus build --native -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=podman
Maven
./mvnw install -Dnative -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=podman
Gradle
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=podman

These are regular Quarkus config properties, so if you always want to build in a container it is recommended you add these to your application.properties in order to avoid specifying them every time.

如果你在试图使用容器构建创建原生可执行文件时,尽管你的JAR已经成功构建,仍看到应用程序JAR出现以下无效路径错误,您很可能为容器运行时使用了一个远程守护进程。

Error: Invalid Path entry getting-started-1.0.0-SNAPSHOT-runner.jar
Caused by: java.nio.file.NoSuchFileException: /project/getting-started-1.0.0-SNAPSHOT-runner.jar

在这种情况下,使用参数 -Dquarkus.native.remote-container-build=true ,而不是 -Dquarkus.native.container-build=true

原因是通过 -Dquarkus.native.container-build=true 调用的本地构建驱动程序使用卷挂载来使 JAR 在构建容器中可用,但卷挂载对远程守护程序不起作用。远程容器构建驱动程序复制必要的文件,而不是挂载它们。请注意,即使远程驱动程序也能与本地守护进程一起工作,但在本地情况下,本地驱动程序应该是首选,因为挂载通常比复制性能更高。

Building with GraalVM instead of Mandrel requires a custom builder image parameter to be passed additionally:

CLI
quarkus build --native -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=graalvm
Maven
./mvnw install -Dnative -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=graalvm
Gradle
./gradlew build -Dquarkus.package.type=native -Dquarkus.native.container-build=true -Dquarkus.native.builder-image=graalvm

Please note that the above command points to a floating tag. It is highly recommended to use the floating tag, so that your builder image remains up-to-date and secure. If you absolutely must, you may hard-code to a specific tag (see here for available tags), but be aware that you won’t get security updates that way and it’s unsupported.

创建一个容器

使用容器-镜像扩展

到目前为止,从你的Quarkus应用程序中创建一个容器镜像的最简单方法是利用容器镜像扩展。

如果这些扩展之一是存在的,那么为原生可执行文件创建一个容器镜像基本上就是执行一个命令的问题:

./mvnw package -Dnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
  • quarkus.native.container-build=true 允许在不安装GralVM的情况下创建一个Linux可执行文件(只有在你没有安装GralVM或者你的本地操作系统不是Linux的情况下才需要)

If you’re running a remote Docker daemon, you need to replace quarkus.native.container-build=true with quarkus.native.remote-container-build=true.

  • quarkus.container-image.build=true 指示Quarkus使用最终的应用程序工件(在本例中是原生可执行文件)创建一个容器镜像

更多细节见 容器镜像指南

手动使用微基础镜像

你可以使用Quarkus Maven插件生成的JAR在容器中运行该应用程序。然而,在本节中我们将重点讨论使用生成的原生可执行文件创建一个容器镜像。

容器化过程

使用本地GraalVM安装时,原生可执行文件的目标是您的本地操作系统(Linux、macOS、Windows等)。然而,由于容器可能不使用与操作系统相同的 executable 格式,我们将指示Maven构建通过利用容器运行时( 如 this section 所述)来生成可执行文件:

产生的可执行文件将是一个64位的Linux可执行文件,所以如果您的操作系统不支持,它可能不能运行。但由于我们会把它复制到容器中,所以这不是一个问题。自动生成的项目在 src/main/docker 目录中提供了一个 Dockerfile.native-micro ,内容如下:

FROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
什么是Quarkus微镜像?

Quarkus 微镜像是一个小型的容器镜像,为运行你的原生应用程序提供了正确的依赖性集合。它是基于 UBI Micro 的。这个基础镜像已经被定制为在容器中完美运行。

你可以在这里阅读更多关于UBI镜像的内容:

UBI镜像可以不受任何限制地使用。

此页 解释了当你的应用有特殊要求时,如何扩展 quarkus-micro 镜像。

然后,如果你没有删除生成的原生可执行文件,可以用以下方法构建docker镜像:

docker build -f src/main/docker/Dockerfile.native-micro -t quarkus-quickstart/getting-started .

最后,用以下方式运行:

docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started

手工使用最小基础镜像

生成的项目也在 src/main/docker 目录中提供了一个 Dockerfile.native ,内容如下:

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
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"]

UBI最小镜像比上面提到的微镜像要大。它包含更多的实用程序,如 microdnf 软件包管理器。

使用多阶段的Docker构建

上一节向你展示了如何使用Maven或Gradle构建原生可执行文件,但这需要你先创建原生可执行文件。此外,这个原生可执行文件必须是Linux 64位可执行文件。

你可能想直接在一个容器中构建原生可执行文件,而不需要一个包含构建工具的最终容器。这种方法可以通过多阶段的Docker构建来实现:

  1. 第一阶段使用Maven或Gradle构建原生可执行文件

  2. 第二阶段是复制产生了原生可执行文件的最小镜像

Before building a container image from the Dockerfiles shown below, you need to update the default .dockerignore file, as it filters everything except the target directory. In order to build inside a container, you need to copy the src directory. Thus, edit your .dockerignore and remove the * line.

这样的多阶段构建可以通过以下方式实现:

用Maven构建的Docker文件例子:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 AS build
COPY --chown=quarkus:quarkus mvnw /code/mvnw
COPY --chown=quarkus:quarkus .mvn /code/.mvn
COPY --chown=quarkus:quarkus pom.xml /code/
USER quarkus
WORKDIR /code
RUN ./mvnw -B org.apache.maven.plugins:maven-dependency-plugin:3.1.2:go-offline
COPY src /code/src
RUN ./mvnw package -Dnative

## Stage 2 : create the docker final image
FROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /work/
COPY --from=build /code/target/*-runner /work/application

# set up permissions for user `1001`
RUN chmod 775 /work /work/application \
  && chown -R 1001 /work \
  && chmod -R "g+rwX" /work \
  && chown -R 1001:root /work

EXPOSE 8080
USER 1001

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
This multi-stage Docker build copies the Maven wrapper from the host machine. The Maven wrapper (or the Gradle wrapper) is a convenient way to provide a specific version of Maven/Gradle. It avoids having to create a base image with Maven and Gradle. To provision the Maven Wrapper in your project, use: mvn -N org.apache.maven.plugins:maven-wrapper-plugin:3.1.1:wrapper.

将此文件保存在 src/main/docker/Dockerfile.multistage ,因为它不包括在开始快速入门中。

用Gradle构建的Docker文件例子:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 AS build
USER root
RUN microdnf install findutils
COPY --chown=quarkus:quarkus gradlew /code/gradlew
COPY --chown=quarkus:quarkus gradle /code/gradle
COPY --chown=quarkus:quarkus build.gradle /code/
COPY --chown=quarkus:quarkus settings.gradle /code/
COPY --chown=quarkus:quarkus gradle.properties /code/
USER quarkus
WORKDIR /code
COPY src /code/src
RUN ./gradlew build -Dquarkus.package.type=native

## Stage 2 : create the docker final image
FROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /work/
COPY --from=build /code/build/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

如果在项目中使用Gradle,你可以使用这个Docker文件例子。将其保存在 src/main/docker/Dockerfile.multistage

docker build -f src/main/docker/Dockerfile.multistage -t quarkus-quickstart/getting-started .

最后,用以下方式运行:

docker run -i --rm -p 8080:8080 quarkus-quickstart/getting-started

如果你的原生可执行文件需要SSL支持,你可以轻松地在Docker镜像中包含必要的库。

To use GraalVM CE instead of Mandrel, update the FROM clause to: FROM quay.io/quarkus/ubi-quarkus-graalvmce-builder-image:jdk-21 AS build.

使用无发行版基础镜像

无发行版支持是试验性的。

如果你正在寻找小型的容器镜像,https://github.com/GoogleContainerTools/distroless[无发行] 的方法可以减少基础层的大小。 distroless 背后的想法是使用一个单一和最小的基础镜像包含所有的需求,有时甚至是应用程序本身。

Quarkus提供了一个无发行版的基础镜像,可以用于你的 Dockerfile 。你只需要复制你的应用程序,就可以了:

FROM quay.io/quarkus/quarkus-distroless-image:2.0
COPY target/*-runner /application

EXPOSE 8080
USER nonroot

CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Quarkus provides the quay.io/quarkus/quarkus-distroless-image:2.0 image. It contains the required packages to run a native executable and is only 9Mb. Just add your application on top of this image, and you will get a tiny container image.

在没有经过严格测试的情况下,不应该在生产中使用无发行版镜像。

Build a container image from scratch

Scratch镜像的支持是试验性的。

Building fully statically linked binaries enables the usage of a scratch image containing solely the resulting native executable.

以下是基于 scratch 镜像,使用Dockerfile多阶段构建镜像的例子:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/ubi-quarkus-graalvmce-builder-image:jdk-21 AS build
USER root
RUN microdnf install make gcc
COPY --chown=quarkus:quarkus mvnw /code/mvnw
COPY --chown=quarkus:quarkus .mvn /code/.mvn
COPY --chown=quarkus:quarkus pom.xml /code/
RUN mkdir /musl && \
    curl -L -o musl.tar.gz https://more.musl.cc/11.2.1/x86_64-linux-musl/x86_64-linux-musl-native.tgz && \
    tar -xvzf musl.tar.gz -C /musl --strip-components 1 && \
    curl -L -o zlib.tar.gz https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz && \
    mkdir zlib && tar -xvzf zlib.tar.gz -C zlib --strip-components 1 && \
    cd zlib && ./configure --static --prefix=/musl && \
    make && make install && \
    cd .. && rm -rf zlib && rm -f zlib.tar.gz && rm -f musl.tar.gz
ENV PATH="/musl/bin:${PATH}"
USER quarkus
WORKDIR /code
RUN ./mvnw -B org.apache.maven.plugins:maven-dependency-plugin:3.1.2:go-offline
COPY src /code/src
RUN ./mvnw package -Dnative -DskipTests -Dquarkus.native.additional-build-args="--static","--libc=musl"

## Stage 2 : create the final image
FROM scratch
COPY --from=build /code/target/*-runner /application
EXPOSE 8080
ENTRYPOINT [ "/application" ]

在没有经过严格测试的情况下,不应该在生产中使用Scratch镜像。

The versions of musl and zlib may need to be updated to meet the native-image executable requirements (and UPX if you use native image compression).

Compress native images

Quarkus可以使用UPX压缩产生的原生可执行文件。更多细节见 UPX压缩文档

Java和原生镜像分开编译

In certain circumstances, you may want to build the native image in a separate step. For example, in a CI/CD pipeline, you may want to have one step to generate the source that will be used for the native image generation and another step to use these sources to actually build the native executable. For this use case, you can set the quarkus.package.type=native-sources. This will execute the java compilation as if you had started native compilation (-Dnative), but stops before triggering the actual call to GraalVM’s native-image.

$ ./mvnw clean package -Dquarkus.package.type=native-sources

编译完成后,你可以在 target/native-sources 找到构建工件:

$ cd target/native-sources
$ ls
getting-started-1.0.0-SNAPSHOT-runner.jar  graalvm.version  lib  native-image.args

From the output above one can see that, in addition to the produced jar file and the associated lib directory, a text file named native-image.args was created. This file holds all parameters (including the name of the JAR to compile) to pass along to GraalVM’s native-image command. A text file named graalvm.version was also created and holds the GraalVM version that should be used. If you have GraalVM installed and it matches this version, you can start the native compilation by executing:

$ cd target/native-sources
$ native-image $(cat native-image.args)
...
$ ls
native-image.args
getting-started-1.0.0-SNAPSHOT-runner
getting-started-1.0.0-SNAPSHOT-runner.build_artifacts.txt
getting-started-1.0.0-SNAPSHOT-runner.jar

Gradle的过程是类似的。

在一个容器中运行构建过程也是可能的:

$ ./mvnw clean package -Dquarkus.package.type=native-sources -Dquarkus.native.container-build=true

-Dquarkus.native.container-build=true will produce an additional text file named native-builder.image holding the docker image name to be used to build the native image.

cd target/native-sources
docker run \
  -it \
  --user $(id -ur):$(id -gr) \
  --rm \
  -v $(pwd):/work \(1)
  -w /work \(2)
  --entrypoint /bin/sh \
  $(cat native-builder.image) \(3)
  -c "native-image $(cat native-image.args) -J-Xmx4g"(4)
1 将主机的目录 target/native-image 挂载到容器的 /work 。因此,生成的二进制文件也将被写入这个目录。
2 将工作目录切换到 /work ,我们已经在<1>中挂载了这个目录。
3 Use the docker image from the file native-builder.image.
4 以文件 native-image.args 的内容为参数调用 native-image 。我们还提供了一个额外的参数,将进程的最大内存限制在4G字节(这可能取决于正在构建的项目和构建它的机器)。

如果你是在Windows机器上运行,请记住,二进制文件是在Linux docker容器中创建的。因此,二进制文件在Windows主机上是无法执行的。

以下是对CI/CD管道的各个步骤的高度概述:

  1. 将执行 ./mvnw …​ 命令的步骤的输出(即目录 target/native-image )注册为构建工件,

  2. 在执行 native-image …​ 命令的步骤中需要这个工件,并且

  3. 将执行 native-image …​ 命令的步骤的输出(即匹配 target/*runner 的文件)注册为构建工件。

执行步骤 1 的环境只需要安装Java和Maven(或Gradle),而执行步骤 3 的环境只需要安装GralVM(包括 native-image 功能)。

根据CI/CD管道的最终期望输出,生成的二进制文件可能被用来创建一个容器镜像。

调试原生可执行文件

Native executables can be debugged using tools such as gdb. For this to be possible native executables need to be generated with debug symbols.

Debug symbol generation is only supported on Linux. Windows support is still under development, while macOS is not supported.

要生成调试符号,在生成原生可执行文件时添加 -Dquarkus.native.debug.enabled=true 标志。你将在原生可执行文件旁边的 .debug 文件中找到原生可执行文件的调试符号。

The generation of the .debug file depends on objcopy. As a result, when using a local GraalVM installation on common Linux distributions you will need to install the binutils package:

# dnf (rpm-based)
sudo dnf install binutils
# Debian-based distributions
sudo apt-get install binutils

当被嵌入到可执行文件中的 objcopy 调试符号不可用时。

除了调试符号外,设置 -Dquarkus.native.debug.enabled=true 标志会生成一个源文件缓存,用于生成原生可执行文件时解决的任何JDK运行时类、GraalVM类和应用程序类。这个源码缓存对原生调试工具很有用,可以在符号和匹配的源代码之间建立联系。在调试本原生执行文件时,它提供了一种方便的方法,使调试器/IDE仅能获得必要的源文件。

第三方jar依赖的源,包括Quarkus源代码,默认情况下不会被添加到源缓存中。要包括这些,请确保你先调用 mvn dependency:sources 。这一步是必须的,以便拉出这些依赖的源代码,并将其包含在源代码缓存中。

源缓存位于 target/sources 文件夹中。

如果从与 target 不同的目录下运行 gdb ,那么可以通过运行源代码加载:

directory path/to/target

gdb 提示中。

或这样运行 gdb 命令:

gdb -ex 'directory path/to/target' path/to/target/{project.name}-{project.version}-runner

比如:

gdb -ex 'directory ./target' ./target/getting-started-1.0.0-SNAPSHOT-runner

关于调试原生镜像的更详细指南,请参考 原生参考指南

Using Monitoring Options

Monitoring options such as JDK flight recorder, jvmstat, heap dumps, and remote JMX (experimental in Mandrel 23) can be added to the native executable build. Simply supply a comma separated list of the monitoring options you wish to include at build time.

-Dquarkus.native.monitoring=<comma separated list of options>
Monitoring Option 描述 Availability As Of

jfr

Include JDK Flight Recorder support

GraalVM CE 21.3 Mandrel 21.3

jvmstat

Adds jvmstat support

GraalVM 22.3, GraalVM CE 17.0.7 Mandrel 22.3 Mandrel 23.0 (17.0.7)

heapdump

Adds support for generating heap dumps

GraalVM 22.3, GraalVM CE 17.0.7 Mandrel 22.3 Mandrel 23.0 (17.0.7)

jmxclient

Adds support for connections to JMX servers.

GraalVM for JDK 17/20 Mandrel 23.0

jmxserver

Adds support for accepting connections from JMX clients.

GraalVM for JDK 17/20 Mandrel 23.0 (17.0.7)

all

Adds all monitoring options.

GraalVM 22.3, GraalVM CE 17.0.7 Mandrel 22.3 Mandrel 23.0 (17.0.7)

Please see the Quarkus Native Reference Guide for more detailed information on these monitoring options.

配置原生可执行文件

有很多不同的配置选项可以影响原生可执行文件的生成方式。这些都是在 application.properties ,与其他配置属性相同。

这些属性显示如下:

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认

Comma-separated, additional arguments to pass to the build process. If an argument includes the , symbol, it needs to be escaped, e.g. \\,

Environment variable: QUARKUS_NATIVE_ADDITIONAL_BUILD_ARGS

Show more

list of string

If the HTTP url handler should be enabled, allowing you to do URL.openConnection() for HTTP URLs

Environment variable: QUARKUS_NATIVE_ENABLE_HTTP_URL_HANDLER

Show more

boolean

true

If the HTTPS url handler should be enabled, allowing you to do URL.openConnection() for HTTPS URLs

Environment variable: QUARKUS_NATIVE_ENABLE_HTTPS_URL_HANDLER

Show more

boolean

false

The default value for java.awt.headless JVM option. Switching this option affects linking of awt libraries.

Environment variable: QUARKUS_NATIVE_HEADLESS

Show more

boolean

true

Defines the file encoding as in -Dfile.encoding=…​. Native image runtime uses the host’s (i.e. build time) value of file.encoding system property. We intentionally default this to UTF-8 to avoid platform specific defaults to be picked up which can then result in inconsistent behavior in the generated native executable.

Environment variable: QUARKUS_NATIVE_FILE_ENCODING

Show more

String

UTF-8

If all character sets should be added to the native image. This increases image size

Environment variable: QUARKUS_NATIVE_ADD_ALL_CHARSETS

Show more

boolean

false

The location of the Graal distribution

Environment variable: QUARKUS_NATIVE_GRAALVM_HOME

Show more

string

${GRAALVM_HOME:}

The location of the JDK

Environment variable: QUARKUS_NATIVE_JAVA_HOME

Show more

File

${java.home}

The maximum Java heap to be used during the native image generation

Environment variable: QUARKUS_NATIVE_NATIVE_IMAGE_XMX

Show more

string

If the native image build should wait for a debugger to be attached before running. This is an advanced option and is generally only intended for those familiar with GraalVM internals

Environment variable: QUARKUS_NATIVE_DEBUG_BUILD_PROCESS

Show more

boolean

false

If the debug port should be published when building with docker and debug-build-process is true

Environment variable: QUARKUS_NATIVE_PUBLISH_DEBUG_BUILD_PROCESS_PORT

Show more

boolean

true

If isolates should be enabled

Environment variable: QUARKUS_NATIVE_ENABLE_ISOLATES

Show more

boolean

true

If a JVM based 'fallback image' should be created if native image fails. This is not recommended, as this is functionally the same as just running the application in a JVM

Environment variable: QUARKUS_NATIVE_ENABLE_FALLBACK_IMAGES

Show more

boolean

false

If all META-INF/services entries should be automatically registered

Environment variable: QUARKUS_NATIVE_AUTO_SERVICE_LOADER_REGISTRATION

Show more

boolean

false

If the bytecode of all proxies should be dumped for inspection

Environment variable: QUARKUS_NATIVE_DUMP_PROXIES

Show more

boolean

false

If this build should be done using a container runtime. Unless container-runtime is also set, docker will be used by default. If docker is not available or is an alias to podman, podman will be used instead as the default.

Environment variable: QUARKUS_NATIVE_CONTAINER_BUILD

Show more

boolean

Explicit configuration option to generate a native Position Independent Executable (PIE) for Linux. If the system supports PIE generation, the default behaviour is to disable it for performance reasons. However, some systems can only run position-independent executables, so this option enables the generation of such native executables.

Environment variable: QUARKUS_NATIVE_PIE

Show more

boolean

If this build is done using a remote docker daemon.

Environment variable: QUARKUS_NATIVE_REMOTE_CONTAINER_BUILD

Show more

boolean

false

The docker image to use to do the image build. It can be one of graalvm, mandrel, or the full image path, e.g. quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21.

Environment variable: QUARKUS_NATIVE_BUILDER_IMAGE

Show more

string

mandrel

The strategy for pulling the builder image during the build.

Defaults to 'always', which will always pull the most up-to-date image; useful to keep up with fixes when a (floating) tag is updated.

Use 'missing' to only pull if there is no image locally; useful on development environments where building with out-of-date images is acceptable and bandwidth may be limited.

Use 'never' to fail the build if there is no image locally.

Environment variable: QUARKUS_NATIVE_BUILDER_IMAGE_PULL

Show more

always, missing, never

always

The container runtime (e.g. docker) that is used to do an image based build. If this is set then a container build is always done.

Environment variable: QUARKUS_NATIVE_CONTAINER_RUNTIME

Show more

docker, docker-rootless, wsl, wsl-rootless, podman, podman-rootless, unavailable

Options to pass to the container runtime

Environment variable: QUARKUS_NATIVE_CONTAINER_RUNTIME_OPTIONS

Show more

list of string

Enable monitoring various monitoring options. The value should be comma separated.

  • jfr for JDK flight recorder support

  • jvmstat for JVMStat support

  • heapdump for heampdump support

  • jmxclient for JMX client support (experimental)

  • jmxserver for JMX server support (experimental)

  • all for all monitoring features

Environment variable: QUARKUS_NATIVE_MONITORING

Show more

list of MonitoringOption

If the reports on call paths and included packages/classes/methods should be generated

Environment variable: QUARKUS_NATIVE_ENABLE_REPORTS

Show more

boolean

false

If exceptions should be reported with a full stack trace

Environment variable: QUARKUS_NATIVE_REPORT_EXCEPTION_STACK_TRACES

Show more

boolean

true

If errors should be reported at runtime. This is a more relaxed setting, however it is not recommended as it means your application may fail at runtime if an unsupported feature is used by accident.

Environment variable: QUARKUS_NATIVE_REPORT_ERRORS_AT_RUNTIME

Show more

boolean

false

Don’t build a native image if it already exists. This is useful if you have already built an image and you want to use Quarkus to deploy it somewhere. Note that this is not able to detect if the existing image is outdated, if you have modified source or config and want a new image you must not use this flag.

Environment variable: QUARKUS_NATIVE_REUSE_EXISTING

Show more

boolean

false

A comma separated list of globs to match resource paths that should be added to the native image.

Use slash (/) as a path separator on all platforms. Globs must not start with slash.

By default, no resources are included.

Example: Given that you have src/main/resources/ignored.png and src/main/resources/foo/selected.png in your source tree and one of your dependency JARs contains bar/some.txt file, with the following configuration

quarkus.native.resources.includes = foo/**,bar/**/*.txt

the files src/main/resources/foo/selected.png and bar/some.txt will be included in the native image, while src/main/resources/ignored.png will not be included.

Supported glob features Feature Description * Matches a (possibly empty) sequence of characters that does not contain slash (/) ** Matches a (possibly empty) sequence of characters that may contain slash (/) ? Matches one character, but not slash [abc] Matches one character given in the bracket, but not slash [a-z] Matches one character from the range given in the bracket, but not slash [!abc] Matches one character not named in the bracket; does not match slash [a-z] Matches one character outside the range given in the bracket; does not match slash {one,two,three} Matches any of the alternating tokens separated by comma; the tokens may contain wildcards, nested alternations and ranges \ The escape character

Note that there are three levels of escaping when passing this option via application.properties:

  1. application.properties parser

    • MicroProfile Config list converter that splits the comma separated list

    • Glob parser All three levels use backslash (\) as the escaping character. So you need to use an appropriate number of backslashes depending on which level you want to escape.

Note that Quarkus extensions typically include the resources they require by themselves. This option is useful in situations when the built-in functionality is not sufficient.

Environment variable: QUARKUS_NATIVE_RESOURCES_INCLUDES

Show more

list of string

A comma separated list of globs to match resource paths that should not be added to the native image.

Use slash (/) as a path separator on all platforms. Globs must not start with slash.

Please refer to includes for details about the glob syntax.

By default, no resources are excluded.

Example: Given that you have src/main/resources/red.png and src/main/resources/foo/green.png in your source tree and one of your dependency JARs contains bar/blue.png file, with the following configuration

quarkus.native.resources.includes = **/*.png
quarkus.native.resources.excludes = foo/**,**/green.png

the resource red.png will be available in the native image while the resources foo/green.png and bar/blue.png will not be available in the native image.

Environment variable: QUARKUS_NATIVE_RESOURCES_EXCLUDES

Show more

list of string

If debug is enabled and debug symbols are generated. The symbols will be generated in a separate .debug file.

Environment variable: QUARKUS_NATIVE_DEBUG_ENABLED

Show more

boolean

false

Generate the report files for GraalVM Dashboard.

Environment variable: QUARKUS_NATIVE_ENABLE_DASHBOARD_DUMP

Show more

boolean

false

The compression level in [1, 10]. 10 means best.

Higher compression level requires more time to compress the executable.

Environment variable: QUARKUS_NATIVE_COMPRESSION_LEVEL

Show more

int

Allows passing extra arguments to the UPX command line (like --brute). The arguments are comma-separated. The exhaustive list of parameters can be found in https://github.com/upx/upx/blob/devel/doc/upx.pod.

Environment variable: QUARKUS_NATIVE_COMPRESSION_ADDITIONAL_ARGS

Show more

list of string

下一步做什么?

本指南介绍了如何为应用程序创建原生(二进制)可执行文件。它提供了一个具备快速启动时间和消耗更少内存的应用程序。然而,还有更多。

我们建议继续阅读 部署到Kubernetes和OpenShift

Related content