idvogadosorg / infra

repositório para infraestrutura
4 stars 2 forks source link

Definições de Infra/Custo #7

Closed deniojunior closed 4 years ago

deniojunior commented 4 years ago

Resumo

Precisamos definir em qual PaaS de Cloud Computing iremos hospedar os nossos serviços. Esta decisão, deve estar diretamente relacionada com custos. Portanto, proponho centralizarmos aqui um estudo para fazermos esta definição. Além disso, fica o espaço aberto para sugestões caso alguém da comunidade já tenha algum tipo de experiência positiva em algum PaaS específico.

Acredito eu que o custo não deve ser o único fator de decisão, mas deve ser o fator mais importante.

Sugestões iniciais

Para iniciar a discussão, gostaria de deixar algumas propostas para serem discutidas:

Backend

Como o nosso backend estará como container Docker, acho que pode ser uma boa ideia rodar a aplicação em um orquestrador de containers. Assim, conseguiremos ter escalabilidade, resiliência e disponibilidade da aplicação.

Uma sugestão inicial seria o uso do Kubernetes. Listei abaixo alguns SaaS:

Em ambos os serviços citados não existe custo da engine do Kubernetes, o custo seria de Computing (cluster).

Em relação à custo, nenhum dos 2 são gratuitos, eu particularmente não conheço e acho improvável de haver algum serviço que irá prover isso gratuitamente, mas em a vantagem do Google em relação ao Digital Ocean é o preço em Real.

Caso a gente consiga alguma(s) instância(s) a um custo viável (mesmo sendo on-premise), podemos configurar o Kubernetes nesta máquina. O esforço para isso seria infinitamente maior, mas como já mencionado, custos é um fator muito importante para a viabilização do projeto

Database

Conforme discutido em no chat no discord, inicialmente a ideia é utilizar um API Database. Com isso, não precisamos nos preocupar com uma infra de um banco de dados, que não é trivial.

A ferramenta proposta inicialmente é o Firebase Realtime Database.

É importante acompanharmos as discussões e nos certificarmos que a estratégia será essa realmente do uso de um API Database.

Frontend

Em relação ao frontend, iremos precisar de um local para armazenar os arquivos de distribuição.

Acredito que não teremos grandes custos com Storage/DataOut, dado o tamanho da aplicação. Como sugestão, deixo apenas esta abaixo, mas espero realmente que possamos discutir melhor este ponto.

Além disso, podemos discutir sobre a utilização de uma camada de CDN para a entrega do frontend. O uso do CDN aumenta a performance da nossa aplicação web e reduz o custo com data out do local em que o nosso front estiver armazenado. No entanto, é necessário também levar em consideração os custos que a utilização de um CDN traria. Algumas sugestões:

rodrigondec commented 4 years ago

Podemos utilizar PaaS gratuições que possuam limitação em um momento inicial para testes. Por exemplo o https://www.heroku.com/ (uma conta só pode ter uma aplicação 'up' por 1000h mensais)

image

Eu mesmo tenho algumas aplicações lá.

rodrigondec commented 4 years ago

O @pictos mencionou a Azue. Aparentemente tem algum plano gratuito para Open Source (ou algo assim)

pictos commented 4 years ago

Opa eis a referência https://azure.microsoft.com/en-ca/blog/announcing-azure-pipelines-with-unlimited-ci-cd-minutes-for-open-source/

pabrrs commented 4 years ago

Bons pontos levantados @deniojunior, gostaria de contribuir com minha visão.

Backend

Acho que partir para uma estratégia de orquestração agora talvez não seja o momento, como o @rodrigondec, para o MVP podemos testar o Heroku pois ele atende bem o que queremos e no futuro migrar para alguma coisa mais flexível, quando o projeto estiver mais estruturado e as ideias já validadas.

Database

Penso que a decisão de usar um Database gerenciado por um terceiro seja a melhor decisão agora, para facilitar o inicio da coisa. O firebase é bem usado pela comunidade e acho que é uma boa escolha. A estruturação do código não deve sofrer muito com a mudança do banco de dados, considerando as escolhas que fizemos para modelagem da arquitetura de backend.

Outra sugestão que gostaria de trazer é o MongoDB, acho que ele é bem simples de usar também e largamente utilizado na comunidade. Caso optemos pelo Heroku pro backend, podemos usar o Addon mLab que possui um plano gratuito.

Frontend

Assim como o backend, talvez o proprio Heroku possa ser a plataforma, assim teríamos todos os projetos lá (API e UI). Acho que já seria suficiente para iniciarmos.

Sobre CDN, se optarmos por utilizar o Heroku, podemos usar um Addon no projeto de Front que ativa uma CDN, a exemplo do Edge (infelizmente ele é pago, $5/mês, não sei se existe algum gratuito)

deniojunior commented 4 years ago

@pabrrs concordo com os pontos que você levantou, realmente para um MVP não precisamos de muita robustês, senão o projeto não sai do lugar hehe.

Achei ótimo as suas sugestões, eu realmente não tenho experiência com o Heroku, por isso não cheguei a falar dele, mas pelos pontos que você levantou acho super válido. Por mim, podemos partir do uso do Heroku mesmo, só acho importante antes de bater o martelo realizar a seguinte análise:

E a partir desta análise, a gente discute a viabilidade. Dependendo do custo, conseguimos manter com doações/vaquinhas mesmo.

deniojunior commented 4 years ago

Aproveitei para dar uma olhada no Terraform em relação à issue #5, e confirmei que o Terraform possui o Heroku como provider também, o que é um outro ponto positivo caso seja essa a escolha final.

Link: https://www.terraform.io/docs/providers/heroku/index.html

deniojunior commented 4 years ago

@rodrigondec e @pictos, pelo que entendi, o que a Azure provém é uma ferramenta para CI grátis para projetos open source, e não os SaaS que a gente precisa para hospedar o projeto, certo?

Vi que as discuções sobre CI estavam rolando no repositório de backend. Não consegui acompanhar tudo nos últimos dias, então não sei se já definiram, caso não tenham definido, acho uma ótima opção o Azure Pipeline.

rodrigondec commented 4 years ago

Vi que as discuções sobre CI estavam rolando no repositório de backend. Não consegui acompanhar tudo nos últimos dias, então não sei se já definiram, caso não tenham definido, acho uma ótima opção o Azure Pipeline.

Foi escolhido o Actions para os repositórios de desenvolvimento. Inclusive já possuem automações (para o próprio kanban do repositório).

@rodrigondec e @pictos, pelo que entendi, o que a Azure provém é uma ferramenta para CI grátis para projetos open source, e não os SaaS que a gente precisa para hospedar o projeto, certo?

Sim, eu tbm tinha entendido que a Azure forneceria o SaaS.

Sobre o CI o github também fornece o actions gratuitamente para Projetos Open Source.

Uma vantagem que eu gosto do Actions é que podemos criar nossos próprios scripts/actions (um script JS ou então um docke rodando qualquer coisa) r e executa-los.

Mas se acharmos limitações no Actions do github acho válido utilizar o da Azure ou fazer esse levantamento

pabrrs commented 4 years ago

Gostaria de sugerir algo :detective:

Fazendo uma conta de padaria e cometendo algumas liberdades criativas, cheguei nos seguintes números:

Público alvo

Uma pesquisa de março/2019 aponta que existem cerca de 4 milhões de entregadores trabalhando nos aplicativos de entrega populares. Em pesquisa de Setembro/2018, a Exame levantou que no Brasil existem cerca de 1,1 milhão de advogados registrados na OAB. Assim sendo, nosso púlico alvo soma 5,1 milhões de pessoas, entre entregadores e advogados.

Temos no Brasil 211 milhões de habitantes, segundo IBGE. Dessa forma, cerca de 2,4% dos brasileiros estão entre os entregadores e advogados que queremos atingir.

Amostragem

O vídeo do Greg News sobre Delivery, no momento desse levantamento, possui 1.067.117 visualizações.

image [Fonte: Greg News - Delivery | 12/05/2020]

Considerando que, da massa de pessoas que visualizaram o vídeo, 2,4 % representa o nosso público alvo, temos então cerca de 25.610 usuários interessados na plataforma (claro, assumindo que todos os 2,4 % vão ao menos enviar o seu email, atestando interesse).

Simulando custo de Banco de dados

Imaginando que iremos armazenar inicialmente apenas o email dos interessados, em um modelo hipotetico da collection do mongo, teríamos três campos: o _id padrão da collection,email e o tipo de usuário, se é entregador ou advogado. Portanto o documento no mongo seria parecido com:

{
    "_id": "5eba0ff87c213e5d2fa84f72",
    "type": 1, // 1 - entregador | 2 - advogado
    "email": "umsuperentregador@umsuperdominio.com.br"
}

Considerando esse dado gravado no mLab do heroku (extensão para mongodb), temos o seguinte panorama de armazenamento:

image [Fonte: Dados coletados do painel de estatísticas do mLab]

Se para cada documento armazenado temos o custo de dataSize = 0,42 KB, para a nossa amostragem de 25.610, teríamos 10756,2 KB ou 10,7562 MB de armazenamento. O plano gratuito do mLab suporta até 496 MB de uso, atendendo assim a demanda inicial.

Simulando custo da API

Traduzindo em requests na página inicial, teríamos a request de acesso a página e o envio dos dados no formulário, dessa forma duas requests. Então se todos os usuários levantados acessarem e enviarem seu email, teremos cerca de 51.220 requests. Diluindo esse volume por uma semana de acesso, teríamos por volta de:

Em um exemplo bem simples, usei o seguinte código em NodeJS, simulando o comportamento de uma API que lê dados da request e persiste no mongo:

const express = require('express')
const mongoose = require('mongoose')

mongoose.connect('mongodb://127.0.0.1:27017/root')
const User = mongoose.model("user", new mongoose.Schema({ email: 'string', type: 'number' }))

const app = express()
app.use(express.json())

const formatUsage = usage => {
  return `${Math.round(usage / 1024 / 1024 * 100) / 100} MB` 
}

app.post('/form', async (req, res) => {
  try {

    const { email, type } = req.body
    await User.create({ email, type })
    const memUsage = process.memoryUsage()

    res.json({
      heapUsed: formatUsage(memUsage.heapUsed)
    })
  } catch (err) {
    console.error({ err })
    res.json({ message: err.message })
  }
})

app.listen(3000, console.log('listen on 3000'))

Através do método process.memoryUsage(), é possível obter números de estatísticas do uso de memória do NodeJS, para a operação efetuada. Então, fazendo uma requisição simples, temos:

curl -XPOST "http://localhost:3000/form" -H 'content-type: application/json' -d'{ "email": "umsuperentregador@umsuperdominio.com.br", "type": 1 }'
{
  "heapUsed":"17.48 MB"
}

O valor que nos interessa desse resultado é heapUsed. Logo, para cada 5 request/minuto , teríamos um consumo médio de 87.40 MB/minuto em memória. O plano básico do heroku para aplicações web, chamado de Free Dyno, suporta até 512 MB de memória, dessa forma usaríamos em torno de 18 % memoria/minuto, considerando a amostragem.

Conlusões

Bem, novamente digo que esses números são extremamente simplificados com o objetivo de gerar reflexão e elucidar alguns pontos sobre o levantamento dos recursos de backend que iremos precisar nesse momento. Não é o cenário mais acurado obviamente, mas pode talvez ajudar a nortear um cálculo mais preciso.

:tada:

dhulke commented 4 years ago

A heap é única pra um processo e contém muita coisa além do que foi alocado no código js, então acredito que múltiplos requests para uma instância node não irão adicionar tudo aquilo de tamanho na heap. Estamos bem tranquilos quanto a memória.

Também não acho que todos os 2,4% que assistem o Greg concordem com ele e imagino que uma porcentagem muito menor terá interesse em engajar com o projeto, por isso esses números seriam em tese muito menores.

Do ponto de vista financeiro acho que é uma boa notícia. O Greg poderia fazer propaganda em seu canal que conseguiríamos aguentar a carga.

pabrrs commented 4 years ago

@dhulke concordo com você, a intenção é sempre errar para mais.

deniojunior commented 4 years ago

Possibilidades

Pessoal, estou fazendo um estudo com algumas propostas de arquitetura e vou documentar 3 possibilidades diferentes, são elas:

deniojunior commented 4 years ago

Elastic Web Server - AWS

Para a primeira proposta, construí um diagrama considerando a cloud da AWS, dado que tenho mais experiência com ela e posso falar com mais segurança. Posteriormente, é possível avaliar a mesma arquitetura e clouds diferentes para comparação de preços.

iDvogados - Infra

A proposta de infra apresentada foi criada dado um cenário ideal para aguentar um projeto robusto, de forma que esta infra conseguiria suprir as necessidades do projeto no seu auge, tendo apenas que mudar o tamanho das instâncias, se necessário.

A infra apresentada garante: escalabilidade, tolerância a falhas, alta disponibilidade e segurança.

Segurança

A proposta apresenta a infra dentro de uma VPC personalizada, a qual estaria linkada à internet por meio de um Internet Gateway. O router faria o encaminhamento do tráfego na VPC interna dado as regras que forem definidas.

Teremos duas Subnets, uma pública e uma privada. Na subnet pública se encontraria o Application Load Balancer (ALB) (que fará o balanceamento de tráfego nas instâncias) e o NAT Gateway com seu Elastic IP atribuído (é necessário).

Com a divisão das duas subnets, garantimos que a internet externa não terá acesso aos componentes que estão na subnet privada, que no caso são as instâncias e o banco de dados. A comunicação é feita apenas com os componentes da subnet pública, os quais comunicam com a subnet privada e fazem o retorno da request. Além disso, podemos incluir diversas regras no Network ACL e Security Groups permitindo apenas requisições HTTP e HTTPS nas portas 80 e 443.

Como a internet externa não terá comunicação com a subnet privada, é necessário garantir que a subnet privada tenha acesso à internet externa, pois a própria aplicação pode necessitar de realizar essa comunicação. Por exemplo, no momento de subir as instâncias, as mesmas precisam baixar a imagem docker da aplicação e para isso elas precisam de acesso à internet. É por este motivo que existe o NAT Gateway, o qual garante que os componentes da subnet privada consigam acessar a internet externa, mas a internet externa não tenha visibilidade destes componentes, garantindo mais proteção às nossas instâncias e ao nosso banco de dados.

Escalabilidade

Com a inclusão das instâncias em um Auto Scaling Group (ASG), conseguirmos aumentar e diminuir o número de instâncias de acordo com o tráfego. Os eventos do Cloudwatch de Network raise e lower disparam este processo. Como forma de controle, definimos o mínimo e o máximo de instâncias que queremos, bem como o número de instâncias desejadas em um tráfego normal.

Disponibilidade e Tolerância a falhas

Foram incluídas duas zonas de disponibilidades na infra, isso quer dizer que teríamos as instâncias em lugares fisicamente distintos no data center da AWS, garantindo que caso haja algum tipo de manutenção ou indisponibilidade em alguma zona do data center, não teremos indisponibilidade na aplicação.

Além disso, o uso do Application Load Balancer, faz com que o tráfego seja balanceado entre as instâncias e com o uso do Auto Scaling, novas instâncias são criadas para lidar com o aumento do tráfego, garantindo mais disponibilidade da aplicação.


Os demais componentes criados listados no Diagrama são:

Bucket S3

O bucket S3 é necessário para o armazenamento dos logs e para armazenar o Terraform State, o arquivo que provém o estado atual da nossa infra na cloud. Como este arquivo contém informações sensíveis, como as chaves utilizadas na criação da infra, é necessário que este arquivo seja salvo de forma encriptada.

CloudWatch Alarms

No Cloudwatch criaremos alguns alarmes para nos avisar quando algo na aplicação aparentemente não está normal. Podemos linkar o aviso com apps terceiros, como o Pager Duty, por exemplo.

Route 53

No Route 53 podemos fazer o gerenciamento DNS e lá, criaríamos um registro CNAME apontando o domínio api.idvogados.com.br para o DNS do ALB, disponibilizando, portanto, a API por meio deste domínio.

Outras considerações

Free Tier

Fiz uma análise da camada gratuita da AWS para ver se ela atende essa infra apresentada:

Free Tier por 1 ano.

Proposta: Criar uma conta nova a cada ano e realizar a migração. Dado que a infra é construída com Terraform, basta abrir uma janela de manutenção, fazer o dump do banco, recriar a infra na outra conta, fazer o import do banco e transferir os arquivos de log. Assim conseguimos manter o Free Tier sempre.

Amazon EC2 :x:

750 horas por mês de uso de instância t2.micro Linux, RHEL ou SLES Dá pra ter 1 instância rodando por 1 mês inteiro.

Elastic Load Balancing :x:

750 horas por mês 15 LCUs para Application Load Balancers

Amazon RDS (SQL) :x:

750 horas por mês de uso do banco de dados db.t2.micro.

Serviço de banco de dados relacional gerenciado para MySQL, PostgreSQL, MariaDB, Oracle BYOL ou SQL Server.

20 GB de armazenamento de banco de dados de uso geral (SSD) 20 GB de armazenamento para backups de banco de dados e DB Snapshots

Amazon DynamoDB (NoSQL) :heavy_check_mark:

25 GB de armazenamento

25 unidades de capacidade de gravação (WCU) provisionada 25 unidades de capacidade de leitura (RCU) provisionada Suficiente para processar até 200 milhões de solicitações por mês.

Amazon S3 :heavy_check_mark:

5 GB de armazenamento padrão 20.000 solicitações GET 2.000 solicitações PUT

Amazon SQS :heavy_check_mark:

1 milhão de solicitações

Amazon CloudFront :heavy_check_mark:

50 GB de transferência de dados para fora 2.000.000 de solicitações HTTP ou HTTPS

Amazon CloudWatch :heavy_check_mark:

10 métricas personalizadas e alarmes 1.000.000 de requisições de API 5 GB de ingestão de dados de log e 5 GB de arquivos de dados de log 3 painéis com até 50 métricas por mês para cada um deles

Análise de Custos que extrapolam o Free Tier

Amazon EC2 e Amazon RDS (SQL)

As instâncias de Banco de dados RDS e de EC2 possuem apenas 750 horas por mês considerando a camada gratuita da AWS. Como um mês possui 730 horas, isto nos dá praticamente apenas 1 instância de DB e 1 instância de EC2 ligada durante todo o mês.

As instâncias gratuitas da AWS também são limitadas, de forma que as instâncias do DB devem ser db.t2.micro e as instâncias do EC2 devem ser t2.micro. Os recursos que estas instâncias provém, são os mesmos:

Nome vCPUs RAM (GiB) Créditos de CPU/h Preço sob demanda/hora* Instância reservada por 1 ano – por hora* Instância reservada por 3 anos – por hora*
db.t2.micro 1 1,0 6 0,0116 USD 0,007 USD 0,005 USD
t2.micro 1 1,0 6 0,0116 USD 0,007 USD 0,005 USD

Caso optemos pelo uso de um banco de dados não relacional podemos utilizar o DynamoDB, o qual possui uma SDK para javascript, fazendo todo o gerenciamento de dados por meio de uma API e que a camada free tier é suficiente para o projeto.

Ao meu ver, o uso do DynamoDB seria mais viável para o projeto do que manter o RDS.

Com estes recursos, garanto que conseguimos subir a aplicação utilizando apenas a camada gratuita da AWS, porém não acredito que estes recursos serão suficientes para aguentar assim que o APP for lançado e divulgado.

Nesse caso, para ter certeza do custo extra que teríamos, seria necessário realizar um teste de carga na nossa aplicação, considerando todos os endpoints.

Enfim, considerando o uso do DynamoDB (NoSQL) ao invés do RDS e o uso de de 1 instância EC2 na camada gratuita mais 3 instâncias EC2 t2.micro para o projeto por mês, teríamos um custo de $8,46 por instância, totalizando $25,3 (dólares) por mês.

Elastic Load Balancing

O ELB probém 750 horas de processamento, ou seja, conseguiríamos ter 1 ELB rodando na camada gratuita. Como a proposta de infra foi feita com base em duas zonas de disponibilidades, teríamos que arcar com o custo de um outro ELB que estaria fora da camada gratuita.

Em relação às 15 LCUs disponibilizadas, é possível perceber conforme a documentação da AWS que é o suficiente para a nossa aplicação. Joguei alguns valores fazendo uma conta bem simples, de uma estimativa de início de projeto e o custo de um segundo ELB seria de $2.92 (dólares).

image

Considerei o processamento de 500MB por hora, 60 requisições por minuto e média de 2 segundos para processar cada requisição.

A estimativa de RMP que o @pabrrs fez é de 5 RPM, considerei 60 para jogar pra cima e ter mais segurança.

Conclusões

Adaptando para Free Tier

Dado o que foi apresentado, conseguimos encaixar esta infra na camada free da AWS realizando algumas alterações, são elas:

Dessa forma, a proposta inicial para construir uma infra gratuita seria:

iDvogados - Infra Free Tier

Infra completa

Com o crescimento do projeto, poderíamos fazer o upgrade da infra para a arquitetura abaixo com um custo mensal conforme abaixo:

ALB: $2.92 EC2: $25.3

Total: $28.22 / mês

Reais: R$138,00 / mês

A infra completa seria conforme o diagrama abaixo:

iDvogados - Infra Complete

:warning: OBS: É necessário realizar um teste de carga em cima das instâncias t2.micro com a nossa aplicação para garantir quantas instâncias seriam necessárias. Este valor levantado está considerando 4 instâncias.

pabrrs commented 4 years ago

[AWS] Elastic Web Server

@deniojunior Muito bom mano, parabéns 🚀 🎉

Tenho alguns pontos a considerar:

Docker registry

Sobre o registry para o Docker, podemos utilizar o registry que o Github fornece, facilitando assim a centralização dos nossos recursos no Github (código, CI/CD, registry docker, tarefas, etc).

Dynamo vs RDS

O Dynamo possui uma interface para consumo de dados bem particular da sua implementação, que difere bastante do que o MongoDB ou outros bancos JSON-like entregam. Fazer queries no Dynamo não é tão trivial assim e o jeito como os dados são entregues após uma leitura, requer uma transformação um pouco chatinha de se fazer.

Não vejo problema algum em utilizarmos Dynamo se for o melhor no quesito dimdim 💸, mas é um ponto de atenção para o time de @idvogados/backend, que deverá criar uma interface alto nível na API para utilizar o Dynamo por debaixo dos panos e tornar o desenvolvimento o mais confortável possível.

Instancia / Deploy

Fiquei na dúvida sobre como seria feito o deploy na instância, iriamos utilizar a instancia 'bare-metal' com algum processo que ativaria o docker pull do registry ? De outra forma, iremos para uma solução como AWS Elastic Beanstalk ou AWS EKS ?

Alertas do Cloudwatch

Acredito que possamos integrar os alarmes do Cloudwatch com o Discord do Idvogados, através de webhooks. Sei que serviços como Zappier e Automate.io já fazem isso com auxilio de uma interface, mas podemos desenvolver um bot (ou usar algum já pronto) que receba uma notificação do Cloudwatch e poste em um canal (somente leitura, com acesso restrito) a mensagem de erro.

Infelizmente o PagerDuty não possui um plano free 😢, então podemos tentar economizar nesse quesito.

deniojunior commented 4 years ago

@pabrrs ótimas considerações, valeu mesmo!

Docker registry

Concordo demais em utilizar o do Github! Fica bem centralizado. Não tinha vindo ele na minha cabeça hehe

Dynamo vs RDS

Esse ponto que você levantou é muito importante mesmo. Vou fazer as demais propostas de arquitetura aqui também, aí a gente discute o que pode ficar melhor pro projeto. Caso essa primeira proposta seja a escolhida, podemos avaliar também a possibilidade de ter uma infra meio híbrida. Tipo, subir as instâncias na AWS mas utilizar o banco Mongo com o mLab ou o Atlas. Inclusive, tem essa documentação da própria AWS sobre o Atlas também:

Eu particularmente prefiro deixar a infra toda centralizada, mas lógico temos que escolher o que for melhor para o projeto. Se a gente optar por esta primeira arquitetura, a gente abre uma discussão sobre o assunto levantando prós e contra.

Instancia / Deploy

Então, nessa proposta seria cada instância rodar a aplicação por meio da imagem Docker. Aí nesse caso, para realizar o deploy de uma nova versão, iríamos subir uma nova instância com a versão mais recente da imagem docker e matar a instância com a versão antiga. Faz sentido pra você?

Isso pode ser feito de forma simples num scriptzinho python (com a SDK da AWS) que pode ser executado no GH Actions assim que a nova imagem Docker for publicada. É bem simples!

Alertas do Cloudwatch

É uma ótima ideia integrar com o Discord, não tinha pensado nessa possibilidade. Boa mesmo! Tô anotando esse tipo de sujestão num documento aqui pra passar pra uma issue e montar um diagrama de todo o fluxo desde a contribuição até a operação.

É uma pena o Pager Duty não ter plano free :cry:

deniojunior commented 4 years ago

Serverless - AWS / GCP

A arquitetura Serverless seria a mais simples para subirmos uma aplicação, no entanto, eu acredito que ela pode não ser vivável dado a forma que estamos construído o back. Uma aplicação serverless deve serguir um boilerplate espefícico considerando esta arquitetura, e como já discutimos e montamos um boilerplate, não acredito que seja tão viável. Por outro lado, acho importante levantar a possibilidade pois pode ser uma alteranativa que supra bem as necessidades do projeto.

A ideia da arquitetura serverless é realmente a gente não precisar se preocupar com um servidor. Apenas especificamos o código que deve ser executado dado um endpoit mapeado. Como opções, eu conheço o AWS Lambda e o Google Cloud Functions, mas existem também soluções da Azure, IBM e outros.

Ambos possuem suporte para Node, então nesse caso, o que faríamos é esse mapeamento. Exemplo:

GET /users: executa código Node que retorna uma listas de usuários. POST /login: executa código Node que avalia um email e senha para um login

Assim, não precisamos nos preocupar com manter um servidor, com escalabilidade e coisas do tipo.

No entanto, esta arquitetura mudaria a forma como desenvolvemos o backend. Não teríamos a aplicação como uma imagem docker, com Apollo Server, GraphQL... enfim, é uma outra arquitetura que precisa que o desenvolvimento da aplicação seja voltado para essa arquitetura.

Neste contexto, a aplicação tem que ser stateless.

Para que uma aplicação inteira Serverless rode bem em produção é interessante utilizar um Orquestrador para que seja possível integrar seu deploy, multistage, testes a qualquer pipeline de entrega contínua, cobrindo muita configuração manual que deveria que ser feita conforme a aplicação escala. Uma sugestão neste ponto é o Serverless Framework

Deixo aqui também um boilerplate de um projeto serverless

AWS Lambda + API Gateway

A AWS possui o Lambda que junto do API gateway fornece uma arquitetura Serverless. No Lambda ficam as funções (códigos) a serem executados. No API Gateway é onde é feito o mapeamento, isto é, qual função lambda (código) deve ser executado quando é realizada uma requisição no enpoint X.

Nesta arquitetura é possível também utilizar um banco de dados, no caso, o DynamoDB pode ser utilizado, sendo bem comum o seu uso neste tipo de arquitetura na AWS. Porém, como o @pabrrs comentou acima, é necessário avaliarmos se o DynamoDB atende bem as necessidades da aplicação.

Em relação a segurança, é importante ter uma VPC da mesma forma que foi proposta na arquitetura Elastic Web Server supracitada, tendo as funções lambdas na subnet privada. Além disso, com o crescimento do projeto, é possível adicionar uma outra camada de segurança, caso necessário: o AWS WAF. Este serviço não está na camada gratuita da AWS, mas o preço não é tão elevado e poderia ser considerado num futuro, inclusive pode ser considerado para a arquitetura Elastic Web Server também.

Diagrama

O diagrama que representa essa arquitetura é parecido com o diagrama apresentado, no entando, é possível perceber que não temos preocupações com o servidor ou com escalabilidade, reduzindo o número de alarmes e eventos do cloudwatch.

PS: O diagrama exibe 3 funções lambda, mas não existe limite, podemos ter quantas forem necessárias.

Serverless

Free Tier

A infra proposta se encaixa na camada free tier da AWS, sendo portanto viável, conforme abaixo:

Lambda :heavy_check_mark:

1.000.000 solicitações gratuitas por mês Até 3,2 milhões de segundos de tempo de computação por mês

API Gateway :heavy_check_mark:

1 milhão de chamadas à API recebidas por mês

Amazon CloudFront :heavy_check_mark:

50 GB de transferência de dados para fora 2.000.000 de solicitações HTTP ou HTTPS

Amazon DynamoDB (NoSQL) :heavy_check_mark:

25 GB de armazenamento

25 unidades de capacidade de gravação (WCU) provisionada 25 unidades de capacidade de leitura (RCU) provisionada Suficiente para processar até 200 milhões de solicitações por mês.

O levantamento feito pelo @pabrrs acima, chegou em um número de 5 RPM (requesições por minuto). Um mês tem 43.800 minutos. 43.800 x 5 = 219.000 requisições por mês, estando longe no limite mensal de 1.000.000 de requisições.

Neste caso, a nossa infra suportaria no máximo um fluxo corrente de 22 RPM na camada gratuita da AWS.

Google Cloud Functions

O diagrama proposto utilizando a AWS pode ser seguido da mesma forma no GCP, com algumas particularidades.

Inicialmente, é importante relatar que é possível criar as configurações de Network mantendo uma VPC com subnets publicas e privadas e um NAT Gateway da mesma forma no GCP. Os produtos e a forma de criá-los/usá-los são diferentes, porém os conceitos e os componentes na criação da rede se mantém. (NAT Gateway no caso é Cloud NAT no GCP).

O Google Cloud Functions traz algumas diferenças em relação ao Lambda + API Gateway. O Cloud Functions não precisa de um outro serviço para fazer o mapeamento endpoint-função, nele mesmo você define o endpoint que irá acionar a função a ser executada, ou seja, ele é basicamente o Lambda + API Gateway. Portanto, é também uma opção para aplicar-se uma arquitetura serverless.

Em relação aos demais componentes apresentados no diagrama acima, o Google também possui serviços equivalentes, são eles:

Enfim, no quesito técnico, a arquitetura apresentada pode ser aplicada das duas Clouds.

Free tier

A camada gratuita do GCP é diferente da AWS. Alguns produtos possuem uma camada totalmente gratuita até certo ponto, outros não possuem. Por outro lado, a Google dá um crédito de $300 válidos por 1 ano para ser utilizado na conta. Analisando item a item do que seria necessário nesta arquitetura:

Cloud Functions :heavy_check_mark:

Possui camada gratuita.

2.000.000 solicitações gratuitas por mês Até 3,2 milhões de segundos de tempo de computação por mês 400,000 GB-seconds 200,000 GHz-seconds of compute time 5GB of Internet egress traffic per month

Cloud Firestore (NoSQL) :heavy_check_mark:

1 GB storage 50,000 reads, 20,000 writes, 20,000 deletes per day

Cloud Storage :heavy_check_mark:

5 GB-months of Regional Storage 1 GB network egress

Stackdriver :heavy_check_mark:

50 GB Logs with 30-day retention 50 GB of logs with 30-day retention read access API Basic email alerting

Cloud CDN :heavy_check_mark:

O Cloud CDN não é gratuito, porém o preço é baixo, e como o GCP dá $300 dólares de créditos, cobre o custo com CDN, conforme a simulação abaixo feito na calculadora do GCP:

Cache egress - North America: 5 GiB HTTP/HTTPS cache lookup requests: 1.000.000 Cache invalidation: 10

Preço: USD $1.20/mês

Conclusão

Para utilizarmos a arquitetura Serverless, teríamos que trocar o boilerplate do projeto :disappointed:

Tanto o GCP quanto a AWS suporta esta arquitetura. Acredito que o GCP é melhor para esta arquitetura dado que consegue lidar com o dobro de requisições por mês na camada gratuita que a AWS e caso venha a ultrapassar o limite, o custo pode ser coberto com os créditos que a google provém.

É uma arquitetura que provém menos operação e é altamente escalável.

rodrigondec commented 4 years ago

@deniojunior Achei muito interessante essa proposta do serverless. É um paradigma diferente do habitual.

Isso tem vantagens e desvantagens.

Aparentemente é menos custoso (e com mais performance) para os provedores de cloud manter ter uma "lambda function" do que uma aplicação hospedada. Partindo desse benefício fico bem empolgado em utilizar.

Porém não é o 'tradicional' da atualidade. Então muita gente pode estranhar, não entender ou não ter domínio sobre. Considerando isso perco um pouco da empolgação :disappointed:

deniojunior commented 4 years ago

@rodrigondec A minha opinião é a mesma que a sua. Quis trazer esta proposta para colocar em discussão pra galera e principalmente para deixar documentado, pois imaginando o crescimento do projeto, esta arquitetura pode fazer sentido no futuro =)

Já dando spoiler rsrs, eu realmente acredito que a arquitetura mais interessante para o projeto será com Kubernetes. Estou fazendo o levantamento e, no momento, estudando alguns pontos que fiquei com dúvida. Tá demorando um poquinho, mas a terceira proposta deve sair ja já.

edmargomes commented 4 years ago

Acho que está complexo d+, ainda mais em node que é leve.

Trabalho muito com Drupal, trabalhei em um projeto de 10 mil visitas por mês, com 700gb de dados no banco e os arquivos no EC2 mesmo, pois não tinha colocado no S3. O custo era de $400 na aws + cloudfront (CDN) Pagava muito alto por conta da latencia de banda que precisa ser muito rapida. O mesmo serviço eu tinha no Tier Free para o ambiente de testes e rodava blz

Esse projeto roda suave no Free Tier, a medida que for tendo necessidade vai adaptando. Node tem muito mais performance que o Drupal se bem programado. Roda tão suave que no dia que derrubar a instancia pelo numero de acesso nós vamos soltar foguete de alegria.

Lembrando que se vc coloca um loadbalance, e ele percebe q sua instancia caiu, ele mesmo sobe ela novamente, ninguem precisa ficar la monitorando, so cadastrar o alerta para o loadbalance e um alerta para avisar a equipe que teve uma queda, para olhar os logs. O dia que precisar ter duas instancias, aaaa ai é 99% de disponibilidade

Serio, tudo que projetaram ai é lindo se projeto for uma netflix, facebook, algo global. Não vale a pena gastar varias horas de dev para economizar $50,00. Tenho certeza que se o tier free não aguentar pq cresceu, todo mundo tirar uns 10 ou 20 reais do bolso para ajudar a pagar.

Acho desnecessário usar NoSQL agora, memoria ram é cara sendo que muita coisa resolve com cache. O ideal é fazer tudo no RDS e depois ver se tem algum ponto que precisa melhorar performance de crud e usa um hibrido de NoSQL e RDS

deniojunior commented 4 years ago

@edmargomes Obrigado pela contribuição!

Entendo o que você quis dizer, e concordo em partes. A complexidade é algo relativo. Todos os componentes propostos são mais no intuito de adicionar camadas de seguraça mesmo. Como você falou, o Node é leve e colocar o projeto rodando não exige muito, mas temos que tentar garantir que não teremos dados roubados, pois isso pode acabar prejudicando os advogados e, possivelmente, os entregadores. Penso que iremos sofrer ataques, visto que é um app que irá ajudar trabalhadores e como consequência poderá causar prejuísos financeiros para algumas empresas.

Concordo com o seu ponto de não deixar o projeto muito complexo, é realmente algo que temos que manter em mente. O que foi proposto não gasta muito mais tempo de desenvolvimento. Estamos pensando em desenvolver a infra com Terraform, e eu mesmo já tenho um boilerplate para montar todo o Network proposto. A arquitetura serverless realmente adicionaria tempo de desenvolvimento pro pessoal do @idvogados/backend, e apesar de ser interessante, realmente não seria bom pro projeto.

Quis propor uma infra que tivesse uma estrutura bem sustentável e que fosse de baixo custo. Pode ser também que tomemos a decisão de só subir um EC2 mesmo por enquanto, só para colocar o MPV rodando, mas de toda forma eu acho muito importante ter um caminho de uma infra ideal para irmos construíndo ela (mesmo que aos poucos), por isso deixei a proposta completa documentada.

Em relação á escolha do NoSQL, é mais uma decisão de modelagem mesmo. Se não me engano, o pessoal do @idvogados/backend discutiu esse assunto no Discord, não sei se já tomaram a decisão. Você pode levantar esse discussão também por lá.

deniojunior commented 4 years ago

Kubernetes - AWS / GCP

O Kubernetes é um orquestrador de containers que automatiza a implantação, o dimensionamento e a gestão de aplicações em containers. Em um cluster kubernetes, podemos ter N nodes (instâncias), com suas respectivas configurações, N deployments (aplicações), N pods (unidade mínima) e N containers rodando uma imagem Docker.

Quando deployamos uma aplicação em um cluster kubernetes, conseguimos utilizar ao máximo os recursos de uma instância, pois o cluster provém X de CPU, X de memória, etc, que é a soma de recursos total que os nodes (instâncias) disponibilizam. Com isso, o uso destes recursos ficam totalmente transparentes. Em alto nível, não importa o node ou o pod que o container está rodando.

O Kubernetes provém ainda uma camada de segurança interna, onde é possível configurar a rede de comunicações privada entre os pods, isto é, se você possui mais de um deployment (aplicação) rodando no mesmo cluster, você configura quais (pods) podem se comunicar com quais de maneira interna.

Além disso, o kubernetes também abstrai a disponibilidade e o balanceamento de tráfego. Você configura o número de réplicas que você quer ter rodando do seu container, e ele garante que você terá este número de réplicas. Se um container cair, ele sobe outro no lugar. Como você pode ter mais de um container rodando, o kubernetes também provém a configuração de balanceamento, possuindo um load balancer interno para balancear o tráfego entre os containers.

Basicamente, acho que é possível dizer que o kubernetes provém uma abstração de toda uma infraestrutura para rodar uma ou mais aplicações dentro de um cluster.

Sendo um pouco mais prático

Como estamos limitados ao free tier das clouds, conseguimos subir apenas 1 instância ("máquina") t2.micro na AWS ou 1 F1-micro no GCP, se seguirmos a primeira proposta, teríamos 1 instância da nossa aplicação banckend rodando em 1 instância ("máquina") na nossa cloud.

Rodando em um cluster kubernetes, teríamos 1 node (instância ("máquina")) no nosso cluster, mas conseguiríamos subir quantos containers (instâncias da aplicação backend) o nosso cluster aguentar - em termos de recursos. Em outras palavras, poderíamos ter 3 instâncias da nossa aplicação backend rodando, com todo um gerenciamento de balanceamento e disponibilidade interno, aproveitando ao máximo os recursos da instância ("máquina").

Google Kubernetes Engine

Tenho mais experiência com kubernetes no GCP, e por isso trago como a primeira proposta com a Google.

Falando sobre a minha experiência, rodo vários microserviços diferentes (deployments) em um cluster kubernetes no GCP, microserviços os quais possuem um RPM muito alto e rodam muito bem, com o mínimo de operação/manutenção. Por causa da camada de segurança que temos dentro do cluster rodo com a configuração de VPC padrão e nunca tive problemas com isso.

Dado este contexto, imagino que poderíamos ter uima infra mais enxuta, conforme proposta abaixo.

Diagrama

Kubernetes

PS: Primeira vez montando um diagrama para o GCP, tentei trazer o conceito da proposta, não tenho certeza se está dentro do padrão (ou se existe um padrão rsrs).

Free tier

Conforme já mencionado, a camada gratuita do GCP é diferente da AWS. Alguns produtos possuem uma camada totalmente gratuita até certo ponto, outros não possuem. Por outro lado, a Google dá um crédito de $300 válidos por 1 ano para ser utilizado na conta. Analisando item a item do que seria necessário nesta arquitetura:

Cloud Firestore (NoSQL) :heavy_check_mark:

1 GB storage 50,000 reads, 20,000 writes, 20,000 deletes per day

Stackdriver :heavy_check_mark:

Logs with 30-day retention 50 GB of logs with 30-day retention read access API Basic email alerting

Cloud Endpoints ✔️

2.000.000 solicitações gratuitas por mês

Cloud Load Balancer ➖

Ingress $0.008 per GB Não é free, mas é um custo mínimo que acredito ser viável.

Kubernetes Engine❌

Each user node is charged at standard Compute Engine pricing Free tier: 1 F1-micro instance per month

Cluster management fee of $0.10 per cluster/hour, except for Anthos GKE

Cluster management fee 😭

O Kubernetes engine era gratuito, cobrando apenas os nodes (instâncias) que eram utilizados no cluster. Se você utilizasse a instância do free tier (F1-micro) seria gratuito.

Infelizmente, nesse mês, dia 06/06/2020, a google introduziu uma taxa de gerenciamento do Cluster de $0.10 doletas por cluster/hora. Isso nos dá um total de $73 doletas por mês.

Dado tudo que a google entrega no Kubernetes Engine, até faz sentido ter uma taxa de gerenciamento, mas infelizmente não é viável para nós.

A google nos dá um crédito de $300 por conta, mas daria para manter somente por 4 meses.

AWS EKS

Particularmente, nunca usei o EKS, mas ele é a forma de implementar um cluster kubernetes na AWS. Em relação á arquitetura, seria também mais enxuta, como o diagrama que foi apresentado acima, dado às camadas do próprio kubernetes, porém é possível criar uma VPC privada e fazer toda a configuração de segurança que foi proposta nas duas primeiras arquiteturas. Só fico com a mesma dúvida que no GCP, poderia ser over-engineering.

O EKS funciona de forma parecida com o Kubernetes Engine, nele os nodes são instâncias EC2 e a cobrança também é feita em relação à instância que você está utilizando. A AWS também possibilita o uso do EKS com o Fargate. Não conheço muito do Fargate, mas pelo que li, ele te permite rodar containers de forma serverless, ou seja, a cobrança é feita em cima dos recursos computacionais utilizados.

image

Cluster management fee 😭

Coincidentemente, ou não, a AWS também anunciou uma taxa de gerenciamento de cluster. Coincidentemente, ou não, a taxa é exatamente $0.10 doletas por cluster/hora =)

Da mesma forma do GCP, acabamos tendo um custo mensal. É ainda pior na AWS pois não temos créditos.

Conclusão

Eu realmente estava apostando muito nesta arquitetura por acreditar que se encaixaria muito bem para o que estamos construindo. Em quesitos técnicos, esta arquitetura possui muitos pontos positivos. No entanto, o fator custo é decisivo no nosso projeto e os valores apresentados acabam inviabilizando o uso nestas clouds.

Possibilidades

A análise foi realizada em apenas 2 clouds, portanto é possível analisar outras clouds e avaliar a viabilidade de aplicação desta arquitetura.

É possível também subir um cluster kubernetes de forma manual, porém o trabalho necessário para isso e a operação seria muito maior, o que pode não compensar para nosso projeto.

deniojunior commented 4 years ago

Definição de arquitetura ✔️

Fizemos uma reunião pelo discord para discutir as arquiteturas propostas, abaixo as conclusões:

Apenas levantamos a dúvida se poderíamos utilizar EC2 ou ECS, no caso da infra estar na AWS. Para isso, levantamos a task #10 para realizar este estudo.

deniojunior commented 4 years ago

Definição de Cloud ➖

Na mesma reunião mencionada acima, discutimos sobre em qual nuvem iremos hospedar nossa aplicação.

Pelos motivos de familiaridade da equipe, reduzimos as nossas discussões em apenas entre AWS e GCP, até porque precisamos de uma definição para sairmos do zero.

Entre AWS e GCP, concordamos na preferência pelo GCP pelos seguintes motivos:

Mas ainda não está definido, pois, precisamos confirmar se realmente é possível implementar a arquitetura definida no GCP. Para isso, levantamos a task #9, com o intuito de fazer uma prova de conceito (POC) e validarmos que é possível.

deniojunior commented 4 years ago

GCP Kubernetes Engine

Galera, pelo visto o GCP voltou atrás e está permitindo ter um cluster Kubernetes por Cluster na camada gratuita :smile:

image

No entanto, dei uma olhada nas configurações de uma instância F1-micro que iria compor o cluster e percebi que os recursos dela são os seguintes:

Machine name Description vCPUs Fractional vCPUs1 Memory (GB) Max number of persistent disks (PDs)2 Max total PD size (TB) Local SSD Network egress bandwidth (Gbps)3
f1-micro Micro machine type with 0.2 vCPU and 0.6 GB of memory, backed by a shared physical core. 1 0.21 0.60 16 3 No 1

O Kubernetes precisa de vários containers Docker internos dele para gerenciar o cluster. São alguns poucos serviços e controllers rodando la dentro no namespace kube-system. Além deles, teríamos o nosso container rodando, ou seja, não acho que um cluster apenas com uma F1-micro aguente.

No final das contas, acho bom a gente manter a nossa decisão mesmo da abordagem de Elastic Web Server.

rodrigondec commented 4 years ago

GCP Kubernetes Engine

Galera, pelo visto o GCP voltou atrás e está permitindo ter um cluster Kubernetes por Cluster na camada gratuita smile

image

No entanto, dei uma olhada nas configurações de uma instância F1-micro que iria compor o cluster e percebi que os recursos dela são os seguintes: Machine name Description vCPUs Fractional vCPUs1 Memory (GB) Max number of persistent disks (PDs)2 Max total PD size (TB) Local SSD Network egress bandwidth (Gbps)3 f1-micro Micro machine type with 0.2 vCPU and 0.6 GB of memory, backed by a shared physical core. 1 0.21 0.60 16 3 No 1

O Kubernetes precisa de vários containers Docker internos dele para gerenciar o cluster. São alguns poucos serviços e controllers rodando la dentro no namespace kube-system. Além deles, teríamos o nosso container rodando, ou seja, não acho que um cluster apenas com uma F1-micro aguente.

No final das contas, acho bom a gente manter a nossa decisão mesmo da abordagem de Elastic Web Server.

Concordo. Ter essa limitação de 1 instância apenas vai prejudicar tanto a aplicação quanto seu gerenciamento no cluster do kubernets.

Melhor

pabrrs commented 4 years ago

Definição de arquitetura heavy_check_mark

Fizemos uma reunião pelo discord para discutir as arquiteturas propostas, abaixo as conclusões:

* x **[Kubernetes](https://github.com/idvogados/infra/issues/7#issuecomment-647872905):** A taxa de gerenciamento de cluster trás um custo que não podemos arcar

* x **[Serverless](https://github.com/idvogados/infra/issues/7#issuecomment-643630695):** O trabalho de mudar o boilerplate e a complexidade que a aplicação pode ficar, podem atrapalhar a contribução da comunidade. Além da falta de conhecimento nossa no desenvolvimento de uma aplicação full serverless.

* heavy_check_mark **[Elastic Web Server](https://github.com/idvogados/infra/issues/7#issuecomment-641481484):** Encaixa totalmente na camada gratuita das clouds levantadas e supre bem as necessidades do projeto

Apenas levantamos a dúvida se poderíamos utilizar EC2 ou ECS, no caso da infra estar na AWS. Para isso, levantamos a task #10 para realizar este estudo.

:clap: :clap: :rocket:

deniojunior commented 4 years ago

Definição de Infra - Plot Twist :exploding_head:

Conseguimos um orçamento para ser gasto com a infra, o que viabilizou a implantação da arquitetura com Kubernetes. Como foi discutido, a arquitetura com kubernetes é a melhor para ser aplicada ao projeto, o que a inviabilizava era apenas que a instancia gratuita do GCP não consegue rodar o projeto. Agora que possuimos um orçamento para infra, optamos por seguir com a arquitetura Kubernetes com GCP

Kubernetes

Máquianas viáveis para compor o cluster

A documentação do GKE provê um paralelo entre os recursos de cada máquina, e os recursos que estarão disponíveis para a aplicação. Cortando apenas para as instâncias menores (possíveis de ser utilizadas no projeto), tem-se:

Machine type Memory capacity (GB) Allocatable memory (GB) CPU capacity (cores) Allocatable CPU (cores)
e2-micro 1 0.62 2 0.941
e2-small 2 1.35 2 0.941
e2-medium (default) 4 2.76 2 0.941
e2-standard-2 8 6.1 2 1.93

Preço

Machine type vCPUs Memory Price (USD) / hour Price (Real) / month
e2-micro 2 1GB $0.008376 R$ 32,41
e2-small 2 2GB $0.016751 R$ 64,81
e2-medium 2 4GB $0.033503 R$ 129,63
e2-standard-2 2 8GB $0.067006 R$ 259,25

Definição de instância

Conforme apresentado na tabela de recursos "alocáveis", todas as instâncias levantadas conseguem rodar a aplicação, a única questão é o preço. Sendo assim, vejo que a melhor abordagem é criarmos a infra inicial com a menor instância possível (e2-micro), fazermos um teste de carga para saber até onde ela aguenta a aplicação subir mais réplicas, e com base neste valor, avaliar se existe a necessidade de attacharmos mais recursos ao cluster.

PS: O custo de 32,41 reais mensais no início do projeto serão pagos pelos créditos de R$ 300 que a Google provém, isto é, temos 9 meses grátis :smiley: