O blog da AWS

Como conteinerizar uma aplicação em 15 minutos

José Yapur, Senior Developer Advocate en AWS

 

Introdução

Está cada vez mais comum falar em containers dentro de desenvolvimento de software. Algumas pessoas já estão trabalhando com este conceito há algum tempo, enquanto outras estão começando agora. Seja qual for o seu caso, eu gostaria de ser sua guia nesta jornada no fantástico mundo dos containers. Juntos iremos construir o Picturesocial, uma aplicação para compartilhar fotos, com uma arquitetura baseada em containers. À medida que formos construindo a aplicação, iremos discutir decisões de arquitetura e explorar possíveis trade-offs.

 

 

O que são containers?

Mas o que é um container? Imagine montar a sala dos seus sonhos, com a decoração que você escolheu, um sofá confortável para ler e relaxar, uma mesinha para o café, seus livros e a iluminação ideal. Você finalmente sente que ela está perfeita e, então, é hora de se mudar para outro lugar e começar tudo de novo.

Agora, imagine que você montou exatamente essa mesma sala, mas em um daqueles grandes containers de carga. Essa sala pode ir com você para qualquer lugar, pode estar em um navio no meio do oceano ou em um avião a caminho de outro continente.

Um container é exatamente isso: sua aplicação, runtime e file system empacotados logicamente (como sua sala em um container de carga) para serem executados em qualquer lugar que suporte containers. Eu, particularmente, adoro containers porque eles me dão a flexibilidade e a liberdade de executar meu código onde eu quiser, com a certeza de que irá funcionar sem precisar de mudanças.

Mesmo quando sua aplicação é conteinerizada, é provável que as dependências dela, como banco de dados ou gerenciador de filas de mensagens, não estejam dentro de containers também. O mais provável é que essas dependências sejam acessadas como serviços externos. Por isso, é fundamental ter um local para especificar as configurações de ambiente da sua aplicação, como strings de conexão ou credenciais de acesso. Assim, ao mudar de um ambiente para outro (como de desenvolvimento para produção), apenas precisamos garantir que estamos apontando para a configuração correta daquele ambiente, evitando erros como direcionar uma aplicação de produção com alto tráfego para um banco de dados de testes.

Conceitos Básicos

Para começar, vamos aprender alguns conceitos básicos sobre containers Docker que vão nos acompanhar nessa jornada:

  • Imagem:Uma Imagem contém tudo que é necessário para executar uma aplicação. Ela contém a aplicação em si, o seu runtime, as suas dependências e configurações. Uma imagem Docker é construída a partir de um Dockerfile, que é composto por uma lista de instruções com o que a aplicação necessita para funcionar.
  • Container: Uma imagem em execução é chamada de Container.
  • Engine: Um container precisa ser executado em um lugar onde o Docker esteja instalado. A forma como o Docker se comunica com o hardware em que está instalado é através de APIs. E essas APIs são parte do Engine. Com o engine, um container obtém acesso a recursos de hardware, como CPU, disco e rede.
  • Registry: Registry é o local onde são armazenadas imagens de container. Ele pode ser público ou privado. Um registry armazena a versão mais recente de uma imagem e também suas diferentes versões, identificadas através de tags. Ele também contém metadados sobre: 1/ quando a imagem foi atualizada 2/ quem atualizou a imagem e 3/ quando uma imagem é utilizada.

Dockerfile

Agora que já sabemos um pouco mais sobre containers, vamos falar do Dockerfile, o arquivo que diz como uma imagem de container deve ser construída. O Dockerfile é similar ao que muitos de nós fazíamos no começo de nossas carreiras: “escrever o manual da aplicação”. Sim, aquele mesmo manual que quase ninguém lia (muito menos atualizava) e que, quando era necessário, quase nunca funcionava. O principal problema desses manuais é que eles são escritos por humanos para humanos. O Dockerfile, por outro lado, é escrito por humanos para ser computadores, então ele precisa ser escrito de forma precisa, sem deixar espaço para mais de uma interpretação.

Aqui temos um exemplo de Dockerfile:

# Dockerfile de exemplo
FROM ubuntu:20.04
RUN mkdir demo
CMD ["echo", "Olá, mundo!"]

FROM

Um Dockerfile deve ter uma instrução FROM, que especifica qual imagem base está sendo usada para construir uma nova imagem. Por exemplo, se você quer criar uma imagem que irá executar comandos no Ubuntu 20.04, o seu Dockerfile vai precisar do seguinte comando FROM:

FROM ubuntu:20.04

Neste exemplo, a imagem base que estamos usando se chama ubuntu. Esta e milhões de outras imagens estão disponíveis no Docker Hub. A maioria delas são públicas e podem ser usadas em nossas aplicações. Ainda no nosso exemplo, 20.04 é a versão do Ubuntu que estamos utilizando. Tudo que está depois dos dois pontos (:) é a tag da imagem. As tags são utilizadas para especificarmos a versão de uma imagem. Acessando a imagem do Ubuntu no DockerHub, podemos ver as diferentes tags disponíveis para essa imagem.

RUN

A instrução RUN é usada para executar os comandos necessários para construir um container. Você pode usar o RUN múltiplas vezes dentro do seu Dockerfile.

Por exemplo, o comando RUN abaixo vai criar uma pasta chamada demo:

RUN mkdir demo

CMD

A instrução CMD é responsável por definir o comando padrão que será executado no momento em que o seu container for inicializado. Um Dockerfile deve conter apenas uma instrução CMD. Caso o Dockerfile tenha mais de um CMD, apenas o último será considerado.

Por exemplo, o seguinte comando vai fazer com que o container em execução imprima “Olá, mundo!” no terminal:

CMD ["printf", "Hello World", ">.conf/config"]

Para saber mais sobre as instruções que podem ser usadas no Dockerfile, acesse a documentação de referência do Dockerfile.

Um Dockerfile é como uma receita em um livro de receitas. Você pode reutilizá-lo para inúmeras aplicações semelhantes, fazendo apenas pequenas alterações quando necessário.

Conteinerizando uma aplicação

Agora que já sabemos um pouco mais sobre containers, vamos conhecer o registry de containers da AWS. O Amazon Elastic Container Registry, ou Amazon ECR, pode ser usado para armazenar nossas imagens de container de maneira pública ou privada. Uma das vantagens do ECR é que ele é compatível com Docker, assim, podemos usar os mesmos comandos do Docker CLI (como docker push ou docker login) para gerenciar nossas imagens no ECR.

Nos próximos passos, iremos conteinerizar uma aplicação e hospedá-la no Amazon ECR.

Pré-requisitos:

Caso essa seja sua primeira vez trabalhando com o AWS CLI, sugiro que você siga este guia para configurar seu ambiente local: https://aws.amazon.com/pt/getting-started/guides/setup-environment/

Passo a passo:

Vamos conteinerizar uma API desenvolvida em .NET 6 que retorna um texto passado como parâmetro através da URL. Ao conteinerizar esta API, iremos manter a consistência entre o nosso ambiente de desenvolvimento local e o ambiente na nuvem.

Criamos um repositório no GitHub com todo código necessário para seguir este passo a passo. Apenas certifique-se de que esteja usando o branch ep1 ao seguir os seguintes passos.

  • Primeiro, iremos clonar o nosso repositório base para termos acesso a todos os arquivos da API e também ao Dockerfile que será usado para criar nossa imagem de container.

git clone https://github.com/aws-samples/picture-social-sample --branch ep1

  • Vamos acessar o diretório do repositório que acabamos de clonar. A partir de agora, iremos executar todos comandos de dentro deste diretório.

cd picture-social-sample/HelloWorld

  • Antes de seguirmos adiante, vamos conferir se o Docker está instalado corretamente. O comando abaixo vai exibir a versão do Docker que você tem instalada. Certifique-se de que a sua versão do Docker seja equivalente a 10ou superior.

docker --version

  • Se você abrir o Dockerfile que está dentro do nosso repositório, vai encontrar todas as instruções necessárias para construir a imagem de container da nossa API .NET 6. Não se preocupe em entender todos os comandos do Dockerfile agora, muitos deles são específicos de aplicações .NET. Também não tenha medo de modificar esse arquivo e experimentar com os comandos. A melhor forma de aprender é testando, errando e encontrando soluções por conta própria.
  • Agora vamos criar nossa imagem de container usando o comando docker builde o parâmetro -t para especificar o nome e tag da nossa imagem no formato nome:tag. Por último, especificamos o caminho até o diretório do Dockerfile. Como já estamos no mesmo diretório em que se encontra o Dockerfile, usaremos . para especificar o diretório atual:

docker build -t helloworld:latest .

  • Caso você esteja usando um computador Mac com processador Apple Silicon, o comando para fazer o buildda imagem fica um pouco diferente, precisando indicar que estamos criando uma imagem que será executada em uma plataforma linux/amd64:

docker buildx build —platform=linux/amd64 -t helloworld:latest .

  • O próximo passo é executar nosso container usando o comando docker run. Vamos usar o parâmetro -dpara especificar que o container será executado em segundo plano. E também usaremos o parâmetro -p para fazer o mapeamento de uma porta na nossa máquina para uma porta no container. Assim, as requisições enviadas para aquela porta serão redirecionadas para o container. Como no nosso Dockerfile especificamos que o container está usando a porta 5111, vamos fazer o mapeamento para esta mesma porta na nossa máquina:

docker run -d -p 5111:5111 helloworld:latest

  • Com o nosso container em execução, podemos abrir o navegador na página http://localhost:5111/api/HelloWorld/mundoe veremos a mensagem “Hello mundo” na tela. Você pode mudar mundo na URL para modificar a mensagem retornada pela API.
  • Agora que nosso container está rodando corretamente e retornando as mensagens conforme esperado, vamos seguir adiante e fazer o upload da nossa imagem de container para um registry de containers privado. Para isso, vamos usar o AWS CLI para criar um repositório chamado helloworld no Amazon ECR:

aws ecr create-repository --repository-name helloworld

Depois de criado nosso repositório, precisamos descobrir qual é o fully qualified domain name (FQDN), ou nome de domínio totalmente qualificado, do nosso registry. Nos comandos a seguir, vamos passar a usar o endereço do nosso registry no ECR para que o Docker use a nossa imagem hospedada remotamente em vez da nossa imagem local. O endereço completo do seu registry terá o seguinte formato:

<aws_account_id>.dkr.ecr.<aws_region>.amazonaws.com

  • Por exemplo, se o meu AWS CLI foi configurado para usar a região us-east-1e o número da minha conta da AWS for 111122223333, o endereço do meu registry será dkr.ecr.us-east-1.amazonaws.com.
  • Com o endereço do nosso registry, vamos fazer o login no Docker CLI. Ao fazermos login no Docker, estamos dando permissão ao nosso ambiente local para enviar imagens ao repositório remoto no ECR:

aws ecr get-login-password --region <aws_region> | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<aws_region>.amazonaws.com

  • Antes de enviarmos nossa imagem para o repositório no ECR, precisamos alterar o nome dela para que inclua o endereço completo do nosso registry:

docker tag helloworld:latest <aws_account_id>.dkr.ecr.<aws_region>.amazonaws.com/helloworld:latest

  • Agora estamos prontos para enviar nossa imagem de container para o repositório que criamos no ECR:

docker push <aws_account_id>.dkr.ecr.<aws_region>.amazonaws.com/helloworld:latest

Se você chegou até aqui, significa que você conseguiu! Parabéns, você conteinerizou uma aplicação e hospedou ela em um repositório no Amazon ECR! 🙌

Também preparamos um vídeo com o conteúdo deste post e o passo a passo deste tutorial: bit.ly/3MvWA3B

Acompanhem os próximos posts desta série para aprender mais sobre containers, Kubernetes, Terraform e EKS.

 

Este artigo foi traduzido do Blog da AWS em Espanhol.

 


Sobre os autores

José Yapur é Senior Developer Advocate na AWS com experiência em Arquitectura de Software e é apaixonado por desenvolvimento especialmente em   .NET y PHP. Trabalhou como Arquiteto de Soluções por vários anos, ajudando empresas e pessoas na América Latina.

 

 

 

 

Ana Cunha é Developer Advocate na AWS para América Latina. Anteriormente, ela trabalhou como engenheira de desenvolvimento de software na Amazon.com.