Conteinerizar um aplicativo Java
Nesta unidade, você contêineriza um aplicativo Java.
Conforme mencionado anteriormente, os contêineres são executados diretamente na parte superior do sistema operacional host, kernel e hardware como processos comuns do sistema. Os contêineres exigem menos recursos do sistema, resultando em um volume menor, menos sobrecarga e tempos de inicialização de aplicativos mais rápidos. Esses benefícios são ótimos casos de uso para dimensionamento sob demanda.
Há contêineres do Windows e contêineres do Linux. Neste módulo, você usa o runtime do Docker amplamente usado para criar uma imagem de contêiner do Linux. Em seguida, você implanta a imagem de contêiner do Linux no sistema operacional host do computador local. Por fim, você implanta a imagem de contêiner do Linux no Serviço de Kubernetes do Azure.
Visão geral do Docker
O runtime do Docker é usado para criar, fazer o pull, executar e fazer o push de imagens de contêiner, conforme mostrado no diagrama a seguir.
A tabela a seguir descreve cada comando do Docker:
| Comando do Docker | Descrição |
|---|---|
docker build |
Cria uma imagem de contêiner que consiste nas instruções ou camadas necessárias para o Docker criar um contêiner em execução a partir de uma imagem. O resultado desse comando é uma imagem. |
docker pull |
Os contêineres são inicializados a partir de imagens, que são extraídas de registros como o Registro de Contêiner do Azure. Esse registro é de onde o Serviço de Kubernetes do Azure é extraído. O resultado desse comando é um pull de rede de uma imagem que ocorre no Azure. Opcionalmente, você pode baixar imagens localmente. Essa opção é comum ao criar imagens que exigem dependências ou camadas que seu aplicativo pode precisar, como um servidor de aplicativos. |
docker run |
Uma instância em execução de uma imagem é um contêiner e esse comando executa todas as camadas necessárias para executar e interagir com o aplicativo de contêiner em execução. O resultado desse comando é um processo de aplicativo em execução no sistema operacional host. |
docker push |
O Registro de Contêiner do Azure armazena as imagens para que elas estejam prontamente disponíveis e fechem a rede para implantações e escala do Azure. |
Clonar o aplicativo Java
Primeiro, clone o repositório Flight Booking System for Airline Reservations e navegue até a pasta do projeto de aplicativo web da companhia aérea.
Observação
Se a criação do Serviço de Kubernetes do Azure for concluída na aba do CLI, use essa aba. Se ela ainda estiver em execução, abra uma nova aba e navegue até o local onde você prefere clonar o Sistema de Reservas de Voos.
Execute os comandos a seguir:
git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines
Opcionalmente, se você tiver Java e Maven instalados, poderá executar o comando a seguir no console do terminal para ter uma noção da experiência de criação do aplicativo sem o Docker. Se você não tiver Java e Maven instalados, poderá prosseguir com segurança para a próxima seção, Construir um arquivo do Docker. Nessa seção, você usa o Docker para baixar Java e Maven e executar as compilações por você.
mvn clean package
Observação
Usamos o mvn clean package comando para ilustrar os desafios operacionais de não usar builds de vários estágios do Docker, que abordaremos em seguida. Novamente, essa etapa é opcional. De qualquer forma, você pode continuar com segurança sem executar o comando Maven.
Se o processo foi bem-sucedido, o Maven criou com sucesso o artefato de arquivo de aplicativo da Web Sistema de Reserva de Voo para Reservas Aéreas AirlinesReservationSample-0.0.1-SNAPSHOT.war, conforme mostrado na saída a seguir:
[INFO] Building war: $PROJECT_PATH/containerize-and-deploy-Java-app-to-Azure/Project/Airlines/target/AirlinesReservationSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.776 s
[INFO] Finished at: 2024-11-15T09:33:26+09:00
[INFO] ------------------------------------------------------------------------
Imagine que você é um desenvolvedor java e acabou de criar AirlinesReservationSample-0.0.1-SNAPSHOT.war. Sua próxima etapa provavelmente é trabalhar com os engenheiros de operação para que esse artefato seja implantado em um servidor local ou em uma máquina virtual. Para que o aplicativo seja iniciado e executado com êxito, os servidores e as máquinas virtuais devem estar disponíveis e configurados com as dependências necessárias. Esse processo é desafiador e demorado, especialmente sob demanda quando o aumento da carga está atingindo seu aplicativo. Com os contêineres, esses desafios são atenuados.
Construir um Dockerfile
Agora você está pronto para construir um Dockerfile. Um Dockerfile é um documento de texto que contém todos os comandos que um usuário executaria na linha de comando para montar uma imagem de contêiner. Cada imagem é uma camada que pode ser armazenada em cache para obter eficiência. As camadas se baseiam umas nas outras.
Por exemplo, o Sistema de Reservas de Voos de Companhias Aéreas precisa implementar e operar dentro de um servidor de aplicações. Um servidor de aplicativos não é empacotado dentro do AirlinesReservationSample-0.0.1-SNAPSHOT.war. É uma dependência externa necessária para que o AirlinesReservationSample-0.0.1-SNAPSHOT.war execute, ouça e processe solicitações HTTP, gerencie sessões de usuário e facilite as reservas de voo. Se você usou uma implantação tradicional e não em contêineres, os engenheiros de operação instalariam e configurariam um servidor de aplicativos em algum servidor físico ou máquina virtual antes de implantar o AirlinesReservationSample-0.0.1-SNAPSHOT.war nele. Esses engenheiros de operação também precisariam garantir que o JDK que está sendo usado em seu computador - que é o que mvn clean package é usado para compilar o arquivo WAR - de fato corresponda ao mesmo JRE que está sendo usado pelo servidor de aplicativos. O gerenciamento dessas dependências é desafiador e demorado.
Com um Dockerfile, você pode escrever as instruções ou as camadas necessárias para atingir essa meta automaticamente, usando as etapas necessárias para garantir que o Sistema de Reserva de Voo para Reservas Aéreas tenha todas as dependências necessárias para implantar no runtime de contêiner do Docker. Essa solução é atraente quando você trabalha com escalabilidade sob demanda em momentos inesperados. Cada camada usa o cache do Docker, que contém o estado da imagem do contêiner em cada marco de instrução, otimizando o tempo de computação e a reutilização. Se uma camada não estiver sendo alterada, as camadas armazenadas em cache serão usadas. Casos de uso comuns para camadas armazenadas em cache são o runtime do Java, o servidor de aplicativos e outras dependências para o aplicativo Web Flight Booking System for Airline Reservations. Se e quando uma versão for alterada em uma camada armazenada em cache anteriormente, uma nova entrada armazenada em cache será criada.
O diagrama a seguir ilustra as camadas de uma imagem de contêiner. Quando os comandos no Dockerfile são executados, as camadas são criadas. A camada superior é o Sistema de Reserva de Voo de leitura/gravação para a camada de aplicativo Web de Reservas Aéreas. Essa camada é criada sobre as camadas anteriores que são apenas de leitura.
O Docker tem o conceito de builds de vários estágios, um recurso que permite criar uma imagem de contêiner menor com melhor cache e um volume de segurança menor, permitindo maior otimização e manutenção do Dockerfile ao longo do tempo. Por exemplo, você pode separar a fase de construção do contêiner para compilar e construir o aplicativo da fase de execução do aplicativo. Esse recurso permite que você copie apenas os artefatos gerados durante o build para a imagem do contêiner de produção, o que reduz o volume. Como as imagens de contêiner são armazenadas em cache, se não houver alterações, as imagens armazenadas em cache poderão ser reutilizadas, reduzindo o custo e o tempo de download na rede.
Os serviços expostos no ambiente de produção devem ser cuidadosamente gerenciados para segurança. Portanto, o ambiente de produção usa e opera uma imagem de contêiner segura. O exemplo usa a CBL-Mariner imagem fornecida pela Microsoft.
CBL-Mariner Linux é um sistema operacional leve, contendo apenas os pacotes necessários para um ambiente de nuvem. Você pode personalizá-lo por meio de pacotes personalizados e ferramentas para atender aos requisitos do seu aplicativo. CBL-Mariner passa por testes de validação do Azure e é compatível com agentes do Azure. A Microsoft cria e testa CBL-Mariner para alimentar vários casos de uso, desde os serviços do Azure até a alimentação da infraestrutura de IoT. É a distribuição do Linux recomendada internamente para uso com os serviços de nuvem da Microsoft e produtos relacionados.
Observação
A Microsoft fornece imagens de contêiner agrupadas com OpenJDK, incluindo as imagens Ubuntu, CBL-Mariner e distroless. A distroless imagem tem o menor tamanho de imagem, mas executar o Tomcat nela é desafiador. Para obter um design leve, a distroless imagem remove muitos comandos e ferramentas, incluindo o shell, o que significa que você não pode chamar catalina.sh para iniciar o Tomcat. A distroless imagem é adequada para executar JARs executáveis, como aqueles usados com Spring Boot ou Quarkus.
No exemplo a seguir, a mesma versão do Microsoft Build do OpenJDK é usada tanto no estágio de build quanto no estágio final. Essa abordagem garante que você crie o código-fonte com a mesma versão do JDK que o Tomcat de implantação de serviço usa, o que ajuda a evitar comportamentos inesperados devido a incompatibilidades de versão.
A imagem a seguir ilustra o build de vários estágios e o que está ocorrendo em cada estágio com base nos comandos especificados no Dockerfile:
No estágio 0, o Tomcat é baixado e extraído em um diretório especificado por uma variável de ambiente em uma imagem do Ubuntu. A TOMCAT_VERSION variável especifica a versão do Tomcat a ser baixada. Se uma nova versão do Tomcat for lançada, você deverá atualizar o número de versão, pois uma nova imagem só será buscada quando o número de versão for alterado. Caso contrário, a imagem armazenada em cache será usada. O Tomcat baixado é copiado para o ambiente de estágio final para uso.
No estágio 1, o Maven é instalado em uma imagem do Ubuntu e os arquivos de configuração e código-fonte criados são copiados antes de criar o projeto Maven. Cada camada é armazenada em cache, de modo que a imagem do sistema operacional e as camadas de imagem do Maven reutilizem o cache. Se arquivos de configuração, arquivos de código-fonte ou o diretório Web forem atualizados, as camadas das alterações em diante serão recriadas. Se o build for concluído com êxito sem erros durante a compilação, um artefato chamado AirlinesReservationSample-0.0.1-SNAPSHOT.war será gerado no diretório de destino . Esse artefato é copiado para o ambiente de estágio final para uso.
No estágio final, a imagem segura CBL-Mariner fornecida pela Microsoft é usada para copiar os artefatos de build Tomcat e Java do estágio 0 e estágio 1, respectivamente. Um usuário nomeado app possui todos os arquivos usados no projeto e o aplicativo também é executado como o app usuário em vez de ter root privilégios. Essa configuração garante que a imagem de contêiner possa ser operada com segurança sem conceder permissões desnecessárias. Por fim, o número da porta 8080 é exposto e o script catalina.sh é executado para iniciar o Tomcat. Quando isso é executado no Docker Desktop local, você pode acessá-lo por meio da URL http://localhost:8080/AirlinesReservationSample.
Na pasta raiz do projeto, containerize-and-deploy-Java-app-to-Azure/Project/Airlines, use o seguinte comando para criar um arquivo chamado Dockerfile:
vi Dockerfile
Adicione o conteúdo a seguir ao Dockerfile e salve e saia. Para salvar e sair, pressione ESC, digite :wq!e pressione Enter.
############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat
ENV CATALINA_HOME=/usr/local/tomcat
# Configure Tomcat Version (Be sure to use the latest version)
ENV TOMCAT_VERSION=10.1.33
# Install Tomcat and required packages
RUN apt-get update ; \
apt-get install -y curl ; \
curl -O https://downloads.apache.org/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
tar xzf apache-tomcat-${TOMCAT_VERSION}.tar.gz ; \
mv apache-tomcat-${TOMCAT_VERSION} ${CATALINA_HOME} ; \
rm apache-tomcat-${TOMCAT_VERSION}.tar.gz && \
apt-get remove --purge -y curl && \
apt-get autoremove -y && \
apt-get clean
############################################
# Build stage (Compiles with Java 17)
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS build
WORKDIR /build
# Install Maven
RUN apt-get update && apt-get install -y maven && mvn --version
# Copy source code
COPY pom.xml .
COPY src ./src
COPY web ./web
# Build the project
RUN mvn clean package
############################################
# Package final stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-mariner
# Configure the location of the Tomcat installation
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the path to the Tomcat binaries
ENV PATH=$CATALINA_HOME/bin:$PATH
# This is the user that runs the Tomcat process
USER app
# Copy the Tomcat installation from the Tomcat stage
COPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}
# Copy the Tomcat configuration files
COPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/conf
# Copy the compiled WAR file from the build stage
COPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.war
# Expose the default Tomcat port
EXPOSE 8080
# Start Tomcat
CMD ["catalina.sh", "run"]
Observação
Opcionalmente, você pode usar o arquivo Dockerfile_Solution na raiz do projeto, que contém o conteúdo necessário.
O Dockerfile é dividido em três estágios, que são descritos nas seguintes tabelas:
O estágio de instalação do Tomcat:
Comando do Docker Descrição FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcatdefine a imagem base para o Microsoft Build do OpenJDK 17 no Ubuntu e nomeia esse estágiotomcat. É aqui que o Tomcat está instalado.ENVENV CATALINA_HOME=/usr/local/tomcatdefine uma variável de ambiente para o diretório de instalação do Tomcat.ENVENV TOMCAT_VERSION=10.1.33define a versão do Tomcat a ser instalada. Isso deve ser atualizado para a versão mais recente, conforme necessário.RUNO RUNcomando atualiza a lista de pacotescurl, instala, baixa a versão especificada do Tomcat, extrai-a, move-a para o diretório especificado e limpa arquivos e pacotes desnecessários. Isso garante que a imagem permaneça leve.O estágio build, que é compilado com o Java 17:
Comando do Docker Descrição FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS builddefine a imagem base para o Microsoft Build do OpenJDK 17 no Ubuntu e nomeia esse estágiobuild. Esse estágio é usado para compilar o aplicativo Java.WORKDIRWORKDIR /builddefine o diretório de trabalho dentro do contêiner para/build, em que o código-fonte é copiado e compilado.RUNRUN apt-get update && apt-get install -y maven && mvn --versioninstala o Maven, uma ferramenta de automação de build usada para projetos Java e verifica sua instalação.COPYCOPY pom.xml .copia o arquivo de configuração do Maven no diretório de trabalho. Esse arquivo é essencial para a criação do projeto.COPYCOPY src ./srccopia o diretório do código-fonte para o contêiner. É aqui que reside o código do aplicativo Java.COPYCOPY web ./webcopia o diretório de recursos da Web para o contêiner. Isso inclui os recursos de aplicativo Web necessários para o build.RUNRUN mvn clean packageexecuta o processo de build do Maven, que compila o aplicativo Java e o empacota em um arquivo WAR.O estágio final do pacote:
Comando do Docker Descrição FROMFROM mcr.microsoft.com/openjdk/jdk:17-marinerdefine a imagem base para o Microsoft Build do OpenJDK 17 emCBL-Marinerque é usado para a implantação final do aplicativo.ENVENV CATALINA_HOME=/usr/local/tomcatdefine a variável de ambiente para o diretório de instalação do Tomcat, semelhante ao estágio de instalação.ENVENV PATH=$CATALINA_HOME/bin:$PATHadiciona o diretório bin Tomcat ao sistemaPATH, permitindo que os comandos Tomcat sejam executados facilmente.USERUSER appespecifica o usuário sob o qual o processo do Tomcat é executado, aprimorando a segurança ao não ser executado como usuário raiz.COPYCOPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}copia a instalação do Tomcat na etapatomcat, definindo como proprietário o usuárioapp.COPYCOPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/confcopia o arquivo de configuração do usuário Tomcat no contêiner, definindo a propriedade para oappusuário.COPYCOPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.warcopia o arquivo WAR compilado dobuildestágio para o diretório webapps do Tomcat, definindo a propriedade para oappusuário.EXPOSEEXPOSE 8080expõe a porta 8080, a porta padrão do Tomcat, permitindo o acesso externo ao aplicativo.CMDCMD ["catalina.sh", "run"]especifica o comando para iniciar o Tomcat quando o contêiner for executado.
Para obter mais informações sobre a construção do Dockerfile, consulte a referência do Dockerfile.


