Aqui está minha contribuição.
Não tentarei listar todas as ferramentas / bibliotecas / plug-ins que existem para aproveitar as vantagens do Docker com Maven. Algumas respostas já o fizeram.
em vez de, vou me concentrar na tipologia de aplicativos e no modo Dockerfile.
Dockerfile
é realmente um conceito simples e importante do Docker (todas as imagens conhecidas / públicas dependem disso) e acho que tentar evitar o entendimento e o uso de Dockerfile
s não é necessariamente a melhor maneira de entrar no mundo do Docker.
Dockerizing um aplicativo depende do próprio aplicativo e do objetivo
1) Para aplicativos que desejamos executar para executá-los no servidor Java instalado / autônomo (Tomcat, JBoss, etc ...)
O caminho é mais difícil e esse não é o alvo ideal porque adiciona complexidade (temos que gerenciar / manter o servidor) e é menos escalonável e menos rápido do que os servidores incorporados em termos de construção / implantação / remoção.
Mas para aplicativos legados, isso pode ser considerado uma primeira etapa.
Geralmente, a ideia aqui é definir uma imagem Docker para o servidor e definir uma imagem por aplicativo a ser implantado.
As imagens docker para os aplicativos produzem o WAR / EAR esperado, mas não são executadas como contêiner e a imagem para o aplicativo do servidor implementa os componentes produzidos por essas imagens como aplicativos implementados.
Para aplicativos enormes (milhões de linhas de códigos) com muitos materiais legados, e tão difíceis de migrar para uma solução embarcada de inicialização completa, essa é realmente uma boa melhoria.
Não vou detalhar mais essa abordagem, já que é para casos de uso menores do Docker, mas eu queria expor a ideia geral dessa abordagem porque acho que, para os desenvolvedores que enfrentam esses casos complexos, é ótimo saber que algumas portas estão abertas para integrar Docker.
2) Para aplicativos que incorporam / inicializam o próprio servidor (Spring Boot com servidor incorporado: Tomcat, Netty, Jetty ...)
Esse é o alvo ideal com o Docker . Especifiquei Spring Boot porque essa é uma estrutura muito boa para fazer isso e também tem um alto nível de manutenção, mas, em teoria, poderíamos usar qualquer outra forma de Java para fazer isso.
Geralmente, a ideia aqui é definir uma imagem Docker por aplicativo a ser implantado.
As imagens docker para os aplicativos produzem um JAR ou um conjunto de arquivos JAR / classes / configuração e estes iniciam uma JVM com o aplicativo (comando java) quando criamos e iniciamos um contêiner a partir dessas imagens.
Para novos aplicativos ou aplicativos não muito complexos para migrar, essa forma deve ser preferida em relação aos servidores autônomos, porque essa é a forma padrão e a forma mais eficiente de usar contêineres.
Vou detalhar essa abordagem.
Dockerizing a maven application
1) Sem Spring Boot
A ideia é criar um jar gordo com o Maven (o plugin maven assembly e o plugin maven sombra ajudam para isso) que contém as classes compiladas do aplicativo e as dependências maven necessárias.
Então podemos identificar dois casos:
se o aplicativo é desktop ou autônomo (que não precisa ser implantado em um servidor): poderíamos especificar como CMD/ENTRYPOINT
na Dockerfile
execução java do aplicativo:java -cp .:/fooPath/* -jar myJar
se o aplicativo for um aplicativo servidor, por exemplo Tomcat, a ideia é a mesma: obter um grande jar do aplicativo e executar um JVM no CMD/ENTRYPOINT
. Mas aqui com uma diferença importante: precisamos incluir algumas lógicas e bibliotecas específicas ( org.apache.tomcat.embed
bibliotecas e algumas outras) que iniciam o servidor embutido quando o aplicativo principal é iniciado.
Temos um guia completo no site do heroku .
Para o primeiro caso (aplicativo autônomo), essa é uma maneira direta e eficiente de usar o Docker.
Para o segundo caso (aplicativo de servidor), isso funciona, mas não é direto, pode estar sujeito a erros e não é um modelo muito extensível porque você não coloca seu aplicativo no quadro de uma estrutura madura como Spring Boot que faz muitos dessas coisas para você e também fornece um alto nível de extensão.
Mas isso tem uma vantagem: você tem um alto nível de liberdade porque usa diretamente a API Tomcat incorporada.
2) Com bota de mola
Por fim, aqui vamos nós.
Isso é simples, eficiente e muito bem documentado.
Na verdade, existem várias abordagens para fazer um aplicativo Maven / Spring Boot rodar no Docker.
Expor todos eles seria longo e talvez enfadonho.
A melhor escolha depende de sua necessidade.
Mas de qualquer maneira, a estratégia de construção em termos de camadas docker parece a mesma.
Queremos usar uma construção de vários estágios: uma contando com Maven para a resolução de dependência e para construção e outra contando com JDK ou JRE para iniciar o aplicativo.
Estágio de construção (imagem Maven):
- pom cópia para a imagem
- dependências e downloads de plug - ins.
Sobre isso, mvn dependency:resolve-plugins
acorrentado mvn dependency:resolve
pode fazer o trabalho, mas nem sempre.
Por quê ? Como esses plug-ins e a package
execução para empacotar o fat jar podem depender de diferentes artefatos / plug-ins e até mesmo para um mesmo artefato / plug-in, eles ainda podem obter uma versão diferente. Portanto, uma abordagem mais segura, embora potencialmente mais lenta, é resolver dependências executando exatamente o mvn
comando usado para empacotar o aplicativo (que puxará exatamente as dependências de que você precisa), mas ignorando a compilação de origem e excluindo a pasta de destino para tornar o processamento mais rápido e evitar qualquer detecção de mudança de camada indesejável para essa etapa.
- cópia do código fonte para a imagem
- empacote o aplicativo
Etapa de execução (imagem JDK ou JRE):
- copie a jarra do estágio anterior
Aqui estão dois exemplos.
a) Uma maneira simples sem cache para dependências do maven baixadas
Dockerfile:
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
COPY pom.xml .
RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/
COPY src ./src
RUN mvn clean package -Dmaven.test.skip
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
FROM openjdk:11.0-jre
WORKDIR /app
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
CMD java -cp .:classes:lib
Desvantagem dessa solução? Quaisquer alterações no pom.xml significam recriação de toda a camada que baixa e armazena as dependências do maven. Isso geralmente não é aceitável para aplicativos com muitas dependências (e Spring Boot puxa muitas dependências), em geral se você não usar um gerenciador de repositório maven durante a construção da imagem.
b) Uma maneira mais eficiente com o cache para dependências do maven baixadas
A abordagem aqui é a mesma, mas os downloads de dependências do maven são armazenados em cache no cache do docker builder.
A operação do cache depende do buildkit (API experimental do docker).
Para habilitar o buildkit, a variável env DOCKER_BUILDKIT = 1 deve ser definida (você pode fazer isso onde quiser: .bashrc, linha de comando, arquivo docker daemon json ...).
Dockerfile:
FROM maven:3.6-jdk-11 as maven_build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip
RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar
FROM openjdk:11.0-jre
WORKDIR /app
ARG DOCKER_PACKAGING_DIR=/app/target/docker-packaging
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/lib /app/lib
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/BOOT-INF/classes /app/classes
COPY --from=maven_build ${DOCKER_PACKAGING_DIR}/META-INF /app/META-INF
CMD java -cp .:classes:lib
mavenCentral()
minhas dependências do Gradle pormaven {url "http://nexus:8081..."
e agora estou apenas tendo problemas de resolução.