Português do Brasil English
Devin no Facebook  Devin no Twitter  RSS do Site 
Linux    

Upstart – Scripts Init no Linux – Parte 2


Comentários  10
Visualizações  
184,611

Em nossa primeira parte, falamos sobre os sistemas de init BSD-like SystemV. Nesta segunda parte, vamos falar sobre um sistema mais moderno: upstart. Se você não leu a pequena introdução sobre os sistemas init na primeira parte, recomendo dar uma olhada antes de começar: vale a pena!

Como vimos anteriormente, apesar do SystemV ser simples, ele tinha algumas limitações que não faziam mais sentido em uma era de computadores mais potentes e modernos (afinal, esse SystemV veio da década de 1980 e 1990!). É por isso que resolveram criar alternativas mais poderosas (que acabaram ficando um pouco mais complexas, mas nada que a gente não dê conta né?). Como dito no tutorial anterior, alguns das principais vantagens desses novos sistemas init:

  • Poder executar serviços paralelamente;
  • Lidar melhor com as dependências entre um serviço e outro (exemplo: precisa de rede para iniciar um servidor web);
  • Adicionar ou remover dispositivos (pendrives, discos, entre outros) e recarregar certos serviços quando isso ocorrer;
  • Não depender apenas de um arquivo com o PID para saber se o serviço está rodando bem…

Mas não é só isso, esses sistemas tem muitos e muitos recursos e e detalhes novos. Neste tutorial vamos ver o necessário para conhecê-los e nos familiarizar com as diferenças de antes para agora.

Upstart

O Upstart foi concedido para resolver as limitações do SystemV init para a distribuição Ubuntu e logo foi adotado por outras distribuições Linux. Seu princípio é ser baseado em eventos: o Upstart cria um ou vários eventos e os serviços podem ser associados à estes eventos. Ele é quem define o que fazer quando um evento começa, muda ou termina (por exemplo: iniciando e parando serviços).

Se vários serviços estão dentro de um evento e ele ocorre, o Upstart pode por exemplo, iniciar todos estes serviços paralelamente. Se um evento tem que ocorrer apenas depois de outro, quando o primeiro evento terminar, ele começa a rodar este outro dependente. Mas vamos ver isso melhor.

Como antes, quando o kernel é iniciado, ele chama o daemon do Upstart, o /sbin/init. Este daemon, quando iniciado, carrega todos os arquivos de configuração do diretório /etc/init. São todos os arquivos que tem extensão .conf. Cada um desses arquivos define um Job. Este Job significa uma tarefa ou serviço. Vale notar também que qualquer alteração nos arquivos vai ser imediatamente lida pelo Upstart, sem precisar recarregar ou algo parecido.

Exemplo simples de um arquivo de configuração de job:

  • /etc/init/hostname.conf
# hostname - set system hostname
#
# This task is run on startup to set the system hostname from /etc/hostname,
# falling back to "localhost" if that file is not readable or is empty and
# no hostname has yet been set.

description     "set system hostname"

start on startup

task
exec hostname -b -F /etc/hostname

Esta configuração de Job serve para, na inicialização da máquina, o sistema definir o seu nome lendo o arquivo /etc/hostname. O que podemos ver de cara no exemplo:

  • Linha 7: uma descrição simples do que faz o job
  • Linha 9: qual estado executar e quando executar (neste caso, iniciar na inicialização :)
  • Linha 11: a configuração é uma tarefa: algo que vai ser executado e depois termina sozinho (exemplo: um shell script)
  • Linha 12: O comando que será executado por esta tarefa

Uma tarefa bem simples, não? Todos os scripts que tem a linha start on startup são os primeiros a serem executados. O que acontece é que depois que o init é carregado e lê todas as configurações, ele emite o evento startup. É deste evento que começa toda a cadeia de outros eventos (por exemplo, a tarefa hostname que mostramos).

Vamos pegar o exemplo de um Ubuntu 12.10 e ver funciona essa cadeia de comandos. Primeiro descobrimos que jobs serão executados no evento startup:

# grep -l "start on startup" /etc/init/*
/etc/init/hostname.conf
/etc/init/mountall.conf

O hostname.conf a gente já tinha visto. Mas também temos o mountall.conf! O mountall vai montar todo o filesystem em seus lugares corretos. Dentro do arquivo, temos as seguintes linhas:

# mountall - Mount filesystems on boot
#
# This helper mounts filesystems in the correct order as the devices
# and mountpoints become available.

description     "Mount filesystems on boot"

start on startup
stop on starting rcS

expect daemon
task

emits virtual-filesystems
emits local-filesystems
emits remote-filesystems
emits all-swaps
emits filesystem
emits mounting
emits mounted

# temporary, until we have progress indication
# and output capture (next week :p)
console output

script
   [...]
end script

post-stop script
    [...]
end script

O que temos de novo por aqui que podemos falar?

  • Linha 9: o serviço deve parar quando estiver iniciando o evento rcS;
  • Linha 11: o upstart vai esperar um daemon ser executado, assim ele saberá qual o seu PID (que pode ser variável devido à execuções fork);
  • Linhas 14-20: Opa! As linhas com emit emitem eventos para todo o Upstart.

Então quando o evento mountall for chamado, ele chama outros eventos. Nestes outros eventos, estão outros serviços e tarefas que serão executados. E é assim que o Upstart trata as dependências de serviços e a ordenação dos mesmos: com eventos e estados de jobs.

Resumo da inicialização

Podemos resumir que a inicialização acontece então nesta ordem:

  1. O upstart é iniciado pelo kernel, executando o /sbin/init;
  2. O upstart lê todos os arquivos .conf do diretório /etc/init e define todos os jobs;
  3. O upstart emite o evento startup;
  4. Os jobs hostname e mountall são executados por causa do evento startup;
  5. O job mountall emite os eventos: virtual-filesystems, local-filesystems, e por aí vai;
  6. Devido ao evento virtual-filesystems…
    1. O job procps é iniciado (configura os sysctls do arquivo /etc/sysctl.conf);
    2. O job udev é iniciado (daemon udev que gerencia os eventos do kernel);
  7. Devido ao evento local-filesystems…
    1. O job dbus é iniciado (daemon de mensagens d-bus);
    2. O job networking é iniciado (interfaces de rede) apenas se os jobs udev-container ou container estiverem parados;
    3. O job networking emite os eventos: static-network-up e net-device-up;
    4. Devido ao evento static-network-up…
      1. O job procps pode ser iniciado (se já não foi antes);
      2. O job rc-sysinit é iniciado (serviços em runlevels, assim como no SystemV)…
        1. Emite o evento runlevel, com o runlevel padrão 2 (em sistemas Debian);
        2. Todos os jobs que estão inscritos no evento runlevel [2] são iniciados;
        3. O job rc é iniciado (compatibilidade com o SystemV;
  8. Devido ao evento net-device-up…
    1. O job mountall-net é iniciado (monta sistemas de arquivos remotos);
    2. O job upstart-socket-bridge é iniciado (recebe eventos em socket e retransmite pro upstart);
  • Devido ao evento e por aí vai…
  • Cara! Como isso ficou complicado! Como eu havia dito antes, a gente ganha muito poder mas aumenta a complexidade :) Tente imaginar uma árvore, com o tronco sendo o Upstart, os vários galhos sendo os eventos e as folhas são os jobs. Se um “galho” é chamado, todas as folhas desse galho podem ser executadas. Há um sistema de dependências muito grande e complexo, mas pra começar pode imaginar desse jeito que ajuda ;)

    Runlevels

    Como deu pra perceber no resumo da inicialização, os jobs mais simples e que não tem muitas dependências podem obedecer o sistema de runlevels, criado pelo rc-sysinit. Veja o exemplo do job atd:

    • /etc/init/atd.conf
    # atd - deferred execution scheduler
    #
    # at is a standard UNIX program that runs user-specified programs at
    # scheduled deferred times
    
    description     "deferred execution scheduler"
    
    start on runlevel [2345]
    stop on runlevel [!2345]
    
    expect fork
    respawn
    
    exec atd
    
    • Ele vai ser iniciado quando o evento runlevel for chamado com o nível 2, 3, 4 ou 5.
    • Ele vai ser parado quando o evento runlevel for chamado com o nível que não seja 2, 3, 4 ou 5.

    Outras coisas legais de se falar sobre os runlevels:

    • Se você mudar a variável DEFAULT_RUNLEVEL do arquivo rc-sysinit.conf, você muda o runlevel inicial do sistema. É a mesma coisa que alterar o initdefault do /etc/inittab no SystemV. Inclusive, o Upstart lê um possível /etc/inittab para procurar por essa linha também;
    • O job rc-sysinit também procura na linha do kernel (/proc/cmdline) por níveis de execução (s para single, 2 para runlevel 2, e por aí vai) e o usa para o runlevel inicial;
    • Um dos jobs que é chamado em todo runlevel é o rc, que é o modo de compatibilidade com o SystemV;
    • O comando telinit emite um evento com o runlevel desejado, exemplo: telinit 3 emite o evento runlevel [3].

    Compatibilidade com o SystemV init

    O modo com que o Upstart funciona faz dele bem flexível e abstrato. Por isso, dá para você pensar e montar esquemas de gerência de serviços diversos. Um dos principais esquemas de serviços é justamente a compatibilidade com o SystemV init, que vimos na primeira parte do turorial. Imagine, se não colocassem uma compatibilidade, muita coisa poderia parar de funcionar de uma versão de distribuição para outra. Acho que ninguém ia querer isso né?

    Uma das coisas que o job rc-sysinit chama através do evento runlevel é o job rc. Este job, que é o responsável pela compatibilidade SystemV, executa o script /etc/init.d/rc $RUNLEVEL, onde $RUNLEVEL é o runlevel atual. Funciona igualzinho ao SystemV. Compare com a parte 1 do tutorial.

    Em outras palavras, em sistemas novos com Upstart você não precisa se preocupar se existirem arquivos de start/stop dentro do /etc/init.d. Inclusive o comando service também funcionará perfeitamente. Mas se você estiver fazendo um serviço novo para as distribuições com Upstart, é recomendável usar o Upstart.

    Manipulando os serviços

    Até agora aprendemos como o Upstart funciona e como definir os seus serviços. Mas e para gerenciá-los?

    O comando do momento aqui é o initctl. Com ele você consegue gerenciar os serviços já existentes. Vamos aos exemplos:

    Listando todos os serviços disponíveis:

    initctl list
    

    (E não apenas lista, como mostra o status de cada um)

    Iniciando um serviço:

    initctl start job
    # ou o antigo
    service <job> start
    

    (O script verifica tanto o estilo SystemV quanto os jobs do Upstart no /etc/init)

    Parando um serviço:

    initctl stop job
    # ou o antigo
    service <job> stop
    

    Reiniciando um serviço:

    initctl restart job
    # ou o antigo
    service <job> restart
    

    Mostrando o status de um serviço:

    initctl status job
    # ou o antigo
    service <job> status
    

    Emitindo eventos

    Como tudo no Upstart são eventos, você pode querer enviar eventos para o sistema. Estes eventos que você enviar manualmente serão lidos por todos os jobs, e aqueles que estão esperando uma ação de start ou stop deste evento serão executados.

    Para enviar um evento pra todo o Upstart:

    initctl emit eusouumeventolegal
    

    Um exemplo legal seria criar um evento chamado “lamp” (Linux + Apache + MySQL + PHP). Para os jobs do apache e mysql, você colocaria um start on lamp e eles serão executados quando você enviar um:

    initctl emit lamp
    

    Os estados dos jobs / serviços

    Ao fazer um initctl status job, podemos ver o estado do serviço, ou seja, em que fase de execução está o job. Um job pode ter duas ações: start e stop. Cada uma dessas ações também tem seus estados e significam em que ponto do start ou do stop o job está. Veja uma lista desses estados:

    • waiting: Estado inicial (esperando algo acontecer);
    • starting: Está para ser iniciado;
    • pre-start: Executando a seção pre-start (preparação para iniciar);
    • spawned: Executando a seção exec ou script
    • post-start: Executando a seção post-start (depois que iniciou)
    • running: O serviço está rodando
    • pre-stop: Executando a seção pre-stop (preparação para parar)
    • stopping: O serviço está parando
    • killed: O serviço já parou, agora o job está quase para parar
    • post-stop: Executando a seção post-stop (depois que parou)

    Alguns exemplos:

    # initctl status tty1
    tty1 start/running, process 1265
    

    O job tty1, que fornece a console 1 (CTRL+ALT+F1) está na fase de start (iniciar) e está em execução com o PID 1265.

    # initctl status mountall
    mountall stop/waiting
    

    O job mountall está na fase de stop (parou) e aguardando algo acontecer/mudar. Muitos jobs estarão neste estado, e isso pode significar que o job já foi executado uma vez (como é o caso desse exemplo).

    Ligando e desligando serviços na inicialização

    Nos sistemas SystemV, desligar ou ligar serviços na inicialização significava mexer nos links simbólicos do diretório /etc/rcX.d (onde o X é o runlevel). No Upstart, as coisas mudam um pouco. Não há mais shell scripts de execução, apenas configurações de job do /etc/init. O que fazer para impedir que eles sejam executados?

    Primeiro identifique se o job é do tipo Upstart ou está utilizando o sistema legado SystemV.

    Caso o serviço já tiver sido convertido para Upstart, o comando initctl status vai ficar assim:

    # initctl status rsyslog
    rsyslog start/running, process 579
    

    Se for do tipo SystemV, vai ficar assim:

    # initctl status nscd
    initctl: Unknown job: nscd
    

    (Não existe o job no Upstart (unknown job) mas existe o script /etc/init.d/nscd)

    Se o script for do tipo SystemV, utilize os mesmos procedimentos da primeira parte deste tutorial.

    Se for um job Upstart, você tem duas alternativas…

    Desabilitando do boot

    • Desde o Upstart 1.3, crie um arquivo de override para o job da seguinte forma:
      # echo "manual" >> /etc/init/job.override
      

      O parâmetro manual vai fazer com que o serviço só seja iniciado manualmente. O arquivo override é um jeito de complementar a configuração do job sem ter que modificar o arquivo original;

    • Ou se a versão for mais antiga, comente (colocar #) todas as linhas que começam com start na configuração do job (/etc/init/job.conf).

    Habilitando no boot

    Partindo do suposto de que o serviço já está desabilitado:

    • Desde o Upstart 1.3, apague o arquivo override:
      # rm -f /etc/init/job.override
      

      Ou se o arquivo override não existir, verifique e retire a linha manual dentro da configuração do job;

    • Ou se a versão for mais antiga, descomente (tirar #) todas as linhas que começam com start na configuração do job (/etc/init/job.conf).

    Um serviço de exemplo

    Vamos criar um serviço! Adivinha como ele vai se chamar?

    • /etc/init/linuxnaveia.conf
    # linuxnaveia - um exemplo de serviço devin
    #
    # Este serviço não faz nada demais, é só um exemplo para demonstrar
    # como o Upstart funciona ;-)
    #
    
    description     "um exemplo de serviço devin"
    
    # quero iniciar nos runlevels mais legais
    start on runlevel [2345]
    
    # quero parar só no halt e reboot
    stop on runlevel [06]
    
    # antes de iniciar, preciso criar um diretorio
    pre-start script
        /bin/mkdir -p /tmp/linuxnaveia
    end script
    
    # este é o serviço que lê um log e coloca em um arquivo (inutil) :P
    # o script vai ser interpretado por uma shell
    script
      exec tail -f /var/log/syslog > /tmp/linuxnaveia/syslog
    end script
    
    # não preciso mais do meu diretório
    post-stop script
        /bin/rm -rf /tmp/linuxnaveia
    end script
    

    Salvei. Pronto! Meu serviço já está instalado. Vamos ver o estado dele:

    # initctl status linuxnaveia
    linuxnaveia stop/waiting
    

    Agora pra não ter que reiniciar, vamos iniciar o serviço na mão:

    # initctl start linuxnaveia
    linuxnaveia start/running, process 4803
    

    Vamos ver o que meu serviço está criando:

    # ls -lha /tmp/linuxnaveia/
    total 12K
    drwxr-xr-x 2 root root 4.0K Apr 10 06:06 .
    drwxrwxrwt 3 root root 4.0K Apr 10 06:06 ..
    -rw-r--r-- 1 root root 1.3K Apr 10 06:06 syslog
    
    # ps aux | grep 4803
    root      4803  0.0  0.0   4344   356 ?        Ss   06:06   0:00 tail -f /var/log/syslog
    

    Ele está rodando no PID 4803 (como o Upstart falou), criou um diretório /tmp/linuxnaveia e o tail está alimentando o arquivo syslog… Ótimo. Mas esse serviço é tão inútil que eu resolvo então pará-lo:

    # initctl stop linuxnaveia
    linuxnaveia stop/waiting
    
    # ls -lha /tmp/linuxnaveia
    ls: cannot access /tmp/linuxnaveia: No such file or directory
    
    # ps aux | grep 4803
    (nada)
    

    Ele parou o tail e removeu o diretório. Tudo como eu disse que ele tinha que fazer. Bom menino!

    Percebeu como foi mais fácil fazer o serviço, do que seria no SystemV?

    • Não precisei que meu serviço gravasse o PID em algum lugar ou algo assim para saber dele ou pará-lo;
    • Só me preocupei mesmo com o que o serviço faz, o resto das funcionalidades (start, stop, status, runlevel, etc) o Upstart cuidou pra mim;
    • Não precisei fazer muito shell-script.

    Até que é fácil, não acham? ;-)

    Referências


    Comentários  10
    Visualizações  
    184,611


    TagsLeia também

    Apaixonado por Linux e administração de sistemas. Viciado em Internet, servidores, e em passar conhecimento. Idealizador do Devin, tem como meta aprender e ensinar muito Linux, o que ele vem fazendo desde 1997 :-)


    Leia também



    Comentários

    10 respostas para “Upstart – Scripts Init no Linux – Parte 2”

    1. marciocbravim disse:

      O criador do "The Linux Manual" !!!!!O criador do "The Linux Manual"!!! Gostei do artigo!
      Rapaz! faz alguns anos que não vejo/escuto seu nome!
      Sou seu admirador de muitos anos! Tive seu manual ainda nos anos 1990s! Acho que foi em 1998 ou 1999!
      Sou Marcio Bravim (antigo Marcio Katan – este nome "katan" estava dando azar, aí troquei!).
      Sou instrutor GNU/Linux LPI 301no Rio de Janeiro.
      Um grande abraço!

      • eitchugo disse:

        Oi Marcio :D Que bom que gostou! Esse ano eu estou me empenhando bastante para escrever várias coisas interessantes aqui no Devin, então não deixe de acompanhar… Fico feliz em ter ajudado nesses anos :-) O TLM é de 1998 mesmo, faz tempo hein? hehehehehe

        Abraços!

    2. Haldrin Figueiredo disse:

      Puts ! Quanto tempo cara ! ainda bem que encotrei teu site. Favoritado e Divugando…… Abraços e parabéns por essa jornada Linux, sou teu fan das antigas e de carteirinha hehehehe.

    3. _JF disse:

      Estou gostando desta série, gostaria de ver um comparativo entre o systemd e o upstart, pois são os principais inits hoje em dia e o systemd parece estar tendo maior aceitação, o upstart só vi sendo usado pelo ubuntu e agora também pelo debian 7. Parabéns pelo site, está com excelente conteúdo e também muito bonito.

      (acredito que você irá falar do systemd na parte3, então tem uma pequena correção no primeiro parágrafo a ser feita, lá você diz que nessa parte 2 também irá falar do systemd, se for possível, pode remover essa parte do meu comentário depois de corrigir).

      • eitchugo disse:

        Oi JF, obrigado! Eu realmente ia fazer o systemd nessa parte, mas como o assunto sobre o Upstart ficou grande, resolvi colocar o systemd na parte 3 (que vai ser publicada nessa próxima semana). Fiz a correção no primeiro parágrafo, valeu por avisar!

    4. Ronan Lucio Pereira disse:

      Hugo,
      Parabéns pela excelente qualidade do post.
      Ficou realmente muito bom, muito fácil de entender e abrangeu muito bem o tema.

      Abraço,
      Ronan

    5. [...] Nota: Aqui no Devin temos tutoriais de como incluir comandos/serviços na inicialização e desligamento. Confira nos artigos: Scripts Init no Linux – Parte 1 e Upstart – Scripts Init no Linux – Parte 2. [...]

    6. Dinho Pereira disse:

      Excelente o seu post.
      Gostaria de entender uma coisa: Apenas Ubuntu 12.04 instalado. Fiz todas as configurações no GRUB para evitar que ele apareça na tela pedindo enter para continuar.
      O problema é que, toda vez que o usuário desliga no momento em que o linux está sendo iniciado, ele insiste em aparecer a tela do grub. Pensei que esta solução pudesse resolver. Tem alguma sugestão?

    Deixe uma resposta