Como otimizar uma aplicação a partir de conceitos básicos e computação em nuvem?
Publicado por
Gabriel Limoni
Publicado por
Gabriel Limoni
Publicado em
Categorias:
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:
- Aplicação exemplo: API CRUD de Usuários
- Database as a Service com MongoDB Atlas
- Dockerizando a aplicação
- Hospedando a API no Cloud Run integrado ao Github
- Testando a aplicação na nuvem
- 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.
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.
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.
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 arquivopackage-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:
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:
Para executar o container de fato, basta rodar o seguinte comando: docker run -p 3000:3000 user-crud-api:1.0.0
.
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
.
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.
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.
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.
Depois, ative a API do Cloud Build clicando em ENABLE CLOUD BUILD API.
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.
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:
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.
Salvando essa configuração do Github, basta configurarmos a variável de ambiente da URL do MongoDB, na seção marcada em vermelho:
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:
Repare que temos uma URL para acessarmos, ela já é a nossa aplicação hospedada na nuvem utilizando o recurso Cloud Run do GCP!
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:
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.
É 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!
Publicado por
Gabriel Limoni
Tech Leader e Desenvolvedor Fullstack, ajudando pessoas a entregar valor por meio da tecnologia.
Tech Leader e Desenvolvedor Fullstack, ajudando pessoas a entregar valor por meio da tecnologia.