2. Criando o primeiro serviço: Unit, Service e Install
2.1 Uma aplicação de demonstração
Vamos criar um coletor Python que escreve mensagens a cada cinco segundos e atende a pedidos de encerramento.
sudo mkdir -p /opt/coletor
sudo useradd --system --home-dir /opt/coletor --shell /usr/sbin/nologin coletor
Se a conta
coletorjá existir, não repita o comando de criação. O caminho denologinpode variar entre distribuições; verifique comcommand -v nologin.
Crie /opt/coletor/coletor.py:
#!/usr/bin/env python3
import logging
import signal
import time
executando = True
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
def encerrar(signum, frame):
global executando
logging.info("Sinal %s recebido", signum)
executando = False
signal.signal(signal.SIGTERM, encerrar)
signal.signal(signal.SIGINT, encerrar)
logging.info("Coletor iniciado")
while executando:
logging.info("Leitura simulada de sensores")
time.sleep(5)
logging.info("Coletor finalizado normalmente")
Aplique permissões:
sudo chown root:root /opt/coletor/coletor.py
sudo chmod 644 /opt/coletor/coletor.py
python3 /opt/coletor/coletor.py # Ctrl+C para encerrar
Em produção, uma aplicação embarcada real poderia substituir a leitura simulada por aquisição UART, I²C, SPI, GPIO ou publicação MQTT, com as devidas permissões.
2.2 O arquivo /etc/systemd/system/coletor.service
[Unit]
Description=Serviço de coleta de sensores
Documentation=man:systemd.service(5)
After=network.target
[Service]
Type=exec
User=coletor
Group=coletor
WorkingDirectory=/opt/coletor
ExecStart=/usr/bin/python3 /opt/coletor/coletor.py
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Confira o caminho do interpretador com command -v python3. Type=exec é recomendado aqui em versões de systemd que o suportam; ele permite detectar certas falhas da etapa de execução, mas não garante que a aplicação esteja operacionalmente pronta.
2.3 As diretivas de [Unit]
Description=: texto descritivo visível emsystemctl status.Documentation=: referência de manual ou URL.After=eBefore=: ordenação, não ativação automática de dependências.Wants=: dependência fraca; tenta ativar a outra Unit.Requires=: dependência mais forte; em conjunto com ordenação, falhas de inicialização podem impedir o serviço.Conflicts=: unidades incompatíveis durante ativação.
Por exemplo:
[Unit]
Wants=network-online.target
After=network-online.target
network.target não garante internet; network-online.target expressa espera pela rede considerada configurada pelo gerenciador de rede, não conectividade comprovada com um broker MQTT remoto. A aplicação deve implementar tentativas de reconexão.
2.4 As diretivas de [Service]
Tipos de inicialização:
Type= | Uso típico |
|---|---|
simple | Processo de longa duração sem protocolo explícito de prontidão |
exec | Como simple, mas aguarda sucesso da etapa exec |
oneshot | Tarefa finita, conclusão considerada na inicialização |
forking | Daemon clássico que se desanexa em processo filho |
notify | Processo comunica prontidão via sd_notify |
dbus | Prontidão vinculada a aquisição de nome no barramento |
User= e Group= delimitam a identidade do processo, mas não concedem automaticamente acesso a dispositivos ou arquivos. WorkingDirectory= define o diretório de trabalho do processo. ExecStart= indica o programa e argumentos.
Não assuma que ExecStart= é uma linha de shell. Isto não é uma composição shell válida para systemd:
ExecStart=cd /opt/coletor && python3 coletor.py
Prefira:
WorkingDirectory=/opt/coletor
ExecStart=/usr/bin/python3 /opt/coletor/coletor.py
Se realmente necessário, invoque explicitamente um shell, como /bin/sh -c '...', com cuidado na expansão de caracteres.
ExecStartPre= pode verificar pré-condições; ExecStartPost= executa trabalho após a etapa de inicialização conforme o tipo; ExecStop= configura comando de parada opcional. No exemplo Python, ExecStop= é desnecessário: o programa já trata SIGTERM.
Restart=on-failure
RestartSec=5s
Em caso de falha reconhecida, o serviço poderá ser reiniciado após cinco segundos, sujeito aos limites de taxa de inicialização. Uma parada pedida com systemctl stop não aciona essa política.
2.5 Configurando ambiente
Variáveis simples podem ser definidas na Unit:
Environment=MQTT_HOST=192.168.1.50
Environment=MQTT_PORT=1883
Ou por arquivo:
EnvironmentFile=/etc/coletor/coletor.env
Exemplo de arquivo:
MQTT_HOST=192.168.1.50
MQTT_PORT=1883
LOG_LEVEL=INFO
No Python:
import os
host = os.getenv("MQTT_HOST", "localhost")
porta = int(os.getenv("MQTT_PORT", "1883"))
Não trate Environment= ou EnvironmentFile= como armazenamento seguro de segredos. Para credenciais sensíveis, considere LoadCredential= e políticas de acesso adequadas.
2.6 O papel de [Install]
[Install]
WantedBy=multi-user.target
Essa declaração é utilizada em operações como systemctl enable, normalmente produzindo links simbólicos que associam a Unit ao target. Não executa o programa por si só.
2.7 Validar, iniciar e acompanhar
sudo systemd-analyze verify /etc/systemd/system/coletor.service
sudo systemctl daemon-reload
sudo systemctl start coletor.service
systemctl status coletor.service
sudo journalctl -u coletor.service -f
Para obter os logs do boot atual:
sudo journalctl -b -u coletor.service
Para habilitar no boot:
sudo systemctl enable --now coletor.service
systemctl is-enabled coletor.service
Experimento de supervisão
Obtenha o PID principal:
systemctl show -p MainPID --value coletor.service
Somente no ambiente de testes, simule a falha substituindo PID_OBTIDO pelo valor retornado:
sudo kill -KILL PID_OBTIDO
sudo journalctl -u coletor.service -f
Observe a tentativa de reinício. Compare depois com a parada intencional:
sudo systemctl stop coletor.service
Ponto de aprendizagem
O código implementa a lógica do coletor; a Unit especifica política de execução e supervisão. Essa separação simplifica manutenção, testes e operação.