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 |
Descarrega uma imagem de um registo para o armazenamento de imagens do daemon Docker selecionado pelo cliente. Camadas de imagem existentes podem ser reutilizadas. Neste módulo, o seu ambiente Docker local utiliza imagens de repositórios de imagens durante o desenvolvimento; mais tarde, os nós AKS retiram a imagem da aplicação do Azure Container Registry para implementação. |
docker run |
Cria e inicia um novo contentor a partir de uma imagem. O contentor executa o seu processo de arranque configurado, passível de substituição por opções da linha de comandos. As camadas de imagem fornecem o sistema de ficheiros do contentor; As instruções de compilação do Dockerfile não são executadas novamente quando o contentor inicia. |
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, o Maven construiu com sucesso o artefacto de arquivo de aplicação web Flight Booking System for Airline Reservations AirlinesReservationSample-0.0.1-SNAPSHOT.war, conforme ilustrado pelo seguinte resultado histórico. A sua marca temporal e duração de construção podem variar:
[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. Uma imagem de contentor pode conter múltiplas camadas de sistema de ficheiros somente leitura. O Docker pode reutilizar resultados de compilações em cache para passos inalterados. 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.
Uma imagem de contentor contém camadas de sistema de ficheiros apenas de leitura. Neste exemplo, o Dockerfile copia o Tomcat e o ficheiro WAR do Sistema de Reservas de Voos para a imagem durante a construção. O Docker adiciona uma camada gravável separada quando cria um contentor a partir da imagem. A camada gravável de cada contentor regista as suas próprias alterações no sistema de ficheiros sem modificar as camadas da imagem.
Observação
Para ficheiros armazenados na camada gravável do contentor, adições, modificações e eliminações afetam esse contentor em vez da imagem original. Alterar ficheiros que vieram de uma camada de imagem usa o comportamento copy-on-write.
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 utiliza uma imagem Azure Linux fornecida pela Microsoft.
A fase final utiliza o Microsoft Build do OpenJDK 17 no Azure Linux 3.0. A etiqueta 17-azurelinux nomeia esta base explicitamente. A etiqueta mais antiga 17-mariner mantém-se como um alias de compatibilidade que espelha a imagem do Azure Linux; não deve ser confundida com as imagens retiradas 17-mariner-cm1 e 17-mariner-cm2. Para mais informações, consulte imagens de contentor do Microsoft Build do OpenJDK.
Observação
A Microsoft fornece imagens de contentores incluídas com o OpenJDK, incluindo Ubuntu, Azure Linux e distroless imagens. 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.
Na fase 0, o Tomcat é descarregado de um arquivo Apache específico de cada versão, verificado com uma soma de verificação SHA-512 e extraído para um diretório especificado por uma variável de ambiente numa imagem Ubuntu. A variável TOMCAT_VERSION especifica o release, e o argumento de build TOMCAT_SHA512 especifica a sua soma de verificação esperada. Atualize ambos os valores em conjunto ao selecionar uma versão mais recente suportada. O Docker pode reutilizar a camada de instalação em cache enquanto as suas instruções e entradas permanecem inalteradas. A instalação Tomcat é copiada para o ambiente da fase 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.
Na fase final, os artefactos Tomcat e Java build dos estágios 0 e 1, respetivamente, são copiados para uma imagem Microsoft Build do OpenJDK 17 no Azure Linux 3.0. 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.
Em containerize-and-deploy-Java-app-to-Azure/Project/Airlines, utilize um editor de texto instalado para criar um ficheiro chamado Dockerfile. Adicione o seguinte conteúdo e guarde o ficheiro:
Importante
Antes de criar uma imagem que vá expor publicamente, verifique a versão atual suportada do Tomcat 10.1.x na página de download do Apache Tomcat e reveja os avisos de segurança. Atualize TOMCAT_VERSION e TOMCAT_SHA512 em conjunto, utilizando as informações de lançamento publicadas pela Apache. O facto de um artefacto permanecer disponível no arquivo da Apache não significa que a sua versão seja atual ou recomendada.
############################################
# Tomcat Intall stage
############################################
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu AS tomcat
ENV CATALINA_HOME=/usr/local/tomcat
# Configure the reviewed Tomcat version and checksum
ENV TOMCAT_VERSION=10.1.61
ARG TOMCAT_SHA512=35120a2b1182bac3d55438fbec0dc581d03b0f1c16ce692a930f3bb6a1097cf896b94b389152257071a87cb2928ac8c15f1bad08387c14acc7c17dbc1bb598fb
# Install Tomcat and required packages
RUN apt-get update && \
apt-get install -y --no-install-recommends curl ca-certificates && \
curl -fSL \
"https://archive.apache.org/dist/tomcat/tomcat-10/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz" \
-o /tmp/tomcat.tar.gz && \
echo "${TOMCAT_SHA512} /tmp/tomcat.tar.gz" | sha512sum -c - && \
mkdir -p "${CATALINA_HOME}" && \
tar -xzf /tmp/tomcat.tar.gz --strip-components=1 -C "${CATALINA_HOME}" && \
rm /tmp/tomcat.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-azurelinux
# 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
Use o Dockerfile mostrado nesta unidade para este exercício. A aplicação deve ser copiada para webapps/AirlinesReservationSample.war para que os URLs nas unidades seguintes utilizem o contexto /AirlinesReservationSample/. Um nome de ficheiro WAR diferente altera o contexto padrão da aplicação Tomcat.
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.61define a versão do Tomcat fixada para este exemplo. Revise as versões suportadas e atualize-as juntamente comTOMCAT_SHA512, conforme necessário.ARGARG TOMCAT_SHA512fornece a soma de verificação SHA-512 publicada por Apache para o arquivo selecionado como argumento de build.RUNInstala curle certificados da CA, e depois usacurl -fSLpara descarregar o arquivo Apache específico de cada versão.-ffaz com que erros HTTP falhem;-Lsegue redirecionamentos. O arquivo é verificado comsha512sum -c -antes da extração emCATALINA_HOMEcom--strip-components=1. A cadeia&¶ em caso de falha em vez de mascarar erros durante a limpeza. Por fim, remove o arquivo comprimido e os pacotes desnecessários e limpa a cache dos pacotes.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-azurelinuxdefine a imagem base para Microsoft Build do OpenJDK 17 no Azure Linux 3.0, que é usado para a implementação em produção final da aplicação.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 8080documenta que a aplicação deve ouvir na porta TCP 8080. Não publica uma porta host. A unidade seguinte expõe a porta com a opção-p 8080:8080emdocker run.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.