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 |
Baixa uma imagem de um registro no repositório de imagens do daemon do Docker selecionado pelo cliente. As camadas de imagem existentes podem ser reutilizados. Neste módulo, seu ambiente local do Docker usa imagens de registries durante o desenvolvimento; posteriormente, os nós do AKS efetuam pull da imagem do aplicativo do Registro de Contêiner do Azure para implantação. |
docker run |
Cria e inicia um novo contêiner a partir de uma imagem. O contêiner executa seu processo de inicialização configurado, sujeito a substituições de linha de comando. As camadas de imagem fornecem o sistema de arquivos do contêiner; As instruções de build do Dockerfile não são executadas novamente quando o contêiner é iniciado. |
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, a Maven construiu com êxito o artefato Flight Booking System for Airline Reservations Web Application Archive airlinesReservationSample-0.0.1-SNAPSHOT.war, conforme ilustrado pela saída histórica a seguir. A data e hora da compilação e a duração podem ser diferentes:
[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. Uma imagem de contêiner pode conter várias camadas de sistema de arquivos somente leitura. O Docker pode reutilizar os resultados de build armazenados em cache para etapas inalteradas. 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.
Uma imagem de contêiner contém camadas de sistema de arquivos somente leitura. Neste exemplo, o Dockerfile copia Tomcat e o arquivo WAR do Sistema de Reserva de Voo para a imagem no momento da compilação. O Docker adiciona uma camada gravável separada quando cria um contêiner a partir da imagem. A camada gravável de cada contêiner registra suas próprias alterações de sistema de arquivos sem modificar as camadas de imagem.
Observação
Para arquivos armazenados na camada gravável do contêiner, adições, modificações e exclusões afetam esse contêiner em vez da imagem original. A alteração de arquivos provenientes de uma camada de imagem usa o comportamento copy-on-write.
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 uma imagem do Linux Azure fornecida pelo Microsoft.
O estágio final usa Microsoft Build do OpenJDK 17 no Azure Linux 3.0. A tag 17-azurelinux nomeia esta base explicitamente. A tag mais antiga 17-mariner continua sendo um alias de compatibilidade que espelha a imagem Azure Linux; ela não deve ser confundida com as imagens 17-mariner-cm2 e 17-mariner-cm1 desativadas. Para obter mais informações, consulte imagens de contêiner do Microsoft Build do OpenJDK.
Observação
Microsoft fornece imagens de contêiner agrupadas com OpenJDK, incluindoUbuntu, Azure Linux e distroless imagens. 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 de um repositório do Apache específico da versão, verificado com uma soma de verificação SHA-512 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 e o argumento de TOMCAT_SHA512 build especifica sua soma de verificação esperada. Atualize os dois valores juntos ao selecionar uma versão mais recente com suporte. O Docker pode reutilizar a camada de instalação armazenada em cache enquanto suas instruções e entradas não são alteradas. A instalação do Tomcat é copiada para o ambiente da etapa 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, os artefatos de compilação tomcat e Java do estágio 0 e estágio 1, respectivamente, são copiados para uma imagem Microsoft Build do OpenJDK 17 no Azure Linux 3.0. 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.
Em containerize-and-deploy-Java-app-to-Azure/Project/Airlines, use um editor de texto instalado para criar um arquivo chamado Dockerfile. Adicione o seguinte conteúdo e salve o arquivo:
Importante
Antes de criar uma imagem que você exporá publicamente, verifique a versão atual do Tomcat 10.1.x com suporte na página de download do Apache Tomcat e examine os avisos de segurança. Atualize TOMCAT_VERSION e TOMCAT_SHA512 em conjunto usando as informações de versão publicadas do Apache. Um artefato que permanece disponível no arquivo Apache não significa que 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. O aplicativo deve ser copiado para webapps/AirlinesReservationSample.war para que as URLs nas unidades a seguir usem o contexto /AirlinesReservationSample/. Um nome de arquivo WAR diferente altera o contexto padrão do aplicativo Tomcat.
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.61define a versão do Tomcat fixada para este exemplo. Examine as versões com suporte e atualize-as junto comTOMCAT_SHA512, conforme necessário.ARGARG TOMCAT_SHA512fornece a soma de verificação SHA-512 publicada pelo Apache para o arquivo selecionado como um argumento de build.RUNInstala curle certificados de AC e, em seguida, usacurl -fSLpara baixar o arquivo do Apache específico da versão.-ffalha em caso de erros HTTP;-Lsegue redirecionamentos. O arquivo é verificado comsha512sum -c -antes de ser extraído emCATALINA_HOMEusando--strip-components=1. A&&cadeia para em caso de falha em vez de mascarar erros durante a limpeza. Por fim, ele remove o arquivo e pacotes desnecessários, além de limpar o cache de pacotes.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-azurelinuxdefine a imagem base como Microsoft Build do OpenJDK 17 no Azure Linux 3.0, 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 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 8080documenta que o aplicativo deve escutar na porta TCP 8080. Ele não publica uma porta do host. A próxima unidade publica 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 for executado.
Para obter mais informações sobre a construção do Dockerfile, consulte a referência do Dockerfile.