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

Criando um Executável Nativo

Este guia inclui:

  • Compilar a aplicação para um executável nativo

  • Empacotar o executável nativo em um container

  • Debugando um executável nativo

Este guia utiliza como base a aplicação desenvolvida no Guia de Iniciação.

Pré-requisitos

Para concluir este guia, você precisa:

Suporte à compilação nativa em C

O que significa ter um ambiente de desenvolvimento em C?

  • No Linux, é necessário o GCC e os headers glibc e zlib. Exemplos de distribuições comuns:

    # 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
  • O XCode fornece as dependências necessárias no macOS:

    xcode-select --install
  • No Windows, terá de instalar Visual Studio 2017 Visual C++ Build Tools

Background

A criação de um executável nativo requer o uso de uma distribuição do GraalVM. Há três distribuições: Oracle GraalVM Community Edition (CE), Oracle GraalVM Enterprise Edition (EE) e Mandrel. As diferenças entre as distribuições Oracle e Mandrel são as seguintes:

  • O Mandrel é uma distribuição downstream do Oracle GraalVM CE. O principal objetivo do Mandrel é fornecer uma maneira de criar executáveis nativos projetados especificamente para dar suporte ao Quarkus.

  • As versões Mandrel são criadas a partir de uma base de código derivada da base de código do Oracle GraalVM CE, com apenas pequenas alterações, mas algumas exclusões significativas que não são necessárias para os aplicativos nativos do Quarkus. Eles suportam os mesmos recursos para criar executáveis nativos que o Oracle GraalVM CE, sem alterações significativas na funcionalidade. Notavelmente, eles não incluem suporte para programação poliglota. O motivo dessas exclusões é oferecer um melhor nível de suporte para a maioria dos usuários do Quarkus. Essas exclusões também significam que o Mandrel oferece uma redução considerável no tamanho de sua distribuição quando comparado ao Oracle GraalVM CE/EE.

  • O Mandrel foi desenvolvido de forma ligeiramente diferente do Oracle GraalVM CE, usando o projeto OpenJDK padrão. Isso significa que ele não se beneficia de alguns pequenos aprimoramentos que a Oracle adicionou à versão do OpenJDK usada para criar seus próprios downloads do GraalVM. Esses aprimoramentos são omitidos porque o OpenJDK upstream não os gerencia e não pode garantir eles. Isso é particularmente importante quando se trata de conformidade e segurança.

  • O Mandrel é recomendado para a criação de executáveis nativos voltados para ambientes em contêineres do Linux. Isso significa que os usuários do Mandrel são incentivados a usar contêineres para criar seus executáveis nativos. Se estiver criando executáveis nativos para o macOS, considere o uso do Oracle GraalVM, pois o Mandrel não está voltado para essa plataforma no momento. É possível criar executáveis nativos diretamente no Linux ou Windows rodando diretamente no hardware, com detalhes disponíveis no LEIAME do Mandrel e nos lançamentos do Mandrel .

Configurando a 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 é necessário.

  1. Instale o GraalVM se ainda não o fez. Existem algumas opções para isso:

  2. Configurar o runtime environment. Defina a variável de ambiente GRAALVM_HOME para o diretório de instalação do GraalVM, por exemplo:

    export GRAALVM_HOME=$HOME/Development/mandrel/

    No macOS (não suportado pelo Mandrel), aponte a variável para o subdiretório Home:

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

    No Windows, você terá que ir até o Painel de Controle para definir as suas variáveis de ambiente.

    A instalação através do scoop irá fazer tudo por você.

  3. (Opcional) Defina a variável de ambiente JAVA_HOME para o diretório de instalação do GraalVM.

    export JAVA_HOME=${GRAALVM_HOME}
  4. (Opcional) Adicione o diretório bin GraalVM bin no PATH

    export PATH=${GRAALVM_HOME}/bin:$PATH
Problemas ao usar o GraalVM com o macOS

Os binários do GraalVM não estão (ainda) certificados para macOS, conforme relatado nesta issue do GraalVM. Isso significa que pode ocorrer o seguinte erro ao usar native-image:

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

Use o seguinte comando para excluir recursivamente o atributo estendido com.apple.quarantine no diretório de instalação do GraalVM como uma solução alternativa:

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

Solução

Recomendamos que siga as instruções nos passos seguintes e que prepare a aplicação passo a passo. No entanto, pode ir diretamente para o exemplo completo.

Clonar o repositório Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git, ou faça o download do arquivo.

A solução está localizada no diretório getting-started.

Desenvolvendo um executável nativo

O executável nativo da nossa aplicação conterá o código da aplicação, as bibliotecas necessárias, as APIs Java e uma versão reduzida de uma VM. A base menor da VM melhora o tempo de inicialização da aplicação e cria um espaço menor em disco.

Criando um executável nativo

Se gerou a aplicação do tutorial anterior, pode encontrar no pom.xml o seguinte profile:

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

Você pode fornecer opções personalizadas para o comando native-image usando a propriedade <quarkus.native.additional-build-args> . Várias opções podem ser separadas por vírgula.

Outra possibilidade é incluir a propriedade de configuração quarkus.native.additional-build-args no seu application.properties.

You can find more information about how to configure the native image building process in the Configuração do executável nativo section below.

Usamos um perfil porque, como você verá em breve, o empacotamento do executável nativo leva alguns minutos. Você poderia simplesmente passar -Dquarkus.package.type=native como uma propriedade na linha de comando, mas é melhor usar um perfil, pois isso permite que os testes de imagem nativa também sejam executados.

Criar um executável nativo utilizando:

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native
Problemas com o empacotamento no Windows

As Microsoft Native Tools for Visual Studio devem ser inicializadas antes do empacotamento. Você pode fazer isso iniciando o x64 Native Tools Command Prompt que foi instalado com o Visual Studio Build Tools. No x64 Native Tools Command Prompt , você pode navegar até a pasta do projeto e executar ./mvnw package -Dnative .

Outra solução é escrever um script para fazer isso para você:

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

Além dos arquivos normais, a construção também produz target/getting-started-1.0.0-SNAPSHOT-runner . Você pode executá-lo usando: ./target/getting-started-1.0.0-SNAPSHOT-runner .

Java preview features

O código Java que depende de recursos preview requer atenção especial. Para produzir um executável nativo, isso significa que o sinalizador --enable-preview precisa ser passado para a invocação da imagem nativa subjacente. Você pode fazer isso prefixando o sinalizador com -J e passando-o como argumento adicional de construção nativa: -Dquarkus.native.additional-build-args=-J—​enable-preview .

Buildando executáveis nativos totalmente estáticos

O suporte a executáveis nativos totalmente estáticos é experimental.

No Linux, é possível empacotar um executável nativo que não dependa de nenhuma biblioteca compartilhada do sistema. Há alguns requisitos de sistema a serem atendidos e argumentos de construção adicionais a serem usados junto com a invocação native-image ; o mínimo é -Dquarkus.native.additional-build-args="--static","--libc=musl" .

A compilação de binários totalmente estáticos é feita através de um link estático com o musl em vez de glibc e não deve ser usada em produção sem testes rigorosos.

Testando o executável nativo

Produzir um executável nativo pode gerar alguns problemas e, por isso, também é uma boa ideia executar alguns testes na aplicação em execução no arquivo nativo. O raciocínio é explicado no Guia de testes.

Para ver o GreetingResourceIT ser executado no executável nativo, utilize ./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
...

Por padrão, o Quarkus aguarda 60 segundos para que a imagem nativa seja iniciada antes de reprovar automaticamente os testes nativos. Essa duração pode ser alterada usando a propriedade do sistema quarkus.test.wait-time . Por exemplo, para aumentar a duração para 300 segundos, use: ./mvnw verify -Dnative -Dquarkus.test.wait-time=300 .

Esse procedimento era realizado anteriormente usando a anotação @NativeImageTest . @NativeImageTest foi substituído por @QuarkusIntegrationTest , que fornece um superconjunto dos recursos de teste de @NativeImageTest . Mais informações sobre @QuarkusIntegrationTest podem ser encontradas no Guia de Testes .

Profiles

Por padrão, os testes de integração build e run são feitos usando o profile prod.

Você pode substituir o perfil com o qual o executável roda durante o teste usando a propriedade quarkus.test.native-image-profile . Adicione-a a application.properties ou acrescente-a à linha de comando: ./mvnw verify -Dnative -Dquarkus.test.native-image-profile=test . Suas propriedades prefixadas com %test. serão usadas no tempo de execução do teste.

Você pode substituir o perfil com o qual o executável é construído e rodado usando a propriedade quarkus.profile=test , por exemplo, ./mvnw clean verify -Dnative -Dquarkus.profile=test . Isso pode ser útil se houver recursos específicos de teste a serem processados, como a importação de dados de teste para o banco de dados.

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

Com o exemplo mencionado acima em seu application.properties , o banco de dados gerenciado do Hibernate ORM será preenchido com dados de teste durante a execução do teste no modo JVM e durante a execução do teste no modo nativo. O executável de produção conterá apenas o recurso version.txt , sem dados de teste supérfluos.

O executável criado com -Dquarkus.profile=test não é adequado para a implantação em produção. Ele contém os arquivos e as configurações dos recursos de teste. Quando o teste for concluído, o executável deverá ser criado novamente, usando o perfil padrão prod .

Como alternativa, se você precisar especificar propriedades específicas ao executar testes no executável nativo criado usando o perfil prod , uma opção é colocar essas propriedades no arquivo src/test/resources/application-nativeit.yaml e fazer referência a ele na configuração do plug-in failsafe usando a variável de ambiente QUARKUS_CONFIG_LOCATIONS . Por exemplo:

<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

O código Java que depende de recursos preview requer atenção especial. Para testar um executável nativo, isso significa que o indicador --enable-preview precisa ser passado para o plug-in Surefire. Adicionar <argLine>--enable-preview</argLine> à sua seção configuration é uma maneira de fazer isso.

Excluindo testes ao executar como um executável nativo

Ao executar testes dessa forma, as únicas coisas que realmente são executadas nativamente são os endpoints da aplicação, que só podem ser testados por meio de chamadas HTTP. Seu código de teste não é realmente executado nativamente, portanto, se você estiver testando um código que não chama seus endpoints HTTP, provavelmente não é uma boa ideia executá-los como parte dos testes nativos.

Se você compartilhar sua classe de teste entre a JVM e as execuções nativas, como aconselhamos acima, poderá marcar determinados testes com a anotação @DisabledOnIntegrationTest para ignorá-los ao testar uma imagem nativa.

Usar @DisabledOnIntegrationTest também desativará o teste em todas as instâncias de teste de integração, incluindo o teste da aplicação no modo JVM, em uma imagem de contêiner e em uma imagem nativa.

Testar um executável nativo existente

Também é possível executar novamente os testes em um executável nativo que já tenha sido criado. Para fazer isso, execute ./mvnw test-compile failsafe:integration-test -Dnative . Isso descobrirá a imagem nativa existente e executará os testes com base nela usando o failsafe.

Se o processo não conseguir encontrar a imagem nativa por algum motivo ou se você quiser testar uma imagem nativa que não esteja mais no diretório de destino, poderá especificar o executável com a propriedade de sistema -Dnative.image.path= .

Criando um executável Linux sem o GraalVM instalado

Antes de prosseguir, certifique-se de ter um ambiente de tempo de execução de contêiner em funcionamento (Docker, podman). Se usar o Docker no Windows, compartilhe a unidade do seu projeto nas configurações de compartilhamento de arquivos do Docker Desktop e reinicie o Docker Desktop.

Muitas vezes, é necessário apenas criar um executável Linux nativo para o aplicativo Quarkus (por exemplo, para ser executado em um ambiente de contêiner) e evitar o trabalho de instalar a versão adequada do GraalVM para realizar essa tarefa (por exemplo, em ambientes de CI, é prática comum instalar o mínimo de software possível).

Para isso, o Quarkus oferece uma maneira muito conveniente de criar um executável nativo do Linux, aproveitando um tempo de execução de contêiner, como o Docker ou o podman. A maneira mais fácil de realizar essa tarefa é executar:

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

Por padrão, o Quarkus detecta automaticamente o tempo de execução do contêiner. Se quiser selecionar explicitamente o tempo de execução do contêiner, você pode fazer isso com:

Para o 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

Para o 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

Essas são propriedades regulares de configuração do Quarkus, portanto, se quiser sempre construir em um contêiner, é recomendável adicioná-las ao seu application.properties para evitar especificá-las sempre.

Se vir o seguinte erro de caminho inválido para o JAR da sua aplicação ao tentar criar um executável nativo utilizando uma build em contêiner, mesmo o JAR ter sido compilado com sucesso, é muito provável que esteja utilizando um daemon remoto para o tempo de execução do contêiner.

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

Neste caso, utilize o parâmetro -Dquarkus.native.remote-container-build=true ao invés de -Dquarkus.native.container-build=true.

O motivo para isso é que o driver do build local é executado através do -Dquarkus.native.container-build=true que utiliza irá montar volumes para tornar o JAR disponível na build do contêiner, mas os volumes montados não funcionam com daemons remotos. O driver que faz a build do contêiner remoto copia apenas os arquivos necessários para montar ele. Observe que, embora o driver remoto também funcione com daemons locais, o driver local deve ser preferido no caso local porque a montagem é geralmente mais eficiente do que a cópia.

Fazer o build com o GraalVM ao invés do Mandrel requer que seja passado um parâmetro adicional ao construtor da imagem:

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

Observe que o comando acima aponta para um indicador flutuante. É altamente recomendável usar o indicador flutuante para que a imagem do construtor permaneça atualizada e segura. Se for absolutamente necessário, você pode codificar para um indicador específico (veja aqui os indicadores disponíveis), mas esteja ciente de que não receberá atualizações de segurança dessa forma e não há suporte para isso.

Criando um contêiner

Utilizar as extensões dos container-image

A maneira mais fácil de criar uma container-image a partir da sua aplicação Quarkus é utilizar uma das extensões de imagem de contêiner.

Se uma dessas extensões estiverem presente, então a criação de uma imagem de contêiner para o executável nativo é executar um único comando:

./mvnw package -Dnative -Dquarkus.native.container-build=true -Dquarkus.container-image.build=true
  • quarkus.native.container-build=true permite criar um executável Linux sem que o GraalVM esteja instalado (e só é necessário se não tiver o GraalVM instalado localmente ou se o seu sistema operacional local não for Linux)

Se estiver executando um daemon Docker remoto, você terá que substituir quarkus.native.container-build=true por quarkus.native.remote-container-build=true.

  • quarkus.container-image.build=true instrui o Quarkus para criar um container-image utilizando o artefato final da aplicação (que, neste caso, é o executável nativo)

Consulte o Guia do container-image para obter mais informações.

Utilizando a micro imagem base manualmente

É possível executar a aplicação em um contêiner usando o JAR produzido pelo Quarkus Maven Plugin. No entanto, nesta seção, vamos nos concentrar na criação de uma imagem de contêiner usando o executável nativo produzido.

Processo de conteinerização

When using a local GraalVM installation, the native executable targets your local operating system (Linux, macOS, Windows etc). However, as a container may not use the same executable format as the one produced by your operating system, we will instruct the Maven build to produce an executable by leveraging a container runtime (as described in this section):

O executável produzido será um executável Linux de 64 bits, portanto, dependendo do seu sistema operacional, ele pode não ser mais executável. No entanto, isso não é um problema, pois vamos copiá-lo para um contêiner. A geração do projeto forneceu um Dockerfile.native-micro no diretório src/main/docker com o seguinte conteúdo:

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?

O Quarkus Micro Image é uma pequena imagem de contêiner que fornece o conjunto certo de dependências para executar sua aplicação nativa. Ela é baseada no UBI Micro. Essa imagem base foi adaptada para funcionar perfeitamente em contêineres.

Você pode ler mais sobre as imagens UBI em:

As imagens UBI podem ser utilizadas sem qualquer limitações.

Esta página explica como extender a imagem quarkus-micro quando a sua aplicação tem requisitos específicos.

Depois, se não tiver deletado o executável nativo criado, pode construir a imagem docker com:

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

E, finalmente, execute-o com:

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

Utilizando manualmente a imagem base mínima

A geração do projeto também forneceu um Dockerfile.native na pasta src/main/docker com o seguinte conteúdo:

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"]

A imagem mínima da UBI é maior do que a micro mencionada acima. Ela contém mais utilitários, como o gerenciador de pacotes microdnf .

Utilizar uma compilação do Docker multi-stage

A seção anterior mostrou como criar um executável nativo usando o Maven ou o Gradle, mas isso exige que o executável nativo tenha sido criado primeiro. Além disso, esse executável nativo deve ser um executável Linux de 64 bits.

Talvez você queira criar o executável nativo diretamente em um contêiner sem ter um contêiner final que contenha as ferramentas de criação. Essa abordagem é possível com uma compilação do Docker em vários estágios:

  1. O primeiro estágio faz o build do executável nativo utilizando o Maven ou o Gradle

  2. O segundo estágio é uma imagem mínima que copia o executável nativo produzido

Antes de construir uma imagem de contêiner a partir dos Dockerfiles mostrados abaixo, é necessário atualizar o arquivo .dockerignore, uma vez que este filtra tudo exceto o diretório target. Para fazer o build dentro de um contêiner, é necessário copiar o diretório src. Portanto, edite seu .dockerignore e remova a linha *.

Esta construção em vários estágios pode ser realizada da seguinte forma:

Exemplo de Dockerfile para fazer o build com o Maven:

## 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"]
Essa compilação do Docker em vários estágios copia o wrapper do Maven do computador hospedeiro. O wrapper do Maven (ou o wrapper do Gradle) é uma maneira conveniente de fornecer uma versão específica do Maven/Gradle. Ele evita a necessidade de criar uma imagem de base com o Maven e o Gradle. Para provisionar o Maven Wrapper em seu projeto, use: mvn -N org.apache.maven.plugins:maven-wrapper-plugin:3.1.1:wrapper .

Salve esse arquivo em src/main/docker/Dockerfile.multistage, uma vez que não está incluído no startup rápido.

Exemplo de Dockerfile para build com o Gradle:

## 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"]

Se estiver utilizando o Gradle no seu projeto, pode utilizar este exemplo de Dockerfile. Salve-o em src/main/docker/Dockerfile.multistage.

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

E, finalmente, execute-o com:

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

Se precisar de suporte SSL no seu executável nativo, pode facilmente incluir as bibliotecas necessárias na sua imagem Docker.

Consulte o nosso guia Usando SSL com executáveis nativos para obter mais informações.

Para usar o GraalVM CE em vez do Mandrel, atualize o trecho FROM para: FROM quay.io/quarkus/ubi-quarkus-graalvmce-builder-image:jdk-21 AS build.

Usando uma imagem base sem distribuição

O suporte de imagens sem distribuição é experimental.

Se estiver procurando por imagens de contêineres pequenas, a abordagem sem distribuição reduz o tamanho da camada base. A ideia por trás do sem distribuição é o uso de uma imagem base única e mínima que contenha todos os requisitos e, às vezes, até a própria aplicação.

O Quarkus fornece uma imagem base sem distribuição que você pode usar no seu Dockerfile . Você só precisa copiar sua aplicação e pronto:

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"]

O Quarkus fornece a imagem quay.io/quarkus/quarkus-distroless-image:2.0 . Ela contém os pacotes necessários para rodar um executável nativo e tem apenas 9 Mb. Basta adicionar sua aplicação sobre essa imagem e você obterá uma imagem de contêiner minúscula.

As imagens sem distribuição não devem ser utilizadas em produção sem um teste rigoroso.

Criar uma imagem scratch

O suporte de imagens scratch é experimental.

O build de binários totalmente linkados estaticamente permite a utilização de uma imagem de rascunho contendo apenas o executável nativo resultante.

Exemplo de Dockerfile de vários estágios para criar uma imagem a partir de scratch:

## 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" ]

As imagens Scratch não devem ser utilizadas em produção sem um teste rigoroso.

As versões de musl e zlib podem precisar serem atualizadas para atender os requisitos do executável de imagem nativa (e UPX se utilizar compressão de imagem nativa).

Compactar imagens nativas

O Quarkus pode compactar o executável nativo produzido usando UPX. Mais detalhes na documentação de Compressão UPX .

Separando a compilação de imagens nativas e Java

Em determinadas circunstâncias, você pode querer criar a imagem nativa em uma etapa separada. Por exemplo, em um pipeline de CI/CD, você pode querer ter uma etapa para gerar o código-fonte que será usado para a geração da imagem nativa e outra etapa para usar esses códigos-fonte para realmente criar o executável nativo. Para esse caso de uso, é possível definir quarkus.package.type=native-sources . Isso executará a compilação java como se você tivesse iniciado a compilação nativa ( -Dnative ), mas parará antes de acionar a chamada real para native-image do GraalVM .

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

Após finalizar a compilação, você encontrará o artefato compilado em target/native-sources:

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

Na saída acima, é possível ver que, além do arquivo jar produzido e do diretório lib associado, foi criado um arquivo de texto chamado native-image.args . Esse arquivo contém todos os parâmetros (inclusive o nome do JAR a ser compilado) a serem transmitidos ao comando native-image do GraalVM. Um arquivo de texto chamado graalvm.version também foi criado e contém a versão do GraalVM que deve ser usada. Se o GraalVM estiver instalado e corresponder a essa versão, você poderá iniciar a compilação nativa executando:

$ 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

O processo para o Gradle é análogo.

Também é possível executar o processo de compilação num contêiner:

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

-Dquarkus.native.container-build=true produzirá um arquivo de texto adicional chamado native-builder.image com o nome da imagem docker a ser utilizada para construir a imagem nativa.

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 Monte o diretório raiz target/native-image no diretório do contêiner /work. Assim, o binário gerado também será escrito neste diretório.
2 Mude o diretório de trabalho para /work, que foi montado em <1>.
3 Utilize a imagem docker do arquivo native-builder.image.
4 Chamar native-image com o conteúdo do arquivo native-image.args como argumentos. Também fornecemos um argumento adicional para limitar a memória máxima do processo a 4 Gigabytes (isto pode variar dependendo do projeto que está sendo construído e da máquina que está sendo usada).

Se você estiver executando em uma máquina Windows, lembre-se de que o binário foi criado em um contêiner docker do Linux. Portanto, o binário não será executável na máquina Windows do hospedeiro.

Uma visão geral de alto nível do que seriam as várias etapas de um pipeline CI/CD é a seguinte:

  1. Registar a saída da etapa que executa o comando ./mvnw …​ (ou seja, o diretório target/native-image) como um artefacto de construção,

  2. Exigir este artefacto no passo que executa o comando native-image …​, e

  3. Registar a saída do passo que executa o comando native-image …​ (ou seja, arquivos que correspondem a target/*runner) como artefacto de build.

O ambiente que executa o passo 1 apenas necessita do Java e Maven (ou Gradle) instalados, enquanto o ambiente que executa o passo 3 apenas necessita de uma instalação GraalVM (incluindo a funcionalidade native-image ).

Dependendo do resultado final desejado do pipeline CI/CD, o binário gerado pode então ser utilizado para criar uma imagem de contêiner.

Debugando um executável nativo

Os executáveis nativos podem ser depurados por meio de ferramentas como gdb . Para que isso seja possível, os executáveis nativos precisam ser gerados com símbolos de depuração.

A geração de símbolos de depuração só é compatível com o Linux. O suporte ao Windows ainda está em desenvolvimento, enquanto o macOS não é compatível.

Para gerar símbolos de depuração, adicione o sinalizador -Dquarkus.native.debug.enabled=true ao gerar o executável nativo. Você encontrará os símbolos de depuração para o executável nativo em um arquivo .debug ao lado do executável nativo.

A geração do arquivo .debug depende do objcopy . Como resultado, ao usar uma instalação local do GraalVM em distribuições comuns do Linux, você precisará instalar o pacote binutils :

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

Quando objcopy não está disponível, os símbolos de debug são incorporados no executável.

Além dos símbolos de depuração, a configuração do sinalizador -Dquarkus.native.debug.enabled=true gera um cache de arquivos de origem para todas as classes de tempo de execução do JDK, classes do GraalVM e classes de aplicação resolvidas durante a geração do executável nativo. Esse cache de fontes é útil para ferramentas de depuração nativas, para estabelecer o vínculo entre os símbolos e o código-fonte correspondente. Ele fornece uma maneira conveniente de disponibilizar apenas os códigos-fonte necessários para o depurador/IDE ao depurar um executável nativo.

Fontes para dependências jar de terceiros, incluindo o código-fonte do Quarkus, não são adicionadas ao cache de fontes por padrão. Para incluí-los, certifique-se de invocar mvn dependency:sources primeiro. Essa etapa é necessária para extrair os códigos-fonte dessas dependências e incluí-los no cache de código-fonte.

A cache de fonte está localizado na pasta target/sources.

Se executar gdb a partir de um diretório diferente do target, então as fontes podem ser carregadas ao executar:

directory path/to/target

no prompt gdb.

Ou começar gdb com:

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

por exemplo,

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

Para obter um guia mais detalhado sobre o debug de imagens nativas, consulte o Guia de Referência Nativo.

Utilizar as opções de monitoração

As opções de monitoramento, como JDK flight recorder, jvmstat, heap dumps e JMX remoto (experimental no Mandrel 23), podem ser adicionadas à construção do executável nativo. Basta fornecer uma lista separada por vírgulas das opções de monitoramento que você deseja incluir no momento da construção.

-Dquarkus.native.monitoring=<comma separated list of options>
Opção de monitoração Descrição Disponibilidade a partir de

jfr

Incluir suporte para o JDK Flight Recorder

GraalVM CE 21.3 Mandril 21.3

jvmstat

Adiciona suporte a jvmstat

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

heapdump

Adiciona suporte para gerar heap dumps

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

jmxclient

Adiciona suporte para conexões a servidores JMX.

GraalVM para JDK 17/20 Mandrel 23.0

jmxserver

Adiciona suporte para aceitar conexões de clientes JMX.

GraalVM para JDK 17/20 Mandrel 23.0 (17.0.7)

todos

Adiciona todas as opções de monitoração.

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

Consulte o Guia de Referência do Quarkus Native para obter informações mais detalhadas sobre estas opções de monitoração.

Configuração do executável nativo

Há várias opções de configuração diferentes que podem afetar a forma como o executável nativo é gerado. Elas são fornecidas em application.properties da mesma forma que qualquer outra propriedade de configuração.

As propriedades são apresentadas abaixo:

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

Configuration property

Tipo

Padrão

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

O que vem a seguir?

Este guia abordou a criação de um executável nativo (binário) para a sua aplicação. Ele fornece uma aplicação que exibe um tempo de inicialização rápido e consome menos memória. No entanto, há muito mais.

Recomendamos continuar a jornada com a implantação no Kubernetes e no OpenShift.

Conteúdo Relacionado