Como otimizar uma aplicação a partir de conceitos básicos e computação em nuvem?

Publicado por

Publicado por

Publicado em

    Categorias:

    Desenvolvimento
 

Apesar da grande variedade de recursos modernos em nuvem existentes nos dias de hoje, ainda é comum vermos projetos hospedados em máquinas virtuais. Isso acaba demandando tempo considerável de configuração das máquinas, instalação de ferramentas e componentes, como sistema operacional, bancos de dados, certificados SSL/TLS, message brokers, cron jobs, entre outros.

O custo dessas máquinas pode ser alto a depender das configurações de memória, CPU e armazenamento contratadas. Além do custo da infraestrutura por si só, podemos ter ainda outros problemas:

  • Escalabilidade limitada ao tamanho da máquina (escalabilidade vertical);
  • Necessidade de time de infraestrutura para gerenciar os recursos;
  • Problemas de segurança devido à falta de atualização das ferramentas;
  • Indisponibilidade geral da aplicação devido a falhas em apenas um dos componentes.
Principalmente para times pequenos, esse tempo de configuração, instalação e manutenção de infraestrutura é muito valioso - poderia ser usado no desenvolvimento de novas features e correção de bugs, por exemplo.
 

Neste post demonstramos como seria possível modernizar uma aplicação Node.js aplicando conceitos básicos e ferramentas de computação em nuvem. As etapas abordadas serão:

 
  1. Aplicação exemplo: API CRUD de Usuários
  1. Database as a Service com MongoDB Atlas
  1. Dockerizando a aplicação
  1. Hospedando a API no Cloud Run integrado ao Github
  1. Testando a aplicação na nuvem
  1. Concluindo e sumarizando os conceitos

1. Aplicação exemplo: API CRUD de Usuários

A aplicação que vamos usar como exemplo é uma simples API HTTP de CRUD de Usuários desenvolvida em Node.js, localizada nesse repositóriodo Github. O branch main contem a aplicação completa.

 
💡
O foco do post não é o código-fonte da aplicação em si, portanto ela é apenas um exemplo simples e não preparada para produção.
 

Para prosseguir junto com o post, você deve fazer um fork desse repositório e adicionar à sua conta do Github, pois vamos utilizá-lo no passo 4. Você pode usar uma aplicação própria, deve funcionar também, desde que a porta exposta da sua aplicação seja acessada pela variável de ambiente PORT.

2. Database as a Service com MongoDB Atlas

Database as a Service (DBaaS) é um conceito de serviço de bancos de dados em nuvem. Nesses serviços, o banco de dados é gerenciado pela plataforma, sem depender de maiores configurações por parte de quem o contrata. De uma forma simplificada, você paga para uma plataforma gerenciar a infraestrutura do banco de dados, deixando-a pronta para uso em produção.

 
Existem prós e contras nesse modelo de uso de banco de dados, mas para times pequenos sem um especialista na área, pode fazer muito sentido, pois permite que o time atue mais no desenvolvimento do produto e menos em configurações de ferramentas.
 
notion image

A nossa aplicação utiliza MongoDB, logo, vamos utilizar a plataforma Atlas que oferece o serviço de DBaaS para Mongo. Acessando o link acima é possível criar uma conta e um projeto em um cluster gratuito de até 512MB de armazenamento - mais que suficiente para esse post, testes casuais e até para ambientes pequenos de desenvolvimento.

💡
Caso tenha problemas em criar seu cluster gratuito, assista esse vídeo do próprio time do MongoDB sobre como fazer a criação.

Com o cluster e projeto configurados no Atlas, basta obter a string de conexão (versão longa) do mesmo e adicioná-la em um arquivo denominado .env na raíz da nossa aplicação, assim:

MONGO_URI=mongodb://user:password@my-url
 

Agora a nossa aplicação está pronta para ser executada! Caso queira rodar alguns testes, no readme do repositório constam alguns comandos cURL para consumir os endpoints da API de forma simples.

3. Dockerizando a aplicação

Uma ferramenta muito utilizada nos dias de hoje é o Docker, que permite isolar aplicações em containeres. Containeres podem ser definidos como pacotes de software isolados que podem ser executados de forma confiável em qualquer ambiente ou sistema operacional. Se precisar realizar a instalação do Docker acesse esse link.

 

Para esse post, o que precisamos saber sobre o Docker é que vamos utilizá-lo para isolar nossa aplicação e todas as suas dependências em uma imagem para conseguirmos rodar em qualquer ambiente.

 
Nesse link o pessoal do Docker consolida de forma mais profunda conhecimentos e definições sobre Container, Imagem e comparações com Máquinas Virtuais - recomendo a leitura para aprofundamento dos conceitos.
 

Com o Docker instalado, vamos criar nossa imagem. Primeiramente vamos criar um arquivo chamado Dockerfile na raíz da aplicação com esse conteúdo:

FROM node:16

WORKDIR /app

COPY package-lock.json .
COPY . .

RUN npm ci

CMD ["npm", "start"]

Explicando cada linha do código acima:

  • FROM node:16 - declara a imagem base que vamos utilizar na construção da nossa própria;
  • WORKDIR /app - define o diretório base dentro do container que vamos utilizar. Pode ser qualquer outro nome, mas comumente é usado /app;
  • COPY package-lock.json . - copia o arquivo package-lock.json para diretório raíz do container - nesse caso o /app, pois definimos ele como diretório base;
  • COPY . . - copia todos os arquivos para o diretório raíz do container (mesmo comportamento do comando anterior);
  • RUN npm ci - instalação das dependências da aplicação;
  • CMD ["npm", "start"] - comando que será executado quando rodarmos o container - basicamente inicia a aplicação.

Com o Dockerfile configurado corretamente, agora podemos criar nossa imagem executando o seguinte comando: docker build . -t user-crud-api:1.0.0. Esse comando constrói uma imagem com base no Dockerfile que configuramos na raíz do projeto. Essa imagem vai se chamar user-crud-api tendo a tag 1.0.0. O comando deve retornar algo assim no console:

notion image

Se tudo deu certo, agora temos nossa aplicação encapsulada em uma imagem Docker, pronta para ser executada! Para conferir se ela está criada mesmo, rode o seguinte comando: docker image ls user-crud-api. Deve retornar a lista de imagens:

notion image
 

Para executar o container de fato, basta rodar o seguinte comando: docker run -p 3000:3000 user-crud-api:1.0.0.

notion image

Esse comando executa a imagem user-crud-api:1.0.0 e faz uma conexão da porta 3000 da nossa máquina com a porta 3000 do docker através da flag -p. Por questões de segurança, o Docker não expõe as portas do container, por esse motivo é necessário usar a flag.

Podemos testar nossa aplicação criando um usuário com o comando: curl -d '{"name": "Limoni", "email": "limoni@mail.com"}' -H "Content-Type: application/json" -X POST e depois listando os usuários: curl http://localhost:3000/user.

notion image
 

Pronto, temos nossa aplicação rodando dentro de uma imagem Docker completamente isolada do resto do sistema operacional!

4. Hospedando a API no Cloud Run integrado ao Github

Agora que temos nossa aplicação encapsulada em um container, vamos fazer ela rodar na nuvem! Nesse post vamos usar o Google Cloud Run e precisaremos criar uma conta e um projeto no Google Cloud Platform (GCP) com uma opção de pagamento configurada obrigatoriamente (pode ser que precise de um cartão de crédito para prosseguir - não vai gastar nada, prometo). Nesse link você consegue criar uma conta usando seu e-mail do Google.

 
💡
É bem simples e direto criar projetos no GCP. Mesmo assim, o Google disponibiliza uma documentação completa para melhor entendimento dos conceitos nesse link.
 

Basicamente, o Cloud Run é um recurso do GCP que permite fazermos a hospedagem de aplicações via imagens de containeres Docker, totalmente auto-gerenciado. Você só paga pela computação usada e existe uma cota gratuita bem generosa que você pode conferir nas precificações do GCP.

Esse tipo de serviço é conhecido como serverless ou Platform as a Service (PaaS) que, assim como no DBaaS, você paga para uma plataforma gerenciar sua infraestrutura básica, podendo focar mais no desenvolvimento das features.

 
💡
Aqui existe uma documentação completa sobre como fazer deploys de aplicações variadas utilizando o recurso, vale a pena dar uma lida. Para mais informações sobre PaaS e seus pares, confira esse documento do Google.
 

Para configurar o Cloud Run, o jeito mais simples é integrando-o diretamente com o Github e deixar que o Cloud Build, ferramenta de build de aplicações em nuvem do GCP, se encarregue de gerar a imagem docker e realizar o deploy para a gente. A seguir, temos um passo a passo de como configurar o Cloud Run com o Github e Cloud Build, confira:

Primeiro, acesse a página do Cloud Run e escolha a opção para criar um novo serviço. Escolha um nome para seu serviço, selecione a segunda opção e clique em SET UP WITH CLOUD BUILD.

 
notion image

Depois, ative a API do Cloud Build clicando em ENABLE CLOUD BUILD API.

notion image

Feito isso, clique em Manage connected repositories. Deve aparecer esse quadro para se autenticar com o GitHub. Se autentique como de costume com o OAuth do Github. Logo após a autenticação, será solicitado também a instalação do cloud build nos seus repositórios. Escolha o repositório que você fez o fork no passo 2 e segue o baile.

notion image

Com o Cloud Build instalado no repositório, ele deve aparecer na listagem para selecioná-lo. A configuração do repositório com Cloud Build deve ficar parecida com a seguinte imagem:

notion image

Nas configurações de Build, configure como no print a seguir. Basicamente, estamos dizendo que todo push no branch main realizará um deploy utilizando o Dockerfile que configuramos nos passos anteriores.

notion image

Salvando essa configuração do Github, basta configurarmos a variável de ambiente da URL do MongoDB, na seção marcada em vermelho:

notion image

Feito isso, basta salvar o formulário para a criação do serviço.

Logo que o serviço for criado, você verá uma tela com as configurações das revisões do mesmo. Se tudo der certo, após alguns instantes sua tela deve ficar parecida com o print a seguir:

notion image

Repare que temos uma URL para acessarmos, ela já é a nossa aplicação hospedada na nuvem utilizando o recurso Cloud Run do GCP!

💡
Existem muitas outras configurações interessantes que o Cloud Run oferece, leia a documentação do recurso do GCP para maiores detalhes. A ideia aqui é apenas dar um empurrão inicial.

5. Testando a aplicação na nuvem

Com a nossa aplicação hospedada, podemos executar as requisições HTTP como fizemos localmente, porém apontando diretamente para a URL que o Cloud Run gerou para a gente. O arquivo README.md possui exemplos de requisições do CRUD de usuários. O GIF abaixo exemplifica os testes da aplicação:

notion image

Conclusão

Nesse post pudemos ver como modernizar uma aplicação Node.js utilizando vários recursos serverless em nuvem, como Database as a Service e Platform as a Service por meio de integrações rápidas e simples entre Github, Cloud Build e Cloud Run.

 
💡
Ao final do passo a passo, lembre-se de apagar seu projeto ou o serviço do Cloud Run para evitar gastos que possam ser gerados. Para maior entendimento com relação a precificação, acesse a documentação do GCP.
 

É importante salientar que essa configuração específica funciona muito bem para uma API HTTP stateless que não precise de processamentos longos, isso não é uma bala de prata!

Entretanto, o passo a passo deve ajudar muitos cenários e principalmente times pequenos que não têm muito tempo para gastar em configurações e atualizações de infraestrutura.

 

Existem outros provedores de serviço em nuvem além do GCP, como AWS, Azure, IBM, entre outros. Todos eles possuem recursos parecidos que facilitam a vida e os processos de desenvolvimento de software. Fica a critério de cada pessoa, time ou empresa optar por um ou outro.

 

O ponto fundamental é: podemos fazer muito mais com muito menos tempo se soubermos utilizar o básico dos recursos modernos que esse universo da computação em nuvem nos oferece!