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:
-
Cerca de 15 minutos
-
Um IDE
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.8
-
Um container runtime instalado (Docker ou Podman)
-
Opcionalmente, o Quarkus CLI se você quiser usá-lo
-
Mandrel ou GraalVM instalado e configurado adequadamente
-
O código da aplicação desenvolvida no Guia de Iniciação.
Suporte à compilação nativa em C
O que significa ter um ambiente de desenvolvimento em C?
|
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.
-
Instale o GraalVM se ainda não o fez. Existem algumas opções para isso:
-
Download do arquivo apropriado de https://github.com/graalvm/mandrel/releases ou https://github.com/graalvm/graalvm-ce-builds/releases e descompacte-o como faria com qualquer outro JDK.
-
Use ferramentas de instalação específicas da plataforma, como sdkman , homebrew ou scoop . Recomendamos a community edition do GraalVM. Por exemplo, instale-o com
sdk install java 21-graalce
.
-
-
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ê.
-
(Opcional) Defina a variável de ambiente
JAVA_HOME
para o diretório de instalação do GraalVM.export JAVA_HOME=${GRAALVM_HOME}
-
(Opcional) Adicione o diretório
bin
GraalVMbin
no PATHexport 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
Use o seguinte comando para excluir recursivamente o atributo estendido
|
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.
If you have generated the application from the previous tutorial, you can find in the pom.xml
the following Maven profile section:
<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>
Você pode fornecer opções personalizadas para o comando Outra possibilidade é incluir a propriedade de configuração You can find more information about how to configure the native image building process in the Configuração do executável nativo section below. |
We use a profile because, you will see very soon, packaging the native executable takes a few minutes. You could just pass -Dquarkus.native.enabled=true as a property on the command line, however it is better to use a profile as this allows native image tests to also be run.
Criar um executável nativo utilizando:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
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 Outra solução é escrever um script para fazer isso para você:
|
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 |
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, 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
...
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 |
Esse procedimento era realizado anteriormente usando a anotação |
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 Como alternativa, se você precisar especificar propriedades específicas ao executar testes no executável nativo criado usando o perfil
|
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 |
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 |
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:
quarkus build --native --no-tests -Dquarkus.native.container-build=true
# The --no-tests flag is required only on Windows and macOS.
./mvnw install -Dnative -DskipTests -Dquarkus.native.container-build=true
./gradlew build -Dquarkus.native.enabled=true -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
Maven
Gradle
Para o podman: CLI
Maven
Gradle
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 |
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 O motivo para isso é que o driver do build local é executado através do |
Fazer o build com o GraalVM ao invés do Mandrel requer que seja passado um parâmetro adicional ao construtor da imagem: CLI
Maven
Gradle
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 See Creating a Linux executable without GraalVM installed for more details. |
-
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.
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 |
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.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"]
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:
-
O primeiro estágio faz o build do executável nativo utilizando o Maven ou o Gradle
-
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 |
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"]
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 wrapper: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.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"]
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 |
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
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 additional flag quarkus.native.sources-only=true
.
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 -Dnative -Dquarkus.native.sources-only=true
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.native.enabled=true -Dquarkus.native.sources-only=true -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:
-
Registar a saída da etapa que executa o comando
./mvnw …
(ou seja, o diretóriotarget/native-image
) como um artefacto de construção, -
Exigir este artefacto no passo que executa o comando
native-image …
, e -
Registar a saída do passo que executa o comando
native-image …
(ou seja, arquivos que correspondem atarget/*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
Quando |
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
no prompt Ou começar
por exemplo,
|
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:
Propriedade de Configuração Fixa no Momento da Compilação - Todas as outras propriedades de configuração podem ser sobrepostas em tempo de execução.
Configuration property |
Tipo |
Padrão |
---|---|---|
Set to enable native-image building using GraalVM. Environment variable: Show more |
boolean |
|
Set to prevent the native-image process from actually building the native image. Environment variable: Show more |
boolean |
|
Comma-separated, additional arguments to pass to the build process. If an argument includes the Environment variable: Show more |
list of string |
|
If the HTTP url handler should be enabled, allowing you to do URL.openConnection() for HTTP URLs Environment variable: Show more |
boolean |
|
If the HTTPS url handler should be enabled, allowing you to do URL.openConnection() for HTTPS URLs Environment variable: Show more |
boolean |
|
The default value for java.awt.headless JVM option. Switching this option affects linking of awt libraries. Environment variable: Show more |
boolean |
|
Defines the file encoding as in Environment variable: Show more |
string |
|
If all character sets should be added to the native executable. Note that some extensions (e.g. the Oracle JDBC driver) also take this setting into account to enable support for all charsets at the extension level. This increases image size. Environment variable: Show more |
boolean |
|
The location of the Graal distribution Environment variable: Show more |
string |
|
The location of the JDK Environment variable: Show more |
|
|
The maximum Java heap to be used during the native image generation Environment variable: 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: Show more |
boolean |
|
If the debug port should be published when building with docker and debug-build-process is true Environment variable: Show more |
boolean |
|
If isolates should be enabled Environment variable: Show more |
boolean |
|
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: Show more |
boolean |
|
If all META-INF/services entries should be automatically registered Environment variable: Show more |
boolean |
|
If the bytecode of all proxies should be dumped for inspection Environment variable: Show more |
boolean |
|
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: 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: Show more |
boolean |
|
Generate instructions for a specific machine type. Defaults to Environment variable: Show more |
string |
|
If this build is done using a remote docker daemon. Environment variable: Show more |
boolean |
|
The docker image to use to do the image build. It can be one of Environment variable: Show more |
string |
|
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: Show more |
|
|
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: Show more |
|
|
Options to pass to the container runtime Environment variable: Show more |
list of string |
|
Enable monitoring various monitoring options. The value should be comma separated.
Environment variable: Show more |
list of |
|
If the reports on call paths and included packages/classes/methods should be generated Environment variable: Show more |
boolean |
|
If exceptions should be reported with a full stack trace Environment variable: Show more |
boolean |
|
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. Note that the use of this flag may result in build time failures due to `ClassNotFoundException`s. Reason most likely being that the Quarkus extension already optimized it away or do not actually need it. In such cases you should explicitly add the corresponding dependency providing the missing classes as a dependency to your project. Environment variable: Show more |
boolean |
|
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: Show more |
boolean |
|
A comma separated list of globs to match resource paths that should be added to the native image. Use slash ( By default, no resources are included. Example: Given that you have
the files Supported glob features Feature Description Note that there are three levels of escaping when passing this option via
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: 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 ( Please refer to By default, no resources are excluded. Example: Given that you have
the resource Environment variable: 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: Show more |
boolean |
|
Generate the report files for GraalVM Dashboard. Environment variable: Show more |
boolean |
|
Include a reasons entries in the generated json configuration files. Environment variable: Show more |
boolean |
|
The compression level in [1, 10]. 10 means best. Higher compression level requires more time to compress the executable. Environment variable: 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: Show more |
list of string |
|
Configuration files generated by the Quarkus build, using native image agent, are informative by default. In other words, the generated configuration files are presented in the build log but are not applied. When this option is set to true, generated configuration files are applied to the native executable building process. Enabling this option should be done with care, because it can make native image configuration and/or behaviour dependant on other non-obvious factors. For example, if the native image agent generated configuration was generated from running JVM unit tests, disabling test(s) can result in a different native image configuration being generated, which in turn can misconfigure the native executable or affect its behaviour in unintended ways. Environment variable: Show more |
boolean |
|
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.