PM2: Como utilizar no NodeJS e funcionalidades secretas

Se por um lado ter um ambiente de desenvolvimento adequado é importante, ter um ambiente de produção robusto é mais que isso, é imprescindível. E na sua toolbox de DEPLOY jamais deve faltar o PM2.

No post de hoje vou um pouco além de te apresentar o PM2, afinal existem muitos posts sobre isso. Hoje vou te mostrar algumas funcionalidades interessantes que ele trás que muitas vezes não são explicadas ou mesmo nem sabidas.

PM2

O PM2 é um gerenciador de processos e com ele lidar com variáveis ambientes, fazer log, saber o estado da aplicação se tornam tarefas simples.

Você basicamente instala ele em sua máquina e ao invés de usar o node para executar sua aplicação, você usa o pm2.

pm2 start index.js --name site1

O comando acima, executa o index.js que ficará rodando em background e nomeia o processo aberto para site assim você não precisa saber o id do processo para manipula-lo.

Você pode utilizar uma sequência de comandos com o pm2, ao invés de ir chamando um a um. O que você precisa é criar um arquivo .json com os scripts e opções que você quer.

{
  "apps" : [{
    "name"        : "site1",
    "script"      : "./server.js serve",
  },{
    "name"       : "kue",
    "script"     : "./ace kue:listen",
  }]
}

Ao dar start neste arquivo você já abre todos os processos ligados a ele centralizando assim as coisas.

Logs

Sim, os logs são extremamente importantes e se você já utiliza o Sentry já está no caminho certo. Mas com o pm2 você pode obter o log da sua aplicação também. Os logs aqui são os informados no console log, info, error.

Você pode muito bem exibir o log de todos os processos:

pm2 logs

Ou de um processo específico:

pm2 logs site1 --lines 100

Esse último exemplo mostra os logs do processo site1 com as últimas 100 linhas de logs, por padrão são exibidas apenas as 15 últimas.

Watch

Outra coisa legal que o PM2 possui que é bem legal é o watch, que vai funcionar como o nodemon no ambiente de desenvolvimento, reiniciando o seu processo caso algum arquivo do diretório ou subdiretório venha ser alterado. Isso é muito interessante se for bem usado.

pm2 start app.js --watch

Esse comando é o suficiente para o pm2 observar as alterações, mas você pode acrescentar isso ao arquivo de configuração .json, lembra que nele você pode passar diversos comandos, e pelo arquivo de configuração você pode passar mais opções.

...
{
  "name"   : "site1",
  "script" : "./server.js serve",
  "watch"  : ["app"],
  "ignore" : ["models"]
}
...

Clusterização

O pm2 permite executar o programa em clusters, aproveitando assim todos os núcleos do servidor. Com o pm2 você não precisa utilizar o cluster.js, que faz a mesma coisa.

pm2 start app.js -i 0

Com o argumento -i passando um valor você define quantas instâncias seu programa vai rodar, o 0 significa que ele rodará em todos os núcleos disponíveis.

Gerenciamento de estado

Você consegue ficar escutando o processo que o pm2 abriu e assim quando ele fechar você montar alguma estratégia, fechar a conexão com o banco de dados por exemplo é uma opção.

process.on('SIGINT', async () => {
  let status = 0
  try {
    await Database.close()
  } catch (err) {
    status = 1
  }
  process.exit(status)
});

Agora quando o pm2 stop ou pm2 restart for executado ele fecha a conexão com o banco de dados antes de finalizar o processo.

Você também pode indicar que o seu serviço está "pronto", para o pm2 considerar que ele está online:

setTimeout(() => {
  process.send("ready")
  setInterval(() => {}, 1000)
}, 3000)

Iniciando o arquivo no pm2 com o argumento --wait-ready sua aplicação só estará como online depois de 3 segundos. Por padrão ele já sabe quando você está trabalhando com o http.Server.listen e faz isso automaticamente.

Trabalhando com actions

Esse pm2 surpreende as vezes, com ele você consegue através de uma biblioteca executar ações dentro do seu programa, isso permitirá executar funcionalidades de logs, mudanças de estado e outras coisas que vão depender da sua criatividade.

const pmx = require('pmx')

pmx.action("log", async function(param, reply) {
switch(param) {
  case "online":
    reply({ 
      online: await User.findBy({ online: true })
    })
    break;
  case "users":
    reply({ 
      users: await User.count()
    })
    break;
  default:
    reply({ 
      online: await User.findBy({ online: true }),
      users: await User.count()
    })
})

Agora você pode pegar essa action através de um trigger:

pm2 trigger site1 log online

O online é o parâmetro passado para action. Sensacional.

Docker

Você consegue usar o pm2 com o Docker também, o que deixa as coisas ainda mais fantásticas. Sobre a utilização pm2 com o Docker exigiria um post ou um vídeo só sobre eles, então só estou te avisando dessa possibilidade. Para saber mais acesse aqui.

Fim

Essa ainda é a ponta do iceberg mas você já percebeu que o pm2 é muito poderoso e vale muito a pena se aprofundar nessas funcionalidades que lhe apresentei, a forma com que você trabalha na produção nunca será a mesma.

Aquele grande e forte abraço, nos vemos!!!