Contentorizar uma aplicação Java
Nesta unidade, você conteineriza um aplicativo Java.
Como mencionado anteriormente, os contêineres são executados diretamente sobre o sistema operacional host, kernel e hardware como processos comuns do sistema. Os contêineres exigem menos recursos do sistema, resultando em menor espaço ocupado, 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.
Existem contêineres Windows e contêineres Linux. Neste módulo, você usa o tempo de execução do Docker amplamente usado para criar uma imagem de contêiner do Linux. Em seguida, você implanta a imagem do contêiner Linux no sistema operacional host da sua máquina local. Finalmente, você implanta a imagem de contêiner do Linux no Serviço Kubernetes do Azure.
Visão geral do Docker
O tempo de execução do Docker é usado para criar, extrair, executar e enviar imagens de contêiner, conforme mostrado no diagrama a seguir:
A tabela a seguir descreve cada comando do Docker:
| Comando 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 deste 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. Este registro é de onde o Serviço Kubernetes do Azure extrai. O resultado deste comando é uma transferência de rede de uma imagem, que ocorre no Azure. Opcionalmente, você pode extrair 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 Registo de Contentores do Azure armazena as imagens para que estejam prontamente disponíveis e próximas à rede para implantações e dimensionamento no Azure. |
Clone o aplicativo Java
Primeiro, clone o repositório do Sistema de Reservas de Voos para Reservas de Companhias Aéreas e navegue até à pasta do projeto da aplicação Web de Companhias Aéreas.
Observação
Se a criação do Serviço Kubernetes do Azure estiver concluída na guia CLI, use essa guia. Se ainda estiver em execução, abra uma nova guia e navegue até o local onde você prefere clonar o Sistema de Reservas de Voos para Reservas de Companhias Aéreas.
Execute os seguintes comandos:
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 o Java e o Maven instalados, poderá executar o seguinte comando 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 o Java e o 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 puxar Java e Maven para executar as compilações em seu nome.
mvn clean package
Observação
Usamos o mvn clean package comando para ilustrar os desafios operacionais de não usar compilações de vários estágios do Docker, que abordaremos a seguir. Mais uma vez, este passo é opcional. De qualquer forma, você pode prosseguir com segurança sem executar o comando Maven.
Se o processo foi bem-sucedido, a Maven construiu com êxito o Flight Booking System for Airline Reservations Web Application Archive artifact 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 desenvolver AirlinesReservationSample-0.0.1-SNAPSHOT.war. Sua próxima etapa provavelmente é trabalhar com os engenheiros de operação para implantar esse artefato em um servidor local ou em uma máquina virtual. Para que o aplicativo seja iniciado e executado com êxito, os servidores e 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 contentores, estes desafios são aliviados.
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 eficiência. As camadas constroem-se umas sobre as outras.
Por exemplo, o Sistema de Reservas de Voos para Reservas de Companhias Aéreas precisa ser implantado e executado dentro de um servidor de aplicativos. Um servidor de aplicativos não é empacotado dentro do AirlinesReservationSample-0.0.1-SNAPSHOT.war. É uma dependência externa necessária para a execução do AirlinesReservationSample-0.0.1-SNAPSHOT.war, escutar e processar solicitações HTTP, gerir sessões de utilizadores e facilitar a reserva de voos. Se você usasse uma implantação tradicional, não conteinerizada, 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 sua máquina - que é o que mvn clean package usou para compilar o arquivo WAR - de fato corresponde ao mesmo JRE que está sendo usado pelo servidor de aplicativos. Gerenciar essas dependências é desafiador e demorado.
Com um Dockerfile, pode-se escrever as instruções ou camadas necessárias para alcançar esse objetivo automaticamente, organizando em camadas os passos necessários para garantir que o Sistema de Reserva de Voos para Reservas de Companhias Aéreas tenha todas as dependências necessárias para implementar no ambiente de execução do Docker. Esta solução é convincente quando se trabalha com escalabilidade sob demanda em intervalos não planeados. Cada camada usa o cache do Docker, que contém o estado da imagem do contêiner em cada etapa instrucional, otimizando o tempo de computação e a reutilização. Se uma camada não estiver mudando, as camadas armazenadas em cache serão usadas. Os casos de uso comuns para camadas em cache são o ambiente de execução Java, o servidor de aplicações e outras dependências para a aplicação Web de Reservas de Voos para Companhias Aéreas. Se e quando uma versão for alterada em uma camada armazenada anteriormente em cache, uma nova entrada armazenada em cache será criada.
O diagrama a seguir mostra 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 Leitura/Escrita de Reserva de Voo para a camada de aplicação web de Reservas de Companhias Aéreas. Essa camada é construída sobre as camadas de apenas leitura anteriores.
O Docker tem o conceito de compilações de vários estágios, um recurso que permite criar uma imagem de contêiner menor com melhor cache e menor pegada de segurança, permitindo maior otimização e manutenção do Dockerfile ao longo do tempo. Por exemplo, você pode separar o estágio de compilação do contêiner para compilar e criar o aplicativo do estágio para executar o aplicativo. Esse recurso permite copiar apenas os artefatos gerados durante a compilação para a imagem do contêiner de produção, o que reduz o espaço ocupado. Como as imagens de contêiner são armazenadas em cache, se não houver alterações, as imagens armazenadas em cache podem ser reutilizadas, reduzindo o custo e o tempo de download da rede.
Os serviços expostos no ambiente de produção devem ser cuidadosamente gerenciados por 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 através de pacotes e ferramentas personalizadas 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 serviços do Azure até a alimentação da infraestrutura de IoT. É a distribuição Linux recomendada internamente para uso com serviços de nuvem da Microsoft e produtos relacionados.
Observação
A Microsoft fornece imagens de contentores em conjunto com o OpenJDK, incluindo imagens Ubuntu, CBL-Mariner e distroless. A distroless imagem tem o menor tamanho de imagem, mas executar o Tomcat nela é um desafio. Para obter um design leve, a imagem remove muitos comandos e ferramentas, incluindo o shell, o que significa que não se pode executar distroless 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 no estágio de compilação e no estágio final. Essa abordagem garante que você crie o código-fonte com a mesma versão do JDK usada pelo Tomcat na implantação do serviço, o que ajuda a evitar comportamentos inesperados devido a incompatibilidades de versão.
A imagem seguinte mostra a construção de vários estágios e o que está a ocorrer 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ê deve atualizar o número da versão, já que uma nova imagem só é buscada quando o número da versão é 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 o código-fonte criado e os arquivos de configuração são copiados antes de construir o projeto Maven. Cada camada é armazenada em cache, de modo que a imagem do sistema operacional e as camadas de imagem do Maven reutilizam o cache. Se os arquivos de configuração, os arquivos de código-fonte ou o diretório da Web forem atualizados, as camadas a partir das alterações serão reconstruídas. Se a compilação for concluída 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 final de estágio para utilização.
No estágio final, a imagem segura CBL-Mariner fornecida pela Microsoft é usada para copiar os artefatos de construção Tomcat e Java do estágio 0 e estágio 1, respectivamente. Um usuário chamado app possui todos os arquivos usados dentro do 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 do contêiner possa ser operada com segurança sem conceder permissões desnecessárias. Finalmente, o número da porta 8080 é exposto e o script catalina.sh é executado para iniciar o Tomcat. Quando isso é executado em seu Docker Desktop local, você pode acessá-lo através do URL http://localhost:8080/AirlinesReservationSample.
Na pasta raiz do seu projeto, containerize-and-deploy-Java-app-to-Azure/Project/Airlines, use o seguinte comando para criar um arquivo chamado Dockerfile:
vi Dockerfile
Adicione o seguinte conteúdo ao seu Dockerfile, salve e saia. Para guardar e sair, prima ESC, escreva :wq! e, em seguida, prima 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 seu projeto, que contém o conteúdo que você precisa.
O Dockerfile é dividido em três estágios, que são descritos nas tabelas a seguir:
O estágio de instalação do Tomcat:
Comando Docker Descrição FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcatdefine a imagem base como Microsoft Build do OpenJDK 17 no Ubuntu e nomeia este 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 pacotes, instala,curlbaixa 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.A etapa de build, que compila utilizando Java 17:
Comando Docker Descrição FROMFROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS builddefine a imagem base como Microsoft Build do OpenJDK 17 no Ubuntu e nomeia este estágiobuild. Esta etapa é usada para compilar a aplicação Java.WORKDIRWORKDIR /buildDefine o diretório de trabalho dentro do contêiner como/build, onde 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 construção usada para projetos Java, e verifica sua instalação.COPYCOPY pom.xml .copia o arquivo de configuração do Maven para o diretório de trabalho. Este arquivo é essencial para a construção do projeto.COPYCOPY src ./srcCopia o diretório do código-fonte para o contêiner. É aqui que reside o código da aplicação Java.COPYCOPY web ./webCopia o diretório Recursos da Web para o contêiner. Isso inclui recursos de aplicativos Web necessários para a compilação.RUNRUN mvn clean packageexecuta o processo de compilação Maven, que compila o aplicativo Java e o empacota em um arquivo WAR.A fase final do pacote:
Comando Docker Descrição FROMFROM mcr.microsoft.com/openjdk/jdk:17-marinerdefine a imagem base como Microsoft Build do OpenJDK 17 noCBL-Mariner, que é 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 do Tomcat ao sistemaPATH, permitindo que os comandos do Tomcat sejam executados facilmente.USERUSER appespecifica o usuário sob o qual o processo Tomcat é executado, aumentando a segurança ao não ser executado como o usuário raiz.COPYCOPY --chown=app:app --from=tomcat ${CATALINA_HOME} ${CATALINA_HOME}copia a instalação do Tomcat datomcatetapa, definindo a propriedade para oapputilizador.COPYCOPY --chown=app:app tomcat-users.xml ${CATALINA_HOME}/confcopia o arquivo de configuração do usuário do Tomcat para o contêiner, definindo a propriedade para oappusuário.COPYCOPY --chown=app:app --from=build /build/target/*.war ${CATALINA_HOME}/webapps/AirlinesReservationSample.warcopia o ficheiro WAR compilado dabuildetapa para o diretório webapps do Tomcat, atribuindo a propriedade ao utilizadorapp.EXPOSEEXPOSE 8080expõe a porta 8080, a porta padrão do Tomcat, permitindo acesso externo ao aplicativo.CMDCMD ["catalina.sh", "run"]especifica o comando para iniciar o Tomcat quando o contêiner é executado.
Para obter mais informações sobre a construção do Dockerfile, consulte a referência do Dockerfile.