Preâmbulo:
Recentemente coloquei no ar o FollowUpTime, que como já disse em outros posts é um sistema de monitoração para redes e servidores.
Como podem imaginar, um bocado de linhas de código está envolvida num sistema assim e a conta só do engine principal passa das 90 mil linhas. Isso sem incluir templates, javascripts, etc.
O sistema é composto de duas partes: Uma que roda num servidor principal gerenciando a maior parte das tarefas e as “probes”. As probes são pequenos scripts que recebem instruções sobre hosts a serem testados e respondem para o servidor principal com o tempo de resposta, um OK ou um FAIL. Simples assim.
São essas probes que temos espalhados por vários cantos do planeta.
Porém quando recebi a probe dos desenvolvedores me vi com um pequeno, mas incoveniente problema. Os desenvolvedores realmente optaram pelo princípio KISS. Eles me entregaram uma excelente probe que fazia o que era necessário. E nada mais. Eu tinha nas mãos um daemon que rodava em foreground e jogava mensagens para stdout e stderr e nada mais.
Eu não me demorei e ataquei o problema da forma errada, pensando como um programador e não como um administrador Unix. E acabei reinventado a roda.
Introdução – O problema
Quando percebi o que eu tinha nas mãos logo pensei nos problemas que eu precisava resolver:
- Preciso iniciar o daemon sempre que der boot na máquina
- Preciso ser capaz de rodar em daemon em background, ou vai me zoar a seqüencia de boot
- Posso precisar parar/reiniciar/iniciar manualmente este daemon
- Se o processo morrer eu tenho que ser notificado e que, de preferência, ele seja reiniciado automaticamente
- E sim, esse processo não deve rodar como root
Já velhaco de Linux logo pensei num script para ser colocado em /etc/init.d/ que ia resolver boa parte dos meus problemas. Com uma combinação bonita de bash scripting, várias ferramentas e comandos Linux que só se conhece depois de muito tempo de casa (como nohup, pidof, &) e gambiarras na crontab eu consegui. Consegui reinventar a roda. Não vou entrar em detalhes da solução que fiz, pois apesar de 100% funcional não era nem de perto tão bonita quanto:
A solução – Daemontools
Nosso caro amigo D.J.B, autor do famoso Qmail também já passou por um problema parecido com o meu, mas ao invés de criar um solução simples para resolver um problema pontual ele foi mais longe. Ele criou uma solução global que atende o problema dele, o meu e provavelmente também o seu.
Na página oficial do daemontools tem uma tabela, que traduzo abaixo, comparando a ferramenta dele com outras abordagens (incluindo a minha, que utilizava init.d):
Funcionalidade | inittab | ttys | init.d | rc.local | /service |
Fácil instalação e remoção de serviços | Não | Não | Sim | Não | Sim |
Simples startup inicial | Não | Não | Não | Não | Sim |
Reinicializações confiáveis | Sim | Sim | Não | Não | Sim |
Sinalização simples e confiável | Não | Não | Não | Não | Sim |
Estado de processo limpo | Sim | Sim | Não | Não | Sim |
Portabilidade | Não | Não | Não | Não | Sim |
Dá para perceber que o cara pensou em tudo e não tem porquê reinventar a roda. Vamos utilizar o daemontools!
Instalando o Daemontools no Debian 5.0 (Lenny):
Quem já instalou o daemontools no Debian no passado lembra que pentelhação que era, pois devido a licenciamento era apenas um pseudo-pacote que roda um script para realmente baixar/instalar o daemontools. Isso foi resolvido quando o D.J.B mudou a licença e agora o pacote é um .deb “de primeira linha”.
# apt-get install daemontools daemontools-run
Pronto. Só isso. Próximo passo…
Criando um serviço
Para fins didáticos vamos criar um serviço simples, chamado homer.sh cujo conteúdo é:
#!/bin/bash
progname=$(basename $0)
LOG=/tmp/$progname.log
AVISO="Hummm... rosquinha..."
SONECA=2
echo `date`: "starting $progname..." >> $LOG
while true
do
echo `date`: "$progname: $AVISO" >> $LOG
echo `date`: "$progname: tirando uma soneca de $SONECA segundos..." >> $LOG
sleep $SONECA
done
Não faz muito, mas funciona. E tem exatamente as mesmas características do meu serviço da probe, inclusive rodando em foregroud. E agora? Como “daemonizar” isso atendendo todos aqueles meus requisitos iniciais?
Juntando no daemontools
Crie um diretório qualquer onde vai guardar seus serviços de forma organizada. Recomendo algo como /opt/services por exemplo. Lá vamos criar um subdiretório homer e moveremos nosso script para lá:
# mkdir -p /opt/services/homer
# mv homer.sh /opt/services/homer/
Para controlar a inicialização dele precisamos de um script dentro do mesmo diretório chamado run. Simples assim:
#!/bin/bash
exec ./homer.sh
E estamos 50% prontos. Não esqueça que tanto o homer.sh como o run precisam ser executáveis!
# chmod 755 /opt/services/homer/*
Agora para ativar o serviço vem a parte realmente fácil:
# ln -s /opt/services/homer /etc/service/
Conte até 5 e dê um ps auxw no prompt. Sim… o seu serviço está no ar.
# ps aux|grep homer
root 3250 0.0 0.1 1620 324 ? S 19:28 0:00 supervise homer
root 3450 1.2 0.4 2792 1220 ? S 19:31 0:00 /bin/bash /opt/services/homer/homer.sh
Observem que além do serviço ainda existe um supervisor que manterá os olhos nele e, caso o mesmo morra, será automaticamente reiniciado. Observem que o PID do processo é 3450, certo?
# kill -9 3450
# ps aux|grep homer
root 3250 0.0 0.1 1620 324 ? S 19:28 0:00 supervise homer
root 3660 8.0 0.4 2788 1212 ? S 19:33 0:00 /bin/bash /opt/services/homer/homer.sh
Menos de um segundo depois de matar o processo um novo (3660) já assume o lugar. Pra que mesmo que perdi todo aquele tempo desenvolvendo um script de inicialização e depois com outro que ficava vendo se o processo estava vivo?
Indo mais adiante
OK, mas um dos requisitos também era rodar o processo como usuário não privilegiado, certo? E, como podem ver ali em cima, tá rodando como root. Vamos modificar um pouco nosso serviço então, criando um usuário, mudando as permissões do arquivo de log e editando o run:
# useradd -s /bin/false -d /dev/null simpson
# chown simpson:simpson /tmp/homer.sh.log
E agora o conteúdo do /opt/services/homer/run passa a ser:
#!/bin/bash
exec setuidgid simpson ./homer.sh
Feito. Vamos reiniciar o processo:
# svc -t /etc/service/homer
# ps aux|grep homer
root 3250 0.0 0.1 1620 324 ? S 19:28 0:00 supervise homer
simpson 4004 1.0 0.5 3304 1484 ? S 19:39 0:02 /bin/bash ./homer.sh
Olha que coisa linda. Tudo que precisamos fazer foi falar pro daemontools: Executa esse cara usando esse id aqui. E pronto.
Comandos que você precisa saber:
Parar um serviço:
# svc -d /etc/service/serviço
Reiniciar um serviço parado manualmente:
# svc -u /etc/service/serviço
Enviando um sinal TERM (comportamento equivalente a restart):
# svc -t /etc/service/serviço
Colocando um serviço em pausa:
# svc -p /etc/service/serviço
Continuando um serviço pausado:
# svc -c /etc/service/serviço
Ainda mais
Tem muito mais que pode ser feito em cima do daemontools, combinando com ferramentas como multilog e ucspi-tcp. Dê uma olhada na página the djb way, que foi ostensivamente copiada extensivamente utilizada como referência para este artigo.
E não vamos esquecer do jabá: Conheça o FollowUpTime! Em 5 minutos ou menos você pode se cadastrar e monitorar um site ou serviço gratuitamente!
Ótima dica. Parabéns!
Cara muito boa solução… Assim até já pensei em algumas ferramentas que nem tinha pensado em criar ainda.
Boa dica!
A mim parece que você está a reinventar a roda ao usar o “svc”, ao menos que só use janelas. Os scripts na rc.d e init.d (depende da distro) não são para decoração.
@Jorge Sousa
Parabéns. Entrou pra minha lista de “comentários mais imbecis de alguém que não se deu ao trabalho de ler o texto”
Esse artigo ficou demais Eri, parabéns! E ainda tivemos a pérola acima, uauahuahuahuahuahuahua
:O
Muito bom!
Já pensei em uma coisa que posso usar isso 😀
tnks Eri!
ps: só achei que você poderia ser mais educado com o colega ali :/
ahuhauhauahahu
abraços!
http://upstart.ubuntu.com/
Boa… eu usei isso uma vez com um msndump.
Ótima resposta hehehehe
abs
Chico