Enfim, depois de todo esforço para desenvolver aquela aplicação, chegou a hora de subir ela em produção. Escolher onde e como disponibilizar essa solução para seus clientes em potencial pode ser determinante para o sucesso do projeto. Com isso em mente, temos ótimas soluções no mercado, entre as principais estão: Amazon AWS, Google Cloud, Microsoft Azure, e a que será foco desse artigo, Digital Ocean. Cada um destes provedores de serviço tem características bem peculiares que podem ou não aderir ao objetivo do projeto, não estando no escopo do artigo discorrer sobre as características de cada uma, porém é importante destacar que a Digital Ocean atende bem a muitos cenários, em especial aqueles que não possuem um grande fluxo de caixa inicial e precisam ter mais controle sobre o custo total de infraestrutura com um valor mais previsível, isso acontece pois lá é possível iniciar pequenas máquinas virtuais, denominadas “droplets” com um custo mensal a partir de US $ 5.00 por mês, na data que escrevo o artigo, com 1GB de memória, 1vCPU, 25 GB de SSD e 1TB de transferência, um valor bem razoável para o que entrega.
Como exemplo, a aplicação que iremos utilizar no artigo é uma api muito simplificada disponível em https://github.com/meneguite/node-simple-api, que nos possibilita descrever com mais facilidade uma forma de levar uma aplicação a produção, usando para isso além do node e npm, o nginx como proxy reverso e o pm2 como supervisor de instancia da aplicação.
Configuração do Droplet
Para iniciarmos é necessário uma conta na Digital Ocean, caso ainda não tenha uma, crie a por esse link https://m.do.co/c/2e1c3d77e32b, o cadastro é muito simplificado, e seguindo o link irá receber US $100 de bônus, o que possibilitara testar completamente a solução proposta por este artigo.
Com a conta criada vamos criar nosso primeiro droplet:
Selecionaremos o plano que melhor se enquadra na nossa necessidade:
É possível incluir novos blocos de storage ao droplet, extendendo a capacidade de armazenamento base do plano, porém para esse exemplo não será necessário.
Selecionar a região do datacenter:
Selecionar o método de autenticação:
Para a autenticação recomendo fortemente que crie uma chave ssh, se já não possuir uma, isso será um bom incremento de segurança em sua instancia.
É possível também, quando necessário, anexar ao droplet um agendamento automático de backup.
Tudo correndo bem, visualizará uma tela semelhante a demonstrada abaixo, com nome da instancia e o ip externo atribuído a mesma:
Para acessar nossa instancia podemos utilizar o comando:
1 | ssh [email protected] |
Instalando e Configurando as dependências para o projeto
Configurando um novo usuário comum
Como pode-se perceber ao acessar nosso novo droplet, o usuário base criado é o root, que possui privilégios muito elevados, sendo fortemente desencorajado a rodar uma aplicação com esse usuário, assim nossa primeira configuração será criar um novo usuário com privilégios menores para rodar nossa aplicação.
1 | adduser web |
A nova conta criada agora é um usuário com privilégios básicos, porém em alguns momentos precisaremos fazer algumas tarefas administrativas, caso não queira fazer a troca de usuário sempre que necessário executar essas tarefas, pode-se incluir esse novo usuário no grupo “sudo”, permitindo que o usuário comum tenha acesso a comandos administrativos mediante ao uso do prefixo “sudo”, para isso execute o seguinte comando:
1 | usermod -aG sudo web |
Liberando acesso via ssh para o novo usuário
Caso esteja usando uma chave ssh, como sugerido acima, para acesso ao servidor, será necessário adicionar uma chave autorizada para o usuário também, permitindo que o mesmo consiga logar no sistema. Em sistemas críticos é extremamente importante não usar a mesma chave para ambos os acessos, porém para este exemplo iremos copiar a mesma autorização do usuário root para simplificar o processo.
1 | rsync --archive --chown=web:web /root/.ssh /home/web |
Com esse comando será copiado os arquivos necessários para acesso, mantendo as permissões adequadas. Agora podemos nos desconectar via root acessando novamente o servidor usando nosso novo usuário com o comando:
1 | ssh [email protected] |
Ativando e configurando algumas regras básicas de acesso no Firewall
O linux possui muitas ferramentas interessantes e poderosas para manter a integridade do servidor restringindo o acesso ao mínimo necessário, para esse exemplo iremos utilizar um firewall que já vem incluso no ubuntu e que a principio atende bem nossa demanda, visto que simplifica muito a criação e administração das regras de acesso ao servidor usando perfis de aplicação registradas na instalação de alguns softwares. Para visualizar os perfis disponíveis no sistema:
1 | sudo ufw app list |
Antes de ativar o firewall, muita atenção para garantir que as conexões via SSH estão habilitadas, para isso execute o seguinte comando:
1 | sudo ufw allow OpenSSH |
Depois disso podemos ativar nosso firewall usando seguinte comando:
1 | sudo ufw enable |
Para visualizar as regras ativas no sistema execute o comando:
1 | sudo ufw status |
Instalando o NodeJs
Para instalarmos o node iremos usar os repositórios disponibilizados via PPA com os seguintes comandos:
1 | cd ~ |
Após a conclusão dos comandos acima, estamos aptos agora a proceder com a instalação do node. Repare porém, que no segundo comando eu defino a versão 14, a LTS no momento que escrevo este artigo, porém provavelmente só ajustar a versão mais atual será o suficiente para fazer a instalação da versão correta que precisa para rodar sua aplicação. Para instalar agora o nodejs basta executar o seguinte comando:
1 | sudo apt install nodejs build-essential |
A instalação do pacote “build-essential” é opcional neste exemplo, porém muito provavelmente vai precisar dele no dia a dia no servidor com aplicações node.
Para verificar a instalação podemos executar o seguinte comando:
1 | node -v |
1 | npm -v |
Instalando e configurando o PM2
O PM2 é um gerenciador de processos para aplicativos node.js que nos possibilitará gerenciar, escalonar e manter o aplicativos sempre disponível. A instalação do PM2 é muito simples e feita diretamente usando o NPM com o comando abaixo:
1 | sudo npm install pm2@latest -g |
Para verificar se a instalação foi feita com sucesso use o comando:
1 | pm2 --version |
Agora chegou o momento de obtermos nossa aplicação para seguirmos com a configuração, para simplificar esse artigo removendo complexidades de integrar esse fluxo em um servidor de CI/CD por exemplo, vou apenas simular que já tenho os arquivos no servidor, baixando os mesmos do repositório via GIT com a seguinte sequência de comandos:
1 | cd ~ |
Para visualizar os arquivos disponibilizados:
1 | ls -la |
Ps: No droplet criado com ubuntu 20 já temos o git instalado por padrão, porém caso ainda não o tenha, basta executar o seguinte comando “sudo apt install -y git” para instalar.
Com os arquivos em mãos podemos dar inicio a configuração da nossa aplicação no PM2, essa configuração pode ser feita parametrizando o pm2 via linha de comando, porém para simplificar disponibilizei no repositório um arquivo “ecosystem.config.js” que já possui todos os parâmetros necessários para configurar a aplicação. O formato do arquivo ecosystem é o seguinte:
1 | module.exports = { |
Para iniciarmos nossa aplicação usando esse nosso arquivo e garantir que o mesmo seja iniciado automaticamente sempre que o PM2 iniciar devemos executar o seguinte comando:
1 | pm2 start ecosystem.config.js --env production && pm2 save |
Assim já temos nossa aplicação rodando na porta 3000 localmente, como podemos verificar abaixo:
1 | curl http://127.0.0.1:3000/users |
O PM2 é muito flexível e poderoso, nesse momento já temos disponível nossa aplicação e podemos escalar ela sem nenhuma dificuldade, por exemplo, para subir para 4 o número de instancias disponíveis, podemos executar o comando:
1 | pm2 scale node-simple-api 4 |
Mais informações e exemplos de uso do PM2 podem ser visualizados na documentação oficial disponível em https://pm2.keymetrics.io/docs/usage/quick-start/
Para configurarmos o PM2 para iniciar junto com o sistema operacional é bem simples, e o próprio PM2 já nos facilita com o comando “pm2 startup”
1 | pm2 startup |
1 | sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u web --hp /home/web |
1 | pm2 save |
Instalando e configurando o nginx como proxy reverso
Para instalarmos o nginx via PPA devemos executar os seguintes comandos:
1 | sudo add-apt-repository ppa:nginx/stable -y |
Podemos verificar se a instalação foi finalizada com sucesso com o seguinte comando:
1 | sudo service nginx status |
Tendo isso em mãos vamos iniciar a configuração de um endereço DNS de entrada, para o teste irei usar o endereço “simple-api.meneguite.com”, para isso iremos criar o arquivo “/etc/nginx/sites-available/simple-api.meneguite.com.conf” com o seguinte conteúdo:
1 | server { |
Remover o arquivo default de configuração do nginx:
1 | sudo rm /etc/nginx/sites-enabled/default |
Criar um link simbólico para habilitar o novo arquivo de configuração:
1 | sudo ln -s /etc/nginx/sites-available/simple-api.meneguite.com.conf /etc/nginx/sites-enabled/simple-api.meneguite.com.conf |
Para testarmos e verificarmos se os arquivos e configurações estão ok antes de reiniciar os serviços execute o seguinte comando:
1 | sudo service nginx configtest |
Tudo certo e configurado podemos fazer o reload do nginx para que o mesmo leia as configurações novamente e ative corretamente os serviços:
1 | sudo service nginx reload |
Se tudo estiver corrido como esperado até aqui, ao executar o comando abaixo já poderemos ver nossa api funcionando já por trás do proxy reverso do nginx.
1 | curl http://127.0.0.1/users |
Tudo configurado e funcionando, porém se tentar acesso pelo browser receberá a seguinte mensagem:
Isso acontece devido a ainda não termos feito a liberação da porta 80 de entrada necessária para acesso ao nginx, para fazer essa liberação devemos proceder com os seguintes comandos:
Verificar os apps disponíveis:
1 | sudo ufw app list |
No nosso exemplo não configuramos os certificados para usarmos o https, assim vamos por hora liberar acesso apenas a porta http do ngix com o seguinte comando:
1 | sudo ufw allow 'Nginx HTTP' |
E enfim temos nosso serviço instalado e configurado:
Nosso servidor está acessível externamente e com a aplicação clousterizada usando o PM2, porém não é tão legal acessar nosso serviço sem um https, muito menos pelo ip, para resolver isso podemos usar o serviço da cloudflare para nos prover um certificado https válido, este processo é bem simples e descrevo como fazer no artigo “Publicar um site com Github Pages e CloudFlare” acessível no endereço: https://meneguite.com/2018/11/10/publicar-um-site-com-github-pages-e-cloudflare
Tudo pronto agora! Para o escopo deste artigo chegamos ao final, mas claro, sabemos que muitas outras necessidades vem ao criar uma aplicação, com essa configuração básica já consegue iniciar e validar pontos iniciais com um custo muito baixo, porém durante a trajetória provavelmente vai sentir a necessidade de um banco de dados, relacional ou não, e pode lançar outros droplets para resolver esta demanda, ou pode optar por usar um serviço disponibilizado pela própria Digital Ocean o “Managed Databases”, com esse serviço terceiriza toda a complexidade inicial de configurar e manter um servidor de banco de dados, para mais informações acesse “https://www.digitalocean.com/products/managed-databases/“. Outra demanda que muito provavelmente precisará em breve é trabalhar na resiliência da aplicação, ainda que muito estável, os servidores ou o próprio droplet pode parar de funcionar em algum momento, ou mesmo sua aplicação pode crescer e somente um único servidor deixar de ser suficiente, assim uma boa estratégia será incluir no meio do caminho um servidor de load balancer, serviço provido pela própria digital ocean, e lançar duas ou mais instancias do droplet que configuramos neste artigo, assim mesmo que tenha uma demanda maior ou um dos droplets tenha algum problema, nosso serviço continua a funcionar normalmente.
Os códigos usados neste artigo estão disponíveis no repositório https://github.com/meneguite/node-simple-api.git, qualquer dúvida ou sugestão de melhora fique a vontade para deixar aqui abaixo nos comentários.