Exemplo de trabalho.
Este não é um tutorial de boot de primavera. É a resposta atualizada a uma pergunta sobre como executar uma compilação Maven em um contêiner do Docker.
Pergunta postada originalmente há 4 anos.
1. Gere um aplicativo
Use o inicializador de primavera para gerar um aplicativo de demonstração
https://start.spring.io/
Extraia o arquivo zip localmente
2. Crie um Dockerfile
#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
COPY src /home/app/src
COPY pom.xml /home/app
RUN mvn -f /home/app/pom.xml clean package
#
# Package stage
#
FROM openjdk:11-jre-slim
COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/local/lib/demo.jar"]
Nota
- Este exemplo usa uma construção de vários estágios . O primeiro estágio é usado para construir o código. O segundo estágio contém apenas o jar construído e um JRE para executá-lo (observe como o jar é copiado entre os estágios).
3. Construa a imagem
docker build -t demo .
4. Execute a imagem
$ docker run --rm -it demo:latest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.3.RELEASE)
2019-02-22 17:18:57.835 INFO 1 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on f4e67677c9a9 with PID 1 (/usr/local/bin/demo.jar started by root in /)
2019-02-22 17:18:57.837 INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2019-02-22 17:18:58.294 INFO 1 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.711 seconds (JVM running for 1.035)
Misc
Leia a documentação do hub Docker sobre como a construção do Maven pode ser otimizada para usar um repositório local para armazenar jars.
Atualização (07/02/2019)
Esta questão já tem 4 anos e, nesse tempo, é justo dizer que a construção de aplicativos usando o Docker passou por mudanças significativas.
Opção 1: compilação em vários estágios
Este novo estilo permite que você crie imagens mais leves que não encapsulam suas ferramentas de construção e código-fonte.
O exemplo aqui novamente usa a imagem base oficial do maven para executar o primeiro estágio da construção usando uma versão desejada do Maven. A segunda parte do arquivo define como o jar construído é montado na imagem de saída final.
FROM maven:3.5-jdk-8 AS build
COPY src /usr/src/app/src
COPY pom.xml /usr/src/app
RUN mvn -f /usr/src/app/pom.xml clean package
FROM gcr.io/distroless/java
COPY --from=build /usr/src/app/target/helloworld-1.0.0-SNAPSHOT.jar /usr/app/helloworld-1.0.0-SNAPSHOT.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/usr/app/helloworld-1.0.0-SNAPSHOT.jar"]
Nota:
- Estou usando a imagem de base sem distração do Google , que se esforça para fornecer tempo de execução suficiente para um aplicativo java.
Opção 2: Jib
Não usei essa abordagem, mas parece digna de investigação, pois permite que você crie imagens sem ter que criar coisas desagradáveis como Dockerfiles :-)
https://github.com/GoogleContainerTools/jib
O projeto possui um plugin Maven que integra o pacote de seu código diretamente em seu fluxo de trabalho Maven.
Resposta original (incluída para ser completa, mas escrita há muito tempo)
Tente usar as novas imagens oficiais, há uma para Maven
https://registry.hub.docker.com/_/maven/
A imagem pode ser usada para executar o Maven em tempo de construção para criar um aplicativo compilado ou, como nos exemplos a seguir, para executar uma construção Maven dentro de um contêiner.
Exemplo 1 - Maven em execução em um contêiner
O comando a seguir executa sua versão Maven dentro de um contêiner:
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
maven:3.2-jdk-7 \
mvn clean install
Notas:
- O interessante dessa abordagem é que todo o software é instalado e executado dentro do contêiner. Só precisa do docker na máquina host.
- Veja Dockerfile para esta versão
Exemplo 2 - Use Nexus para armazenar arquivos em cache
Execute o contêiner Nexus
docker run -d -p 8081:8081 --name nexus sonatype/nexus
Crie um arquivo "settings.xml":
<settings>
<mirrors>
<mirror>
<id>nexus</id>
<mirrorOf>*</mirrorOf>
<url>http://nexus:8081/content/groups/public/</url>
</mirror>
</mirrors>
</settings>
Agora execute o Maven vinculando ao contêiner nexus, para que as dependências sejam armazenadas em cache
docker run -it --rm \
-v "$(pwd)":/opt/maven \
-w /opt/maven \
--link nexus:nexus \
maven:3.2-jdk-7 \
mvn -s settings.xml clean install
Notas:
- Uma vantagem de executar o Nexus em segundo plano é que outros repositórios de terceiros podem ser gerenciados por meio da URL do administrador de forma transparente para as compilações do Maven em execução em contêineres locais.
mavenCentral()
minhas dependências do Gradle pormaven {url "http://nexus:8081..."
e agora estou apenas tendo problemas de resolução.Pode haver muitas maneiras .. Mas eu implementei seguindo duas maneiras
O exemplo dado é de projeto maven.
1. Usando Dockerfile no projeto maven
Use a seguinte estrutura de arquivo:
Demo └── src | ├── main | │ ├── java | │ └── org | │ └── demo | │ └── Application.java | │ | └── test | ├──── Dockerfile ├──── pom.xml
E atualize o Dockerfile como:
FROM java:8 EXPOSE 8080 ADD /target/demo.jar demo.jar ENTRYPOINT ["java","-jar","demo.jar"]
Navegue até a pasta do projeto e digite o seguinte comando que você poderá criar e executar a imagem:
$ mvn clean $ mvn install $ docker build -f Dockerfile -t springdemo . $ docker run -p 8080:8080 -t springdemo
Obtenha um vídeo no Spring Boot com Docker
2. Usando plug-ins Maven
Adicionar determinado plugin maven em
pom.xml
<plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.5</version> <configuration> <imageName>springdocker</imageName> <baseImage>java</baseImage> <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin>
Navegue até a pasta do projeto e digite o seguinte comando, você será capaz de criar e executar a imagem:
$ mvn clean package docker:build $ docker images $ docker run -p 8080:8080 -t <image name>
No primeiro exemplo, estamos criando Dockerfile e fornecendo imagem de base e adicionando jar e, depois de fazer isso, vamos executar o comando docker para construir uma imagem com nome específico e, em seguida, executar essa imagem.
Enquanto no segundo exemplo estamos usando o plugin maven no qual fornecemos
baseImage
e,imageName
portanto, não precisamos criar Dockerfile aqui .. após empacotar o projeto maven, obteremos a imagem do docker e só precisamos executar essa imagem.fonte
Como regra geral, você deve construir um JAR gordo usando Maven (um JAR que contém seu código e todas as dependências).
Em seguida, você pode escrever um Dockerfile que corresponda aos seus requisitos (se você pode construir um JAR grande, você só precisa de um sistema operacional de base, como o CentOS e o JVM).
Isso é o que eu uso para um aplicativo Scala (que é baseado em Java).
FROM centos:centos7 # Prerequisites. RUN yum -y update RUN yum -y install wget tar # Oracle Java 7 WORKDIR /opt RUN wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/7u71-b14/server-jre-7u71-linux-x64.tar.gz RUN tar xzf server-jre-7u71-linux-x64.tar.gz RUN rm -rf server-jre-7u71-linux-x64.tar.gz RUN alternatives --install /usr/bin/java java /opt/jdk1.7.0_71/bin/java 1 # App USER daemon # This copies to local fat jar inside the image ADD /local/path/to/packaged/app/appname.jar /app/appname.jar # What to run when the container starts ENTRYPOINT [ "java", "-jar", "/app/appname.jar" ] # Ports used by the app EXPOSE 5000
Isso cria uma imagem baseada em CentOS com Java7. Quando iniciado, ele executará seu jar de aplicativo.
A melhor maneira de implantá-lo é por meio do Docker Registry, é como um Github para imagens Docker.
Você pode construir uma imagem como esta:
# current dir must contain the Dockerfile docker build -t username/projectname:tagname .
Você pode enviar uma imagem desta forma:
docker push username/projectname # this pushes all tags
Depois que a imagem estiver no Docker Registry, você pode extraí-la de qualquer lugar do mundo e executá-la.
Consulte o Guia do usuário do Docker para obter mais informações.
Algo para ter em mente :
Você também pode colocar seu repositório dentro de uma imagem e construir o jar como parte da execução do contêiner, mas não é uma boa abordagem, já que o código pode mudar e você pode acabar usando uma versão diferente do aplicativo sem aviso prévio.
Construir um frasco de gordura remove esse problema.
fonte
RUN wget -O {project.build.finalname}.jar
Mas eu quero baixar o jar acima da Nexus.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 deDockerfile
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
naDockerfile
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):
Sobre isso,
mvn dependency:resolve-plugins
acorrentadomvn 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 omvn
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.Etapa de execução (imagem JDK ou JRE):
Aqui estão dois exemplos.
a) Uma maneira simples sem cache para dependências do maven baixadas
Dockerfile:
########Maven build stage######## FROM maven:3.6-jdk-11 as maven_build WORKDIR /app #copy pom COPY pom.xml . #resolve maven dependencies RUN mvn clean package -Dmaven.test.skip -Dmaven.main.skip -Dspring-boot.repackage.skip && rm -r target/ #copy source COPY src ./src # build the app (no dependency download here) RUN mvn clean package -Dmaven.test.skip # split the built app into multiple layers to improve layer rebuild RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar ########JRE run stage######## FROM openjdk:11.0-jre WORKDIR /app #copy built app layer by layer 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 #run the app CMD java -cp .:classes:lib/* \ -Djava.security.egd=file:/dev/./urandom \ foo.bar.MySpringBootApplication
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:
# syntax=docker/dockerfile:experimental ########Maven build stage######## FROM maven:3.6-jdk-11 as maven_build WORKDIR /app #copy pom COPY pom.xml . #copy source COPY src ./src # build the app (no dependency download here) RUN --mount=type=cache,target=/root/.m2 mvn clean package -Dmaven.test.skip # split the built app into multiple layers to improve layer rebuild RUN mkdir -p target/docker-packaging && cd target/docker-packaging && jar -xf ../my-app*.jar ########JRE run stage######## FROM openjdk:11.0-jre WORKDIR /app #copy built app layer by layer 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 #run the app CMD java -cp .:classes:lib/* \ -Djava.security.egd=file:/dev/./urandom \ foo.bar.MySpringBootApplication
fonte