Recentemente eu lancei o Keepup, que é basicamente uma ferramenta super simples de monitoramento de processos em ambientes Unix. Obviamente me questionaram o motivo de eu ter escrito mais uma ferramenta de monitoramento de processos, já que quase todo mundo conhece as famosas Monit, BluePill e God. Apesar da descrição do projeto já resumir bem os motivos, vou endereça-los um pouco melhor aqui.

Simplicidade de configuração

Todas as ferramentas que eu conheço fazem muito mais do que apenas monitorar se um "processo está de pé". Elas monitoram também memória, processamento, IO, possuem sistemas de notificação integrado, entre outras dezenas de outras funcionalidades. Isso é ótimo quando você precisa disso tudo, mas eu estava precisando apenas monitorar um único processo NodeJS. Eu não precisava saber se estava consumindo muita memória, nem precisava receber um e-mail e um SMS me avisando que o processo caiu (apesar de que essa última funcionalidade pode ser facilmente utilizada com o Keepup), eu precisava apenas reiniciar o processo automaticamente caso ele caísse.

Justificativas

Vou enumerar rapidamente os contras (super superficiais) que encontrei com cada uma das ferramentas ao tentar apenas monitorar um processo (tendo como premissa o meu conhecimento extremamente limitado de cada uma delas).

  • Monit: era preciso escrever um código utilizando uma DSL própria, que apesar de ser aparentemente muito simples, eu estava sujeito a erros.

  • God e Blueplill: ambos são escritos em Ruby, e tirando o fato de eu já ter lido alguns posts mostrando possíveis vazamentos de memória em relação a processos daemons com Ruby, eu seria obrigado a instalá-lo na máquina. Além, é claro, de cada um também ter a sua DSL específica...

A conclusão que cheguei é que eu ia acabar utilizando um canhão para matar uma formiga. Eu queria achar uma ferramenta onde eu poderia apenas rodar monitore "node server.js" e pronto, era o suficiente para mim. Sem DSLs, apenas um simples comando, como o Unix By The Book manda: fazer uma coisa apenas, e muito bem.

Simplicidade no código

Já que eu queria algo simples, e já que os processos em ambientes Unix são muito bem definidos, decidi que se eu fosse escrever algo do zero, que o código fosse tão simples quanto a API que eu tinha em mente. Isso me levou às seguintes decisões:

  • Escrever em C: nada contra Ruby, é sem dúvida minha linguagem favorita, mas por mais que você tente, nada vai ser tão resiliente e simples do que algo escrito em C. Além disso, já deixei bem claro que eu não gostaria de ter que instalar um Ruby em toda máquina que quisesse monitorar um processo.

  • Zero dependências: instalar com apenas um make install, que basicamente compila um arquivo com o compilador C padrão do sistema. Sem nenhuma linkagem a não ser das bibliotecas padrão do Unix, e, na medida do possível, sem nenhum pré-processamento.

Parece um desejo meio sadomasoquista impor estas duas restrições, mas na realidade o que eu precisava fazer era algo muito simples, e as system calls que eu utilizaria eram todas igualmente simples e conhecidas. Basicamente é um fork, exec e wait recursivo.

Resultado

Subi uma versão inicial do Keepup a dois meses no http://som.rnavarro.com.br e até agora, ele vem funcionando perfeitamente. Para demonstrar como ficou simples a API, isso é tudo que estou fazendo para monitorar a aplicação.

$ keepup -d "node server.js"

Isso além de monitorar a aplicação, também a daemoniza (com a opção -d).

Claro que assim que publiquei a ferramenta, algumas sugestões foram dadas e algumas ideias vieram à tona, sendo assim foram introduzidas mais duas funcionalidades importantes: uma maneira de escrever um PID file para o processo (e dessa forma poder, por exemplo, reiniciá-lo mais facilmente) e uma forma de saber quando o processo deu erro. Segue um exemplo básico de como usar o comando utilizando praticamente todas as features implementadas até o momento.

$ keepup -d -p /var/run/som.pid -e send_email.sh "node server.js"

Isso irá salvar um PID em /var/run/som.pid e também irá rodar o comando send_email.sh toda vez que o processo tiver que ser reiniciado.

Situação do código

Por hora estou conseguindo manter o código extremamente simples. Nenhuma dependência ou pré-processamento foi necessário até o momento, o que me deixa bem satisfeito com a atual implementação. Em matéria de linhas de código, não chega nem a 150, e isso contando o white space, e os comentários, já que um dos objetivos é também ser algo de fácil leitura e compreensão.

A maior parte do código trata os argumentos passados, como daemonizar processos e escrever os PID Files. O core em si da ferramenta é muito simples, e pode ser basicamente encontrado na função monitor.

Melhorias

A instalação da ferramenta ainda não está no ponto ideal. Atualmente é necessário clonar o repositório (ou copiar os arquivos) e rodar make install. Dentro de alguns dias espero gerar os binários e testar em várias plataformas para melhorar essa situação.

Outra funcionalidade que pode ser muito útil é incluir alguma opção de log. Inicialmente havia pensado em apenas utilizar o STDOUT, mas isso não funcionaria quando o processo for daemonizado, que é uma situação provável na grande maioria dos casos.

Colaborem

Creio que não sou o único que pode ver utilidade nessa ferramenta, e como ela vem funcionando muito bem por hora, eu diria para dar uma chance a ela caso esteja num dilema semelhante ao meu.

Atualmente creio que apenas eu estou usando a ferramenta em produção, e é óbvio que quanto mais gente tentar, mais fácil será de encontrar possíveis bugs. Além disso, eu não tenho quase nenhuma experiência com C, então, se alguém ver algo que pode ser melhorado ou corrigido na implementação, por favor, abra uma Issue no Github ou, melhor ainda, mande um Pull Request. Keepup!