Systemd – Adicionando scripts na inicialização do Linux

Em diversos projetos e produtos que criamos, com Linux embarcado ou na utilização de servidores Linux para back-end, front-end, banco de dados e etc, se faz necessário ter um gerenciamento completo de certos scripts e programas durante todo o ciclo de vida dele (desde o boot até falhas repentinas). Um caso bem comum, por exemplo, são scripts Python para tratamento de dados recebidos via MQTT.

Seja qual for o motivo, muitos desses scripts devem ser inicializados junto do sistema operacional e permanecerem em execução 100% do tempo, pois se não estiverem em execução, alguma etapa do seu projeto irá parar, podendo gerar perdas incalculáveis. Um grande desafio também é manter esse script monitorado, a fim de reiniciá-lo automaticamente caso pare, executar ações antes e/ou depois de iniciá-los e etc.

O mais comum é adicionar a execução do nosso script/serviço dentro do arquivo “rc.local”, que é inicializado junto do sistema, mas isso tem diversos problemas. Por exemplo: Se nosso script/serviço vier a parar (encerrado pelo usuário, travamento, falta de memória e etc), ele não será reiniciado automaticamente, podendo gerar muitos problemas.

Para solucionar, vamos aprender um pouco sobre o Systemd, um sistema que gerencia quase toda a inicialização do sistema, padrão em diversas distribuições Linux, que também consegue gerenciar todo ciclo de vida de um serviço (nosso script), sendo facilmente configurado com incontáveis parâmetros de configuração.

O que é Systemd?

Systemd é um sistema que gerencia a inicialização de boa parte do sistema operacional e de serviços, estando presente nas mais diversas distribuições, inclusive nos Raspberry Pi’s, sendo que seu aprendizado é muito importante para quem pretende se aprofundar um pouco mais em Linux.

Um processo resumido de boot pode ser composto por:

  1. Bootloader.
  2. Kernel.
  3. Systemd.

Unidades (Units): Unidades são arquivos de texto que contém instruções de inicialização de um recurso, como por exemplo: Serviço, timer, path, mount, automount, socket e etc.

Unidade alvo (Target): Uma unidade alvo é um agrupamento de outras unidades que levam o sistema a um ponto específico no processo de inicialização, indicando que certos serviços e etc (dependências) já foram executados.

Entendendo sobre a árvore de boot

O systemd é focado em alta paralelização e dependências, contendo diversos targets que indicam em qual ponto da inicialização já foi atingido (ou pode ser atingido) e quais as próximas unidades que devem ser executadas para se chegar no target desejado. Sendo comumente comparados aos “runlevels do System V”.


Podemos utilizar esses pontos ao nosso favor para inicializar unidades o mais breve possível, já que estaremos cientes de que certas dependências já foram atendidas. A seguir uma pequena lista de alguns targets importantes do systemd. Uma breve ordem em que cada um ocorre pode ser melhor visto na figura 1 abaixo.

  • sysinit.target: Ponto em que apenas os serviços mais básicos necessários para o restante da inicialização foram executados. (single-user, non-graphical).
  • basic.target: Ponto em que todos file systems, devices, swaps, sockets, timers e etc já foram montados e inicializados. Há todo o necessário para executar daemons e scripts de propósito geral. (single-user, non-graphical).
  • multi-user.target: Ponto em que o sistema já permite multi-usuários (non-graphical) no sistema.
  • graphical.target: Ponto em que o sistema já inicializou a interface gráfica do sistema.
  • default.target: Ponto padrão de inicialização do systemd, sendo apenas um apelido (alias, symlink) para o multi-user.target ou graphical.target. Ao fim de todo processo de inicialização, o sistema chega ao ponto “default.target” e está pronto para uso.
  • network.target: Ponto em que o mínimo necessário para o funcionamento da parte de redes do computador já foi atingido. Atenção, isso não indica que as interfaces já obtiveram IP e etc. Veja com mais detalhes nas referências.
  • rescue.target: Inicia o sistema com o terminal no modo “rescue”, apenas com o file system e serviços básicos necessários para alguma manutenção.
  • emergency.target: Inicia o sistema com o terminal no modo “emergency”, sendo o modo mais simples possível, iniciando o menor número de serviços e normalmente deixando o file system apenas em “read-only”. Mais utilizado para encontrar problemas ao inicializar o sistema operacional.
  • reboot.target: Leva o sistema para o estado de desligamento e o reinicia.
  • poweroff.target: Leva o sistema para o estado de desligado.

Como foi dito, é um sistema de dependências, logo, conseguimos fazer algumas coisas muito interessantes, como por exemplo: Executar comandos/scripts na falha de outro serviço ou até antes do reinicio/desligamento do sistema. Isso facilita bastante a gerência de servidores, onde a parada de um serviço (banco de dados, broker e etc) deve ser notificada e assim por diante.

Systemd
Figura 1: Processo de inicialização do systemd resumido em uma árvore.

Comandos relevantes para gerenciamento do systemd

Systemd
Figura 2: Status de um serviço de Watchdog no Raspberry Pi Zero W.

  • systemctl status: Mostra detalhes sobre o serviço, como log, se está em execução e etc.
  • systemctl start: Inicia o serviço.
  • systemctl stop: Encerra o serviço.
  • systemctl enable: Adiciona o serviço na inicialização do Linux.
  • systemctl disable: Remove o serviço da inicialização do Linux.

Criando um novo serviço para nosso script

Agora que já temos uma noção básica do que é o systemd e seu funcionamento, vamos criar uma nova unidade de serviço para executar nosso script Python automaticamente durante a inicialização do Linux e também garantir que ele fique sempre em execução, sendo reiniciado automaticamente caso venha a ser encerrado.

O script em questão é um supervisório que criei para monitorar estatísticas de CPU, RAM, temperatura e etc de dispositivos Linux. Um simples script Python que coleta informações e salva em um banco de dados localmente na memória RAM (SQLite3) de tempo em tempo. Lembre-se que é apenas um exemplo demonstrativo.

Obtendo o script do github

Criando o arquivo de serviço


Atenção
, os diretórios utilizados se referem ao meu computador, que podem ser diferentes do seu. Altere se necessário, principalmente a pasta do script.

Após a criação do arquivo de serviço, basta iniciá-lo com o comando “systemctl start supix”. Para ver se a execução do serviço foi bem sucedida, veja com comando de status “systemctl status supix”. Podemos notar que o serviço está ativo (running) e não retornou nenhum erro.

Systemd
Figura 3: Novo serviço iniciado.

Agora, queremos que esse script (serviço) seja iniciado junto com o sistema caso o computador venha a ser reiniciado e etc. Execute “systemctl enable supix” e pronto! Nosso serviço será iniciado automaticamente quando o sistema iniciar.

No arquivo de serviço que criamos anteriormente, há algumas das inúmeras configurações disponíveis para uma unidade do systemd. Adicionamos, por exemplo, “Restart=always” e “RestartSec=10”. Isso indica ao Systemd que ele deve reiniciar o serviço após 10 segundos, caso venha a ser encerrado por falhas, OOM Killer ou mesmo o usuário. Para testar isso, encerre o processo manualmente pelo comando “kill” ou até “htop”, você notará que após 10 segundos, o serviço estará ativo novamente. Isso é maravilhoso!

Algumas opções bem interessantes são sobre ordem e dependência de execução. Às vezes precisamos que um serviço só execute após algum outro serviço ou até que só possa ser executado enquanto outro serviço esteja ativo. Para isso, temos as opções “After=” e “Requires=”, respectivamente. Isso é útil, por exemplo, para iniciar um script que monitora dados MQTT do broker Mosquitto, porém, ele só deve ser iniciado após o broker. Logo, adicionamos “After=mosquitto.service”.

Outros exemplos de opções interessantes são para execução de algo antes (ExecStartPre=) ou depois da execução do serviço (ExecStartPost=) , ou até mesmo quando ocorrer uma falha e o serviço encerrar (ExecStopPost=) , como por exemplo, enviar um email! Podemos inclusive reiniciar o sistema inteiro se assim desejar. Se você tiver interesse e precisa de alguma lógica mais profunda para sua unidade, não se esqueça de consultar as referências e documentos do Systemd.

Ainda podemos especificar qual o tipo (type=) de serviço e como ele se comporta diante do sistema:

  • simple: O serviço é iniciado como processo principal, não sendo necessário seu encerramento para o próximo serviço executar.
  • oneshot: O serviço é iniciado e bloqueia o restante da inicialização até seu próprio encerramento.
  • idle: O serviço é iniciado apenas quando todos os outros já foram inicializados.

Saiba mais

Otimização de Tempo de Boot no Linux Embarcado

Yocto Project: Introdução

Referências

https://www.freedesktop.org/software/systemd/man/systemd.unit.html

https://www.freedesktop.org/software/systemd/man/bootup.html

https://www.freedesktop.org/software/systemd/man/systemd.special.html

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Home » Linux Embarcado » Systemd – Adicionando scripts na inicialização do Linux
Comentários:
Notificações
Notificar
guest
0 Comentários
Inline Feedbacks
View all comments
Talvez você goste:
Nenhum resultado encontrado.

Séries

[vc_widget_sidebar sidebar_id=”us_widget_area_series”]
Menu