Contentorizar uma aplicação Java
Nesta unidade, você colocará em contêiner um aplicativo Java.
Como mencionado anteriormente, os contêineres são executados diretamente sobre o sistema operacional host, kernel e hardware como essencialmente apenas outro processo do sistema. Os contêineres exigem menos recursos do sistema, o que resulta em menor espaço ocupado, menos sobrecarga e tempos de inicialização de aplicativos mais rápidos. Estes são ótimos casos de uso para dimensionamento sob demanda.
Existem contêineres Windows e contêineres Linux. Neste módulo, você aproveitará o tempo de execução do Docker amplamente usado para criar uma imagem de contêiner do Linux. Em seguida, você implantará a imagem do contêiner Linux no sistema operacional host de sua máquina local. Finalmente, você implantará a imagem de contêiner do Linux no Serviço Kubernetes do Azure.
Descrição geral de Docker
O tempo de execução do Docker é usado para criar, extrair, executar e enviar imagens de contêiner. A imagem a seguir mostra esses casos de uso, seguida por uma descrição de cada comando de caso de uso/Docker.
Comando Docker | Description |
---|---|
docker build |
Constrói uma imagem de contêiner; essencialmente, as instruções/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, e é de onde o Serviço Kubernetes do Azure será extraído. O resultado desse comando é um pull de rede de uma imagem que ocorrerá no Azure. Observe que, opcionalmente, você pode extrair imagens localmente; Isso é comum ao criar imagens que exigem dependências/camadas para as quais 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 armazenará as imagens para que elas fiquem prontamente disponíveis e a rede seja fechada para implantações e dimensionamento do Azure. |
Clone o aplicativo Java
Primeiro, você clonará o repositório do Sistema de Reservas de Voos para Reservas de Companhias Aéreas e o cd para a pasta do projeto do aplicativo Web Companhias Aéreas.
Nota
Se a criação do Serviço Kubernetes do Azure tiver sido concluída com êxito na guia CLI, use essa guia; se ainda estiver em execução, abra um novo separador e um cd para o local onde prefere clonar o Sistema de Reservas de Voos para Reservas de Companhias Aéreas.
Execute o seguinte comando na sua CLI:
git clone https://github.com/Azure-Samples/containerize-and-deploy-Java-app-to-Azure.git
Execute o seguinte comando na sua CLI:
cd containerize-and-deploy-Java-app-to-Azure/Project/Airlines
Nota
Opcionalmente, se você tiver o Java e o Maven instalados, poderá executar o(s) seguinte(s) comando(s) em sua CLI para ter uma noção da experiência na criação do aplicativo sem o Docker. Se você não tiver o Java e o Maven instalados, poderá avançar com segurança para a próxima seção, Construir um arquivo do Docker. Nessa seção, você usará o Docker para puxar Java e Maven para executar as compilações em seu nome.
Opcionalmente, se você tiver o Maven e um JDK(8) ou superior instalado, poderá executar o seguinte comando na CLI:
mvn clean install
Nota
Usamos o mvn clean install
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 se mover com segurança sem executar o comando Maven.
A Maven deve ter construído com sucesso o Flight Booking System for Airline Reservations Web Application Archive artifact FlightBookingSystemSample-0.0.-SNAPSHOT.war, como na saída a seguir:
[INFO] Building war: /mnt/c/Users/chtrembl/dev/git/containerize-and-deploy-Java-app-to-Azure/Project/FlightBookingSystemSample/target/FlightBookingSystemSample-0.0.1-SNAPSHOT.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 17.698 s
[INFO] Finished at: 2021-09-28T15:18:07-04:00
[INFO] ------------------------------------------------------------------------
Imagine que você é um desenvolvedor Java e acabou de construir isso FlightBookingSystemSample-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, isso requer que os servidores e máquinas virtuais estejam disponíveis e configurados com as dependências necessárias. Isso é 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
Neste ponto, você está pronto para construir um Dockerfile. Um Dockerfile é um documento de texto que contém todos os comandos que um usuário pode executar na linha de comando para montar uma imagem de contêiner, cada um dos quais são camadas (que podem ser armazenadas em cache para eficiência) que se baseiam umas nas 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 FlightBookingSystemSample-0.0.1-SNAPSHOT.war
, é uma dependência externa necessária para executar FlightBookingSystemSample-0.0.1-SNAPSHOT.war
, ouvir e processar solicitações HTTP, gerenciar sessões de usuários e facilitar reservas de voos. Se essa fosse uma implantação tradicional e não conteinerizada, os engenheiros de operação instalariam e configurariam um servidor de aplicativos em algum servidor físico e/ou máquina virtual antes de implantá-lo FlightBookingSystemSample-0.0.1-SNAPSHOT.war
. Esses engenheiros de operação também precisariam garantir que o JDK que está sendo usado em sua máquina (o que estava sendo usado para compilar o mvn clean install
.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, você pode escrever as instruções (camadas) necessárias para fazer isso automaticamente, colocando em camadas as etapas necessárias para garantir que o Sistema de Reservas de Voos para Reservas de Companhias Aéreas tenha todas as dependências necessárias para implantar no tempo de execução do contêiner do Docker. Isso é muito atraente quando você começa a pensar em escala sob demanda em intervalos não planejados. Vale a pena notar que cada camada está aproveitando o cache do Docker, que contém o estado da imagem do contêiner em cada etapa de instrução, 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. Casos de uso comuns para camadas armazenadas em cache são coisas como tempo de execução Java, servidor de aplicativos e/ou 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 anteriormente em cache, uma nova entrada armazenada em cache será criada.
A imagem a seguir mostra as camadas de uma imagem de contêiner. Você notará que a camada superior é o Sistema de Reserva de Voos de leitura/gravação para a camada do aplicativo da Web Reservas de Companhias Aéreas, que é construído sobre as camadas somente leitura anteriores, todas resultantes dos comandos no Dockerfile.
O Docker também 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 uma pegada de segurança menor, permitindo uma maior otimização e manutenção do Dockerfile ao longo do tempo; Por exemplo, instruções que você pode usar para realizar tanto uma compilação do aplicativo (FlightBookingSystemSample-0.0.1-SNAPSHOT.war
) quanto uma compilação da própria imagem do contêiner, deixando os restos da compilação para trás, resultando em uma pegada FlightBookingSystemSample-0.0.1-SNAPSHOT.war
menor. A longo prazo, isso paga dividendos quando você começa a pensar nessas imagens viajando pela rede. Com compilações de vários estágios, você usa várias instruções FROM em seu Dockerfile. Cada instrução FROM pode usar uma base diferente, e cada uma dessas instruções começa com uma folha limpa, removendo quaisquer arquivos desnecessários na camada de cache que normalmente podem ser armazenados em cache.
É imperativo garantir que o aplicativo seja construído pelo mesmo JDK correspondente ao mesmo JRE que será isolado na imagem do contêiner em tempo de execução. No exemplo a seguir, você terá um estágio Build que aproveita uma versão específica do Maven e uma versão específica do JDK para compilar o FlightBookingSystemSample-0.0.1-SNAPSHOT.war
. Este estágio garante que qualquer tempo de execução do Docker que execute este estágio obterá o código de byte gerado esperado que o autor do Dockerfile especificou (caso contrário, os engenheiros de operação teriam que fazer referência cruzada ao tempo de execução do Java e do servidor de aplicativos com o do desenvolvedor). O estágio Pacote usará uma versão específica do Tomcat e do JRE correspondente ao JDK no estágio Build. Novamente, isso é feito para garantir que todas as dependências (Java Development Kit JDK, Java Runtime Environment JRE, servidor de aplicativos) sejam controladas e isoladas para garantir o comportamento esperado em todas as máquinas nas quais essa imagem será executada.
Também vale a pena notar que, com esta compilação de vários estágios, tecnicamente não há necessidade de Maven e Java serem instalados no sistema. O Docker irá puxá-los para baixo para uso com a criação do aplicativo, bem como o tempo de execução do aplicativo, evitando qualquer potencial conflito de versão e comportamento inesperado; a menos, é claro, que você esteja compilando código e criando artefatos fora do Docker.
A imagem a seguir mostra a compilação de vários estágios e o que está ocorrendo em cada estágio com base nos comandos especificados no Dockerfile. Na Etapa 0, a Etapa de Construção, o aplicativo da web Sistema de Reservas de Voos para Reservas de Companhias Aéreas é compilado e FlightBookingSystemSample-0.0.1-SNAPSHOT.war
gerado. Esta etapa permite a consistência das versões Maven e Java usadas para compilar esta aplicação. FlightBookingSystemSample-0.0.1-SNAPSHOT.war
Uma vez criada, essa é a única camada necessária para o Estágio 1 (Estágio de Tempo de Execução), e todas as camadas anteriores podem ser descartadas. O Docker usará essa FlightBookingSystemSample-0.0.1-SNAPSHOT.war
camada do Estágio 0 para construir as camadas restantes necessárias para o tempo de execução, neste caso, configurando o servidor de aplicativos e iniciando o aplicativo.
Dentro da raiz do seu projeto, containerize-and-deploy-Java-app-to-Azure/Project/Airlines, crie um arquivo chamado Dockerfile:
vi Dockerfile
Adicione o seguinte conteúdo ao Dockerfile, salve e saia pressionando ESC e, em seguida, digitando :wq! e pressionando Enter:
#
# Build stage
#
FROM maven:3.6.0-jdk-11-slim AS build
WORKDIR /build
COPY pom.xml .
COPY src ./src
COPY web ./web
RUN mvn clean package
#
# Package stage
#
FROM tomcat:8.5.72-jre11-openjdk-slim
COPY tomcat-users.xml /usr/local/tomcat/conf
COPY --from=build /build/target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war
EXPOSE 8080
CMD ["catalina.sh", "run"]
Nota
Opcionalmente, o Dockerfile_Solution na raiz do seu projeto contém o conteúdo necessário.
Este estágio de compilação do Dockerfile tem seis instruções:
Comando Docker | Description |
---|---|
FROM |
FROM maven será a camada base a partir da qual isso FlightBookingSystemSample-0.0.1-SNAPSHOT.war é construído, uma versão específica do Maven e uma versão específica do JDK para garantir que a mesma compilação de código de byte ocorra em todas as máquinas que executam essa compilação. |
WORKDIR |
WORKDIR é usado para definir o diretório de trabalho de um contêiner a qualquer momento; neste caso, onde os artefatos compilados residirão. |
COPY |
COPY adiciona arquivos do diretório atual do cliente Docker. Configurando os arquivos necessários para o Maven compilar, pom.xml serão necessários para o contexto do Docker. |
COPY |
Configura os arquivos necessários para o Maven compilar. O contexto do Docker precisará da pasta src que contém o aplicativo da Web Sistema de Reservas de Voos para Reservas de Companhias Aéreas. |
COPY |
Configura os arquivos necessários para o Maven compilar. O contexto do Web Docket precisará da pasta que contém as dependências do aplicativo Web Flight Booking System for Airline Reservations. |
EXECUTAR | A RUN mvn clean package instrução é usada para executar qualquer comando na parte superior da imagem atual. Nesse caso, RUN é usado para executar a compilação do Maven, que compilará o FlightBookingSystemSample-0.0.1-SNAPSHOT.war arquivo . |
Este estágio do pacote de arquivos do Docker tem cinco instruções:
Comando Docker | Description |
---|---|
FROM |
FROM tomcat será a camada base sobre a qual a imagem do contêiner será construída. A imagem do contêiner do Sistema de Reservas de Voos para Reservas de Companhias Aéreas será uma imagem construída sobre a imagem do tomcat. O tempo de execução do Docker tentará localizar a imagem do tomcat localmente. Se ele não tiver essa versão, ele puxará um para baixo do registro. Se inspecionasse a imagem do tomcat que está sendo referenciada aqui, você descobriria que ela é construída usando muitas outras camadas, que a tornam reutilizável como uma imagem de contêiner do servidor de aplicativos empacotada para o mundo usar ao implantar seu aplicativo Java. Nós selecionamos e testamos tomcat:8.5.72-jre11-openjdk-slim para fins de módulo. Observe que todas as camadas anteriores do primeiro estágio de compilação desaparecerão quando o Docker reconhecer essa segunda instrução FROM. |
COPY |
COPY tomcat-users.xml copiará o arquivo de tomcat-users.xml que gerencia o Sistema de Reservas de Voos para usuários de Reservas de Companhias Aéreas (gerenciado dentro do controle do código-fonte usando a identidade Tomcat; normalmente, isso estaria em um sistema de gerenciamento de identidade externo) para a imagem do contêiner tomcat para que ela esteja presente na imagem do contêiner sempre que uma imagem de contêiner for criada. |
ADD |
ADD target/*.war /usr/local/tomcat/webapps/FlightBookingSystemSample.war copiará o maven compilado FlightBookingSystemSample-0.0.1-SNAPSHOT.war para a pasta Tomcat images webapps para garantir que, quando o Tomcat for inicializado, ele encontrará o FlightBookingSystemSample-0.0.1-SNAPSHOT.war a ser instalado no servidor de aplicativos. |
EXPOSE |
EXPOSE 8080 é necessário porque o Tomcat está configurado para ouvir o tráfego na porta 8080. Isso garante que o processo do Docker escutará nessa porta. |
CMD |
A instrução CMD define um comando a ser executado ao executar o contêiner. Nesse caso, CMD ["catalina.sh", "run"] instrui o Docker a inicializar o servidor de aplicativos Tomcat. |
Nota
Sem uma tag de FROM tomcat
versão na linha, a versão mais recente será aplicada. Geralmente, você vai querer aproveitar uma tag de versão (lembre-se, o cache é aplicado, portanto, se as camadas estiverem mudando consistentemente, você incorrerá em largura de banda, latência, tempo de computação e/ou efeitos colaterais de compilações/camadas não testadas). Para este módulo, pré-selecionamos tags específicas Maven, Tomcat e Java JRE/JDK que são testadas para trabalhar em FlightBookingSystemSample-0.0.1-SNAPSHOT.war
tempo de execução.
Para obter mais informações sobre a construção do Dockerfile, consulte Referência do Dockerfile