Building a Native Executable

Prerequisites

include::./_includes/prerequisites.adoc[]* working C development environment* Getting Started Guide 中开发的应用程序代码。

Supporting native compilation in C

拥有一个可工作的 C 开发环境意味着什么?

  • 在 Linux 上,您需要 GCC 及 glibc 和 zlib 头文件。适用于常见发行版的示例:[source, bash]

# 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 上提供必需的依赖项:[source, bash]

xcode-select --install

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 项目。这意味着它无法从甲骨文添加到用于构建其 GraalVM 下载的 OpenJDK 版本中的一些小增强中受益。省略这些增强是因为上游 OpenJDK 未管理它们,并且无法保证。在谈到一致性和安全性时,这一点尤其重要。

  • 建议使用 Mandrel 来构建针对 Linux 容器化环境的原生可执行文件。这意味着鼓励 Mandrel 用户使用容器构建其原生可执行文件。如果你正在为 macOS 构建原生可执行文件,你应该考虑使用 Oracle GraalVM,因为 Mandrel 目前不支持此平台。可以在 bare metal Linux 或 Windows 上直接构建原生可执行文件,详细信息可以在 Mandrel READMEMandrel releases 中找到。

Configuring GraalVM

仅在针对非 Linux 操作系统生成原生可执行文件时才需要此步骤。对于针对 Linux 生成原生可执行文件,你可以选择跳过此部分,而 use a builder image

如果你无法安装 GraalVM,则可以使用多阶段 Docker 构建在嵌入 GraalVM 的 Docker 容器内运行 Maven。the end of this guide 中对此进行了说明。

需要 GraalVM ${graal-community.version-for-documentation}。

  1. 如果你尚未安装,请安装 GraalVM。以下是你的一些选择:

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

export GRAALVM_HOME=$HOME/Development/mandrel/

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

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

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

通过 Scoop 安装将自动完成上述操作。

  1. (可选)将 JAVA_HOME 环境变量设置为 GraalVM 安装目录。[source, bash]

export JAVA_HOME=${GRAALVM_HOME}
  1. (可选)将 GraalVM bin 目录添加到路径[source, bash]

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

正如 GraalVM issue 中所述,GraalVM 二进制文件尚未对 macOS 进行公证。这意味着在使用 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}/../..

Solution

我们建议您按照下一章节中的说明逐步打包应用程序。不过,您可以直接转至完成的示例。

克隆 Git 存储库: git clone $${quickstarts-base-url}.git,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。

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

Producing a native executable

我们应用程序的本机可执行文件将包含应用程序代码、必需的库、Java API 以及精简版的 VM。较小的 VM 基础可缩短应用程序的启动时间,并生成最小的磁盘占用。

Creating a native executable

如果您已从前一个教程生成了应用程序,您可以在 pom.xml 中找到以下 Maven 配置文件部分:

<profiles>
    <profile>
        <id>native</id>
        <activation>
            <property>
                <name>native</name>
            </property>
        </activation>
        <properties>
            <skipITs>false</skipITs>
            <quarkus.native.enabled>true</quarkus.native.enabled>
        </properties>
    </profile>
</profiles>

你可以使用 <quarkus.native.additional-build-args> 属性为 native-image 命令提供自定义选项。多个选项可以用逗号分隔。 另一个可能性是在你的 application.properties 中包含 quarkus.native.additional-build-args 配置属性。 你可以在下面的 Configuring the Native Executable 部分中找到有关如何配置原生镜像构建过程的更多信息。

我们使用一个配置文件是因为,你很快就会看到,打包原生可执行文件需要 few 分钟。你可以直接将 -Dquarkus.native.enabled=true 作为命令行上的一个属性传递,但是最好使用一个配置文件,因为这允许原生镜像测试也可以运行。

使用以下命令创建本机可执行文件:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true
Issues with packaging on Windows

Microsoft Visual Studio 的本机工具必须在打包之前初始化。你可以通过启动随 Visual Studio 构建工具安装的 x64 Native Tools Command Prompt 来做到这一点。在 x64 Native Tools Command Prompt 中,你可以导航到你的项目文件夹并运行 ./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 preview features

依赖于预览功能的 Java 代码需要特别注意。要生成原生可执行文件,这意味着 --enable-preview 标志需要传递给基础原生镜像调用。你可以通过在标志前加上 -J 并将其作为附加原生构建参数 -Dquarkus.native.additional-build-args=-J—​enable-preview 传递来做到这一点。

Build fully static native executables

完全静态的原生可执行文件支持是实验性的。

在 Linux 上,可以打包一个不依赖于任何系统共享库的原生可执行文件。在 native-image 调用中,需要满足 some system requirements 并使用其他构建参数,最小参数为 -Dquarkus.native.additional-build-args="--static","--libc=musl"

编译完全静态的二进制文件是通过静态链接 musl 而不是 glibc 来完成的,并且不应在没有严格测试的情况下在生产中使用。

Testing the native executable

生成原生可执行文件可能会导致一些问题,因此针对在原生文件中运行的应用程序运行一些测试也是一个好主意。原因在 Testing Guide 中进行了说明。

若要查看针对原生可执行文件运行的 GreetingResourceIT,请使用 ./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:{mandrel-flavor} -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, rest, 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
...

默认情况下,Quarkus 会等待 60 秒,让原生镜像在自动使原生测试失败之前启动。此持续时间可通过 quarkus.test.wait-time 系统属性更改。例如,要将持续时间增加到 300 秒,请使用:./mvnw verify -Dnative -Dquarkus.test.wait-time=300.

此过程以前使用 @NativeImageTest 注释完成。 @NativeImageTest 已被 @QuarkusIntegrationTest 取而代之,它提供了 @NativeImageTest 的测试功能的超集。有关 @QuarkusIntegrationTest 的更多信息可在 Testing Guide 中找到。

Profiles

默认情况下,集成测试既使用 build 又使用 run 原生可执行文件,并使用 prod 配置文件。

你可以在测试过程中使用 quarkus.test.native-image-profile 属性来覆盖可执行文件的配置文件 runs。通过将其添加到 application.properties 中或附加到命令行中:./mvnw verify -Dnative -Dquarkus.test.native-image-profile=test。你的 %test. 前缀属性将在测试运行时使用。

你可以在 builtruns 中覆盖可执行文件的配置文件,并使用 quarkus.profile=test 属性,例如 ./mvnw clean verify -Dnative -Dquarkus.profile=test。如果有一些特定的需要处理的测试资源,例如将测试数据导入数据库,则这可能派得上用场。

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 中的前述示例,你的 Hibernate ORM 管理的数据库将在 JVM 模式测试运行期间和原生模式测试运行期间用测试数据填充。生产可执行文件将仅包含 version.txt 资源,不会有冗余的测试数据。

使用 -Dquarkus.profile=test 构建的可执行文件不适合进行生产部署。它包含你的测试资源文件和设置。完成测试后,必须使用默认的 prod 配置文件重新构建可执行文件。 或者,如果你需要在针对使用 prod 配置文件构建的原生可执行文件运行测试时指定特定的属性,一种选择是将这些属性放在文件 src/test/resources/application-nativeit.yaml 中,并使用 QUARKUS_CONFIG_LOCATIONS 环境变量从 failsafe 插件配置中引用它。例如:

<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 preview features

Java preview features

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

Excluding tests when running as a native executable

以这种方式运行测试时,唯一真正以原生方式运行的是你的应用程序端点,你只能通过 HTTP 调用对其进行测试。你的测试代码实际上并没有以原生方式运行,因此,如果你正在测试不调用你的 HTTP 端点的代码,则将其作为原生测试的一部分运行可能不是一个好主意。

如果你像我们上面建议的那样在 JVM 和原生执行之间共享你的测试类,你可以使用 @DisabledOnIntegrationTest 注释标记某些测试,以便在针对原生镜像进行测试时跳过这些测试。

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

Testing an existing native executable

也可以针对已经构建的原生可执行文件重新运行测试。要执行此操作,请运行 ./mvnw test-compile failsafe:integration-test -Dnative。这将发现现有的原生镜像,并使用 failsafe 针对它运行测试。

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

Creating a Linux executable without GraalVM installed

在继续之前,请确保拥有一个可正常工作的容器运行时(Docker、podman)环境。如果你在 Windows 上使用 Docker,则应在 Docker Desktop 文件共享设置中共享你的项目的驱动器,并重启 Docker Desktop。

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

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

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.native.enabled=true -Dquarkus.native.container-build=true

默认情况下,Quarkus 会自动检测容器运行时。如果你想显式选择容器运行时,你可以使用: 对于 Docker: include::./_includes/devtools/build-native-container-parameters.adoc[]:!build-additional-parameters: 对于 podman: include::./_includes/devtools/build-native-container-parameters.adoc[]:!build-additional-parameters: 这些是常规的 Quarkus 配置属性,因此如果你总是想在容器中构建,建议将它们添加到 application.properties 中,以避免每次都指定它们。

如果您在尝试使用容器构建创建本机可执行文件时看到应用程序 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,但卷装载无法与远程后台程序配合使用。远程容器构建驱动程序复制必要的 JAR 文件,而不是装载这些文件。请注意,尽管远程驱动程序还可以与本地后台程序配合使用,但在本地情况下,应优先使用本地驱动程序,因为装载通常比复制性能更佳。

如果要使用 GraalVM 而非 Mandrel 进行构建,则需要另外传递自定义构建器映像参数: include::./_includes/devtools/build-native-container-parameters.adoc[]:!build-additional-parameters: 请注意,上述命令指向浮动标签。强烈建议使用浮动标签,以便使构建器映像保持最新并安全。如果您必须,可以硬编码为特定标签(参见 here 以获取可用标签),但请注意,您将不会通过这种方式获得安全更新,并且它不受支持。

Creating a container

Using the container-image extensions

到目前为止,创建 Quarkus 应用程序容器映像的简单方法是利用容器映像扩展之一。

如果其中一个扩展程序存在,则为原生可执行文件创建一个容器映像本质上是执行以下命令的问题:

./mvnw package -Dnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
  • quarkus.native.container-build=true 允许在没有安装 GraalVM 的情况下创建 Linux 可执行文件(仅当您尚未本地安装 GraalVM 或您的本地操作系统不是 Linux 时才需要)

如果您正在运行远程 Docker 守护程序,您需要使用 quarkus.native.remote-container-build=true 替换 quarkus.native.container-build=true 。 有关更多详细信息,请参阅 Creating a Linux executable without GraalVM installed

  • quarkus.container-image.build=true 指示 Quarkus 使用最终应用程序工件(在本例中为本地可执行文件)创建容器映像。

更多详情,请参阅 Container Image guide

Manually using the micro base image

你可以使用 Quarkus Maven 插件生成的 JAR,在容器中运行应用程序。然而,本节重点介绍如何使用生成的本地可执行文件创建容器镜像。

Containerization Process

使用本地 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 Micro Image?

Quarkus 微镜像是一个小容器镜像,它提供了运行本地应用程序所需的正确依赖关系集。它基于 UBI Micro。这个基础镜像已经定制成非常适合在容器中工作。 你可以在以下内容中了解有关 UBI 镜像的更多信息:

UBI 镜像可以用在没有任何限制的情况下。 This page 中解释了当你应用程序有特定要求时,如何扩展 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

Manually using the minimal base image

项目生成了 src/main/docker 目录中的 Dockerfile.native,其内容如下:

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10
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 包管理器。

Using a multi-stage Docker build

上一节向你展示了怎样使用 Maven 或 Gradle 构建本地可执行文件,但是它要求你首先创建本地可执行文件。此外,这个本地可执行文件必须是 Linux 64 位可执行文件。

你可能希望直接在容器中构建本地可执行文件,而不用拥有一个包含构建工具的最终容器。这种方法可以使用多阶段 Docker 构建来实现:

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

  2. 第二阶段是最小化的图像,用于复制已生成的本机可执行文件。

在从下列 Dockerfiles 构建容器图像之前,需要更新默认的 .dockerignore 文件,因为该文件将过滤掉除 target 目录之外的所有内容。为了在容器内构建,需要复制 src 目录。因此,编辑 .dockerignore 并删除 * 行。

多阶段构建可通过以下步骤实现:

用于构建 Maven 的示例 Dockerfile:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:{mandrel-flavor} 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"]

多阶段 Docker 构建会从主机复制 Maven wrapper。Maven wrapper(或 Gradle wrapper)是提供特定 Maven/Gradle 版本的便捷方式。它避免了使用 Maven 和 Gradle 创建基础镜像的情况。在项目中供应 Maven Wrapper,使用: mvn wrapper:wrapper

将此文件保存在 src/main/docker/Dockerfile.multistage 中,因为它未包含在快速上手指南中。

用于构建 Gradle 的示例 Dockerfile:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:{mandrel-flavor} 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.native.enabled=true

## 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,可以使用此示例 Dockerfile。将它保存在 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 镜像中包含必要的库。 请参阅 our Using SSL With Native Executables guide 以了解更多信息。

若要使用 GraalVM CE 代替 Mandrel,将 FROM 子句更新为: FROM quay.io/quarkus/ubi-quarkus-graalvmce-builder-image:${graal-community.image-tag-for-documentation} AS build

Using a Distroless base image

Distroless 镜像支持为实验性功能。

如果您正在寻找小型容器镜像, distroless 方法会减小基本层的尺寸。 distroless 的理念是使用单一且最小的基础镜像,包含所有需求甚至有时包含应用程序本身。

Quarkus 提供了可以在 Dockerfile 中使用的 distroless 基础镜像。您只需复制您的应用程序即可完成:

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 提供 quay.io/quarkus/quarkus-distroless-image:2.0 镜像。它包含运行本机可执行文件所需的软件包,并且仅 9Mb。只需将您的应用程序添加到此镜像上即可获得一个微小的容器镜像。

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

Build a container image from scratch

Scratch 镜像支持为实验性功能。

构建完全静态链接二进制文件支持使用包含结果原生可执行文件的 scratch image

用于从 `scratch`构建映像的示例多阶段 Dockerfile:

## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/ubi-quarkus-graalvmce-builder-image:{graalvm-flavor} 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" ]

在没有严格测试的情况下,不应在生产中使用擦除镜像。

musl 和 zlib 的版本可能需要更新以满足原生镜像可执行文件要求(如果您使用原生镜像压缩,则还需要更新 UPX)。

Compress native images

Quarkus 可以使用 UPX 压缩生成的原生可执行文件。有关 UPX Compression documentation 的更多详细信息。

Separating Java and native image compilation

在某些情况下,您可能需要单独执行一个步骤来构建原生镜像。例如,在 CI/CD 管道中,您可能需要执行一个步骤来生成原生镜像生成的源,同时执行另一个步骤来使用这些源实际构建原生可执行程序。对于此用例,您可以设置附加标记 quarkus.native.sources-only=true。这将执行 Java 编译,就像您启动原生编译一样(-Dnative),但是会在触发对 GraalVM 的 native-image 的实际调用之前停止。

$ ./mvnw clean package -Dnative -Dquarkus.native.sources-only=true

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

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

从上面的输出中可以看到,除了生成的 jar 文件和相关的 lib 目录之外,还创建了一个名为 native-image.args 的文本文件。此文件保存了所有参数(包括要编译的 JAR 的名称)以传递给 GraalVM 的 native-image 命令。还创建了一个名为 graalvm.version 的文本文件,其中保存了应使用的 GraalVM 版本。如果您安装了与该版本匹配的 GraalVM,则可以通过执行以下操作来启动原生编译:

$ 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.native.enabled=true -Dquarkus.native.sources-only=true -Dquarkus.native.container-build=true

-Dquarkus.native.container-build=true 将生成一个名为 native-builder.image 的其他文本文件,其中包含用于构建原生镜像的 Docker 镜像名称。

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 使用来自文件 native-builder.image 的 Docker 镜像。
4 使用文件 native-image.args 的内容作为参数调用 native-image。我们还提供了一个附加参数,以将进程的最大内存限制为 4 GB(这可能因正在构建的项目和构建它的机器而异)。

如果您在 Windows 机器上运行,请记住,该二进制文件是在 Linux Docker 容器中创建的。因此,二进制文件在主机 Windows 机器上不可执行。

CI/CD 管道的各个步骤的一般概述如下:

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

  2. 在执行 native-image &#8230;&#8203; 命令的步骤中需要此工件,并

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

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

根据 CI/CD 管道期望的最终所需输出,所生成的二进制文件可用于创建容器映像。

Debugging native executable

可以使用诸如 gdb 的工具对本地可执行文件进行调试。为此,需要使用调试符号来生成本地可执行文件。

仅在 Linux 上支持调试符号生成。Windows 支持仍在开发中,而 macOS 不支持。

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

.debug 文件的生成依赖于 objcopy。因此,当在常见 Linux 发行版上使用本地 GraalVM 安装时,您需要安装 binutils 包:

# 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,则可以通过运行在 gdb 提示符中加载源:

directory path/to/target

或使用以下命令启动 gdb: 例如,

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

如果从与 target 不同的目录运行 gdb,则可以通过运行在 gdb 提示符中加载源:

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

有关调试原生映像的更详细指南,请参阅 Native Reference Guide

Using Monitoring Options

可以将监控选项(如 JDK 飞行记录器、jvmstat、堆转储和远程 JMX(Mandrel 23 中的实验性功能))添加到原生可执行文件版本中。只需在构建时提供您希望包含的监控选项的逗号分隔列表。

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

jfr

包含 JDK Flight Recorder 支持

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

添加了生成堆转储的支持

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

jmxclient

添加了对连接到 JMX 服务器的支持。

面向 JDK 17/20 的 GraalVM Mandrel 23.0

jmxserver

添加了接受来自 JMX 客户端连接的支持。

面向 JDK 17/20 的 GraalVM 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)

请参阅 Quarkus Native 参考指南,了解有关这些监控选项的更详细信息。

Configuring the Native Executable

有很多不同的配置选项可能会影响本机可执行文件的生成方式。这些配置选项已在 application.properties 中提供,与任何其他配置属性相同。

属性如下所示:

Unresolved include directive in modules/ROOT/pages/building-native-image.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-core_quarkus.native.adoc[]

What’s next?

本指南介绍了为应用程序创建一个本机(二进制)可执行文件。它提供了一个展示了快速启动时间和消耗较少内存的应用程序。不过,还要做更多的事情。

我们建议继续使用 deployment to Kubernetes and OpenShift 进行后续操作。