A stack BrazilJS
Começar algo do zero é tão divertido quanto é complicado, e foi bem assim que montamos o ambiente para rodar as aplicações que hoje formam uma pseudo-plataforma BrazilJS. Neste post vou tentar listar tudo o que foi feito e como foi feito para subir o CMS BrazilJS, o Blog BrazilJS e mais umas pequenas ferramentas.
Hosting
Depois de pesquisar as variadas soluções de mercado, optamos por usar a Digital Ocean. O principal fator decisório foi o fato do serviço da Digital Ocean ser realmente muito simples de usar. Além disso, a documentação é fantástica e na grande maioria dos casos o problema com a solução já está documentado. Isso facilita e muito a vida de quem não tem tanta experiência com essa parte de infra-estrutura e servidores. O preço também é bem acessível, estamos pagando cerca de $10 (dólar) mensais com o sistema de backup ativado, o que é bem interessante, pois não precisamos nos preocupar em instalar ferramentas de backup, agendar scripts, etc.
Servidor
Nossas aplicações foram desenvolvidas com Node.js, então obviamente o Node.js precisa estar rodando no servidor. Usamos uma solução for dummies, as chamadas "One-click apps" que a Digital Ocean oferece. Nesse caso a imagem utilizada foi a node v4.2.4 on 14.04
, que vem com o Ubuntu server 14.04. Com isso, o primeiro passo foi dado. Um servidor Ubuntu + Node.js rodando em poucos minutos.
Gerenciamento e Deploy
Mesmo não tendo muita experiência com o deploy de aplicações Node.js, queríamos ter ao menos o básico. Em outras palavras, uma solução simples para colocar novas versões das aplicações no ar. Atualmente muito se fala do Docker, e com razão. É uma solução incrível que em breve devemos adotar na nossa stack. A solução atual foi um pouco mais simples, optamos por usar o pm2. O pm2 nada tem a ver com o Docker, mas ele possui uma opção de deploy bem simples que nos agradou bastante. A principal função do pm2 é gerenciar aplicações Node.js. Vejam na imagem abaixo um exemplo em produção do pm2 gerenciando nossas aplicações. O pm2 nos dá uma série de recursos interessantes, como por exemplo levantar e monitorar aplicações Node.js. Veja como o nosso blog é iniciado com o pm2: shell NODE_ENV=production pm2 start /path/to/app/index --name braziljs-blog
Ao executar esta linha, o pm2 passará a gerenciar essa aplicação, de forma que conseguimos executar ações bem úteis, como reiniciar a aplicação, parar, matar, monitorar, ver os logs, e muito mais.
Com o pm2, o deploy se torna uma tarefa trivial. Bastou criar um arquivo chamado ecosystem.json
na raíz do projeto ( é possível executar pm2 ecosystem
para gerar automaticamente), configurar algumas coisas e pronto. Existem alguns outros passos de configuração que podem ser vistos na documentação sobre deploy do pm2, mas o que importa é que no final basta rodar o comando: pm2 deploy ecosystem.json production
. Deploy feito. Um ponto que vale mencionar aqui é a configuração das chaves SSH. Estamos utilizando o Bitbucket, e para fazer o deploy funcionar corretamente foi preciso criar uma chave pública no servidor onde as aplicações estão rodando e adiciona-la no Bitbucket. A chave pública local (desenvolvimento) precisa ser adicionada na lista de hosts conhecidos no servidor.
Nginx
Uma boa prática para aplicações desenvolvidas com Node.js é usar o Nginx como ponto de entrada. Isso é bom por vários motivos, um deles é o fato de poder delegar menos tarefas ao próprio Node.js. Por exemplo, a entrega de arquivos estáticos pode ser feita sempre pelo Nginx, dando um bypass total na aplicação Node.js.
Usando um proxy-reverso do Nginx conseguimos definir rotas para cada aplicação Node.js. Isso também nos da a possibilidade de escalar as aplicações, fazendo o Nginx funcionar como um load-balancer no futuro.
Nosso arquivo (/etc/nginx/sites-available/default
) de configuração ficou mais ou menos assim: `\
server { listen 80;
server_name braziljs.org; location / { proxy_pass [URL PRIVADA NODE APP]; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; }
} Para servir arquivos estáticos sem passar pelo Node.js, bastou adicionar mais um `location`:
location /public { root /path/to/public; } `\
Desta maneira, sempre que o path path/to/public
for requisitado, o Nginx será o responsável pela entrega dos arquivos. Memcached Mais uma boa prática quase que obrigatória em qualquer aplicação Web é o uso de alguma solução de cache. O Memcached é uma solução free e open-source, de alta performance, de um sistema distribuído de cache em memória. Tanto a instalação/configuração e uso nas nossas aplicações Node.js foram bem tranquilas. Utilizamos o módulo memcached que parece ser a solução mais eficaz no mundo Node.js. Abaixo segue um exemplo de como fizemos cache em uma das seções do CMS BrazilJS: `\
js var Memcached = require('memcached'); var mc = new Memcached('URL DO MEMCACHED:PORTA', { maxExpiration: 60, timeout: 2500 }); mc.get('eventsList', function(err, data) { // Sem cache, vai ir na aplicação Node.js if (!data) { // ... // Adiciona no Memcached mc.set('eventsList', locals.events, 60, function(err) { if(err){ console.error('Failed setting memcached [EVENTS]', err); } else { console.log('Stored in memcached [EVENTS]'); } }); // Dados no Memcached } else { // Usa o valor da variável data } }); `\
Com poucas linhas de código em pontos estratégicos de cada aplicação, conseguimos ter um ganho de performance expressivo. Obviamente ainda temos muito a melhorar nessa questão, mas estamos no caminho :) Ghost Escolhemos o Ghost como plataforma para os nossos artigos. Alguns fatores influenciaram a escolha. Um deles é o fato do Ghost ser uma solução Node.js. Já que o nosso stack estava pronto e configurado, nada melhor do que usar uma ferramenta fácil de plugar. Além disso, a interface do Ghost para os autores é muito boa, e essa era uma premissa. Obviamente o Ghost têm algumas falhas, uma delas sendo a falta de uma API para interagir com a plataforma. Por esse motivo acabamos criando uma ferramenta bem simples de scraping, para poder ter a liberdade de listar os artigos em nossas páginas do CMS. Tivemos também problemas com o envio de emails através do Ghost. Depois de dias perdidos, acabamos encontrando a solução, que basicamente era alterar a porta. KeystoneJS Como solução para o nosso CMS acabamos escolhendo o KeystoneJS. Existem diversas soluções e frameworks legais para Node.js, mas o Keystone foi o favorito por alguns motivos: É um projeto open-source e bem ativo Fácil e rápido de aprender, além de ser bem flexível Database-driven Fácil de criar APIs Admin gerado baseado nos models da aplicação Swap Uma pequena dor de cabeça que tivemos, devido a falta de conhecimento, foi com o nosso CMS baseado no KeystoneJS. Ao dar o start em uma aplicação KeystoneJS, o processamento e o uso de memória é bastante elevado. O problema que enfrentamos foi o constante uso do processador, chegando a 90% em cada hit da aplicação. Depois de muita pesquisa e estudo, chegamos a solução: Swap. Swap é um espaço em disco dedicado a memória virtual, e por padrão a máquina Ubuntu da Digital Ocean não cria esse espaço. Depois de descobrir que o problema era esse (pedimos ajuda no Gitter do KeystoneJS), o problema foi facilmente resolvido. Open-source Ainda não liberamos nenhuma das soluções devido a falta de documentação, testes e uma série de fatores que julgamos necessários para abrir um projeto. Com o tempo vamos melhorar isso e cada parte desse pequeno mundo que criamos será aberto para a comunidade. Outro fator é que até agora pouca coisa de fato foi desenvolvida que mereça se tornar um projeto Open-source. Nada demais têm em um CMS, em um tema para um blog, etc. Mas claro, esse é apenas o primeiro passo. Com a evolução novos projetos virão e o processo natural é que tudo seja aberto. A exceção aqui é o pequeno serviço de scraping que desenvolvemos, que sim, pode ser útil para a comunidade. Em breve ele estará disponível. Conclusão Esse foi um resumo do que fizemos até aqui, em breve farei mais alguns posts relacionados a stack e aos projetos em si, com mais detalhes técnicos, etc. A ideia desse post foi compartilhar o que está por trás dos nossos projetos, como fizemos e quais foram os problemas encontrados durante o desenvolvimento. Referências Deploy PM2: http://pm2.keymetrics.io/docs/usage/deployment/ Node.js, PM2, Digital Ocean: https://www.digitalocean.com/community/tutorials/how-to-use-pm2-to-setup-a-node-js-production-environment-on-an-ubuntu-vps SSH keys Digital Ocean: https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-digitalocean-droplets SSH issues Bitbucket: https://confluence.atlassian.com/bitbucket/troubleshoot-ssh-issues-271943403.html Node.js em produção na Digital Ocean: https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-14-04 Ghost na Digital Ocean: https://www.howtoinstallghost.com/how-to-install-ghost-on-digital-ocean-vps-old/ Swap na Digital Ocean: https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04 Performance Node.js: https://www.nginx.com/blog/5-performance-tips-for-node-js-applications/ Ghost timeout error: http://onemobdev.com/blog/2015/08/14/ghost-etimedout-error-digitalocean/ Foto no topo da página por coniferconifer