Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Dockerize Java Web App: Maven + Tomcat

Tenho visto frequentemente diversos tutoriais usando docker parar subir serviços prontos, porém vejo poucas instruções relacionadas em como utilizá-lo dentro do fluxo de desenvolvimento. Principalmente em linguagens compiladas como exemplo o java que exige um passo adicional de compilação que ao contrário das linguagens dinâmicas não exigem esse passo.

Como eu poderia organizar o meu workflow de desenvolvimento utilizando o Docker em todo ciclo de vida? Tanto na utilização de um container para compilar meu código, rodando o "mvn clean install", quanto no deploy no servidor de aplicação (aqui no caso o tomcat). Eu deveria montar 2 containers um para cada passo? Como ficaria as dependências entre esses dois containers já que o segundo responsável pelo deploy só poderá entrar em ação após o container de compilação ter feito seu trabalho e gerado o artefato (.war)?

Um próximo passo (e importante) seria criar uma integração da execução desses passos com uma IDE (Netbeans ou Eclipse por exemplo) para que este fluxo de desenvolvimento com Docker ficasse transparente durante o processo.

Resumindo:

  • Como ficaria o fluxo?
  • Como ficariam os Dockerfile?
  • Como organizar a ordem de execução? (Utilizando shellscript, talvez ou caberiam em um docker-compose?)
5 respostas

Acho que Fernando, o nosso maior especialista em docker vai poder te ajudar.. não com todos os detalhes da resposta, mas sim com alguns direcionamentos. Vamos ver se até segunda ele entra em ação :).

Obrigado por se prontificar, Alberto. Ainda continuo a minha saga.. rsrs

Até agora o que consegui de "melhor" (e até então ainda não havia conseguido) foi criar uma estratégia de dois passos como eu havia imaginado anteriormente. Primeiramente utilizei um container para compilar e outro para subir o tomcat.

Passo 1

Utilizei a imagen maven:3.2-jdk-7 para compilar (mvn clean install) usando o comando abaixo:

$ docker run -it --rm \
-v ~/.m2:/root/.m2 -v "$PWD":/usr/src/mymaven \
-w /usr/src/mymaven maven:3.2-jdk-7 mvn clean install

Passo 2

2.1 - Crie um Dockerfile contendo:


FROM tomcat:8.0

ADD . /code
WORKDIR /code
COPY target/*.war $CATALINA_HOME/webapps/
EXPOSE 8888:8080

2.2 - Gerei a imagem a partir do meu Dockerfile

$ docker build -t java/apachetomcat:1.0 .

2.3 - Criei o meu container a partir da imagem

$ docker run -it --rm -p 8888:8080 java/apachetomcat:1.0

[Considerações]

Não sei se é preciosismo da minha parte, mas para mim ainda parecem muitos passos. Talvez agora, eu poderia "encapsular" esses passos em um shellscript mas IHMO me parece estranho necessitar de uma solução extra (aqui no caso o shellscript) para compor uma solução que deveria cobrir casos como este. Eu até tentei unir os dois em um docker-compose, mas acabei esbarrando com o problema de dependências entre os containers (mencionado anteriormente). Em um cenário que o container responsável pela compilação não ficava pronto antes do container responsável pelo tomcat. Talvez eu tenha falhado em algum ponto ou talvez exista uma forma mais elegante para cumprir esta tarefa. Então, gostaria muito de ainda ouvir a opinião do Fernando em relação ao problema e solução propostos para podermos amadurecer a ideia.

Começo a desconfiar que o JAVA não fala muito bem baleiês... rsrsrs Mas enquanto não encontro nada melhor que isso "continuo a nadar" :P

Fico no aguardo ;)

Fala aí Rogério tudo bem?

Primeiramente desculpe a demora... =/

Bom sua solução me parece bacana, o que eu acho que poderia melhorar é o seguinte:

  1. Criar um Dockerfile para o maven dessa forma podemos customizar algumas coisas após ele fazer o build da aplicação. (Vide nº 4)

  2. Expor um volume no container do tomcat para o diretório $CATALINA_HOME/webapps/

  3. Fazer com que o container do maven acesse o diretório exposto pelo container do tomcat (para isso podemos usar o parâmetro --volume-from)

  4. Após fazer o container do maven compilar e gerar nosso artefato podemos copiar o .war para o volume que foi exposto pelo container do tomcat (para isso podemos usar um script no Entrypoint/Cmd do Dockerfile da imagem do maven ou usar um plugin de copia do maven porém nessa segunda abordagem fica a cargo do dev colocar o plugin no pom e por isso sugiro a primeira)

Minha ideia com tudo isso é deixar o container do tomcat sempre de pé e o container do maven será usado quando necessário. Melhor ainda seria se o container do maven baixa-se o código do repositório antes de rodar o mvn clean package. Dessa forma podemos deixar tudo stateless.

Não vejo a necessidade de ter um docker-compose, pois o container do maven será usado somente para compilar e empacotar. Maaassss nada impede de você criar o docker-compose para subir os dois containers quando o container do maven terminar de compila ele irá morrer e só o container do maven ficará up.

Não sei se era exatamente isso que você queria. Se não estiver de acordo com o que você quer, me mande aqui e eu tento te ajudar.

Espero ter ajudado e bons estudos.

solução!

Desculpem a todos pela demora.

Fernando, achei as dicas excelentes, porém demorei um tempinho para conseguir digerir e ajustar tudo da forma que eu queria. Acho que alguns pontos ainda podem ser melhorados, mas agora está mais próximo de como eu gostaria.

A ideia é que eu possa usar esta solução em qualquer projeto que utilize "Maven + Tomcat" colocando os arquivos na raiz do projeto seguindo os passos abaixo.

No exemplo utilizei o projeto do Curso de JPA 2

1 - docker-compose.yml

version: '2'
services:
  db:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=projeto_jpa
    ports:
      - "3306:3306"
  maven:
      build:
        context: dockerfiles
        dockerfile: Dockerfile-maven
      volumes:
        - ~/.m2:/root/.m2
        - .:/usr/src/mymaven
      volumes_from:
        - tomcat
  tomcat:
    build:
      context: dockerfiles
      dockerfile: Dockerfile-apache
    ports:
      - "8888:8080"

2 - Um diretório com alguns arquivos de configuração e os Dockerfile

dockerfiles/
├── Dockerfile-apache
├── Dockerfile-maven
├── Dockerfile-mysql
├── entrypointscript.sh
└── tomcat-users.xml

2.1 Dockerfile-apache

FROM tomcat:8.0

ADD . /code
WORKDIR /code
COPY tomcat-users.xml  $CATALINA_HOME/conf/
VOLUME $CATALINA_HOME/webapps

2.2 Dockerfile-maven

FROM openjdk:7-jdk

ARG MAVEN_VERSION=3.3.9
ARG USER_HOME_DIR="/root"

RUN mkdir -p /usr/share/maven /usr/share/maven/ref \
  && curl -fsSL http://apache.osuosl.org/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz \
    | tar -xzC /usr/share/maven --strip-components=1 \
  && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn

ENV MAVEN_HOME /usr/share/maven
ENV MAVEN_CONFIG "$USER_HOME_DIR/.m2"

COPY entrypointscript.sh /usr/local/bin/mvn-entrypoint.sh
COPY settings-docker.xml /usr/share/maven/ref/

VOLUME "$USER_HOME_DIR/.m2"


COPY . /usr/src/mymaven
WORKDIR /usr/src/mymaven
ENTRYPOINT ["/usr/local/bin/mvn-entrypoint.sh"]

2.3 Dockerfile-mysql

FROM mysql:5.7

ENV MYSQL_ROOT_PASSWORD=root
ENV MYSQL_DATABASE=projeto_jpa

expose 3306:3306

2.4 entrypoint.sh - Um ponto de atenção para este aqui. Ele é responsável por compilar e copiar todos artefatos para o tomcat. No meu caso tenho um projeto que tem vários módulos, então o resultado final terá mais de um .war, por este motivo o "find" buscando em todo diretório e sub-diretórios.

#!/bin/bash
mvn clean install -e -f  /usr/src/mymaven  &&
find . -name "*.war" -exec cp '{}' /usr/local/tomcat/webapps \;

2.5 tomcat-users.xml

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
  <role rolename="manager"/>
  <role rolename="admin"/>
  <user username="tomcat" password="tomcat" roles="admin,manager, manager-gui"/>
</tomcat-users>

Comandos:

1 - Para subir os containers:

$ docker-compose build --no-cache && docker-compose up -d

2 - Para recompilar basta executar apenas o serviço específico do maven. Ele fará o papel de recompilar o código e copiar para o volume compartilhado entre os dois containers como foi indicado na dica do Fernando.

$ docker-compose run maven

Quem tiver interesse em conferir subi uma cópia no bitbucket. https://bitbucket.org/rogeriofonseca/projeto-jpa-2

Eu estava com umas dúvidas semelhantes. Muito obrigado por compartilharem essas informações!

Mas ainda tenho a dúvida: no fim das contas, qual a grande vantagem disso tudo em relação ao meu atual ambiente sem docker (exemplo: tomcat, postgres, jdk e demais coisas instaladas normalmente)?

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software