Dominando Arquivos de Serviço Systemd: Um Guia Abrangente
Crie arquivos de serviço systemd confiáveis com seções de unidade corretas, comportamento de reinicialização, logs, segurança e temporizadores.
Dominando Arquivos de Serviço do Systemd: Um Guia Abrangente
Os arquivos de serviço do Systemd informam ao Linux como iniciar, parar, reiniciar e supervisionar sua aplicação. Se o seu serviço inicia manualmente, mas falha na inicialização, reinicia agressivamente demais ou escreve logs no lugar errado, o arquivo de unidade geralmente é onde você precisa olhar.
Este guia foca em criar e configurar arquivos de unidade de serviço do systemd do zero. Você verá as seções principais, um exemplo de serviço Python funcional, comandos comuns de solução de problemas e alguns controles de segurança e recursos que vale a pena usar em produção.
Entendendo os Arquivos de Unidade do Systemd
O Systemd usa arquivos de unidade para descrever vários recursos do sistema, como serviços, soquetes, dispositivos, pontos de montagem e muito mais. Um arquivo de unidade de serviço, normalmente terminando com a extensão .service, define como o systemd deve gerenciar um daemon ou aplicação específica.
Esses arquivos são organizados em seções, com cada seção contendo pares chave-valor representando diretivas de configuração. As seções principais nas quais vamos focar são [Unit], [Service] e [Install].
Anatomia de um Arquivo de Serviço do Systemd
Um arquivo de serviço systemd típico tem a seguinte estrutura:
[Unit]
Description=Uma breve descrição do serviço.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/minha_aplicacao --config /etc/minha_app.conf
Restart=on-failure
User=meuusuario
Group=meugrupo
[Install]
WantedBy=multi-user.target
Vamos detalhar cada seção e suas diretivas comuns:
A Seção [Unit]
Esta seção fornece metadados sobre a unidade e define seu relacionamento com outras unidades. É usada para dependências e ordenação.
Description=: Um nome legível por humanos para o serviço. É o que você verá na saída desystemctl status.Documentation=: URLs ou caminhos para documentação do serviço.Requires=: Define dependências fortes. Se uma unidade listada aqui falhar ao iniciar, esta unidade também falhará ao iniciar.Wants=: Define dependências fracas. Se uma unidade listada aqui falhar ao iniciar, esta unidade ainda tentará iniciar.Before=: Garante que esta unidade inicie antes das unidades listadas.After=: Controla apenas a ordenação. Por exemplo,After=network.targetinicia esta unidade após o alvo de rede básico, mas não garante conectividade externa. Serviços dependentes de rede podem precisar deAfter=network-online.targetmais o serviço wait-online da distribuição.Conflicts=: Se uma unidade listada aqui for iniciada, esta unidade será parada, e vice-versa.
A Seção [Service]
Esta seção configura o comportamento do próprio serviço. É onde você define como iniciar, parar e gerenciar o processo.
Type=: Especifica o tipo de inicialização do processo. Valores comuns incluem:simple(padrão): O processo principal é aquele especificado emExecStart=. O Systemd assume que o serviço é iniciado imediatamente após o processoExecStart=ser bifurcado (fork).forking: O processoExecStart=bifurca um filho, e o pai sai. O Systemd considera o serviço iniciado quando o pai sai. Muitas vezes você precisa especificarPIDFile=com este tipo.oneshot: Similar asimple, mas espera-se que o processo saia após seu trabalho ser concluído. Útil para scripts de configuração.notify: O daemon envia uma mensagem de notificação ao systemd quando foi iniciado com sucesso. Este é o tipo preferido para daemons modernos que o suportam.dbus: O serviço adquire um nome D-Bus.
ExecStart=: O comando a ser executado para iniciar o serviço. Para a maioria dos tipos de serviço, use um comandoExecStart=. Múltiplas linhasExecStart=são válidas paraType=oneshot, onde elas são executadas sequencialmente.ExecStop=: O comando a ser executado para parar o serviço.ExecReload=: O comando a ser executado para recarregar a configuração do serviço sem reiniciar.Restart=: Define quando o serviço deve ser reiniciado automaticamente. Valores comuns:no(padrão): Nunca reiniciar.on-success: Reiniciar apenas se o serviço sair limpo (código de saída 0).on-failure: Reiniciar se o serviço sair com um código de saída diferente de zero, for terminado por um sinal ou exceder o tempo limite.on-abnormal: Reiniciar se for terminado por um sinal ou exceder o tempo limite.on-abort: Reiniciar apenas se for terminado de forma suja por um sinal.always: Sempre reiniciar, independentemente do status de saída.
RestartSec=: O tempo para esperar antes de reiniciar o serviço (padrão é 100ms).User=: O usuário para executar o serviço.Group=: O grupo para executar o serviço.WorkingDirectory=: O diretório para mudar antes de executar os comandos.Environment=: Define variáveis de ambiente para o serviço.EnvironmentFile=: Lê variáveis de ambiente de um arquivo.PIDFile=: Caminho para o arquivo PID (frequentemente usado comType=forking).StandardOutput=/StandardError=: Controla para onde stdout e stderr vão, comojournal,nullouinherit. Em distribuições comuns baseadas em systemd, a saída do serviço normalmente vai para o diário (journal), a menos que os padrões do gerenciador tenham sido alterados.
A Seção [Install]
Esta seção define como a unidade deve ser habilitada ou desabilitada, tipicamente criando links simbólicos.
WantedBy=: Especifica o alvo que deve "querer" este serviço quando ele for habilitado. Valores comuns:multi-user.target: Para serviços que devem iniciar quando o sistema atinge um estado de linha de comando multiusuário.graphical.target: Para serviços que devem iniciar quando o sistema atinge um estado de login gráfico.
Criando Seu Primeiro Arquivo de Serviço do Systemd
Vamos criar um arquivo de serviço simples para um script Python hipotético chamado meu_app.py localizado em /opt/meu_app/meu_app.py.
1. Crie o arquivo de serviço:
Arquivos de serviço para aplicações personalizadas são tipicamente colocados em /etc/systemd/system/. Vamos nomear nosso arquivo meu_app.service.
# Crie o diretório se ele não existir
sudo mkdir -p /etc/systemd/system/
# Crie o arquivo de serviço usando um editor de texto
sudo nano /etc/systemd/system/meu_app.service
2. Adicione o seguinte conteúdo a meu_app.service:
[Unit]
Description=Minha Aplicação Python Personalizada
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/meu_app/
ExecStart=/usr/bin/python3 /opt/meu_app/meu_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Explicação do exemplo:
Description: Identifica claramente nossa aplicação.After=network.target: Garante que a rede esteja disponível antes de iniciar.Type=simple: Assume quemeu_app.pyé o processo principal e não bifurca.User=appuser,Group=appgroup: Especifica o usuário e o grupo sob os quais a aplicação deve ser executada. Certifique-se de que esses usuários e grupos existam em seu sistema e tenham as permissões apropriadas. Você pode precisar criá-los:sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/meu_app/WorkingDirectory: Define o contexto para o script.ExecStart: O comando para executar o script Python. Certifique-se de que/usr/bin/python3é o caminho correto para seu interpretador Python e que o script é executável.Restart=on-failure: Se o script falhar, o systemd tentará reiniciá-lo.WantedBy=multi-user.target: Este serviço será iniciado automaticamente quando o sistema inicializar em um ambiente multiusuário.
3. Recarregue a configuração do gerenciador systemd:
Após criar ou modificar um arquivo de serviço, você deve informar ao systemd para recarregar sua configuração.
sudo systemctl daemon-reload
4. Habilite e Inicie o Serviço:
- Habilitar: Isso faz com que o serviço inicie automaticamente na inicialização.
sudo systemctl enable meu_app.service - Iniciar: Isso inicia o serviço imediatamente.
sudo systemctl start meu_app.service
5. Verifique o Status do Serviço:
Para verificar se seu serviço está em execução e ver quaisquer erros potenciais:
sudo systemctl status meu_app.service
Se houver problemas, o comando status frequentemente mostrará mensagens de erro ou logs do journald.
6. Visualizando Logs:
O Systemd se integra com o journald para registro de logs. Você pode visualizar logs para seu serviço usando:
sudo journalctl -u meu_app.service
Você também pode seguir logs em tempo real:
sudo journalctl -f -u meu_app.service
Outros Comandos Úteis
- Parar o serviço:
sudo systemctl stop meu_app.service - Reiniciar o serviço:
sudo systemctl restart meu_app.service - Recarregar configuração (se suportado pelo app):
sudo systemctl reload meu_app.service - Desabilitar início automático na inicialização:
sudo systemctl disable meu_app.service
Configuração Avançada e Melhores Práticas
Considerações de Segurança
- Execute serviços como usuários não-root: Sempre especifique
User=eGroup=a menos que seja absolutamente necessário. Isso segue o princípio do menor privilégio. - Isole serviços: Considere recursos de sandboxing como
PrivateTmp=true,ProtectSystem=strict,ProtectHome=trueeNoNewPrivileges=true. Teste-os com sua aplicação porque eles podem bloquear gravações legítimas de arquivos.PrivateTmp=true: Dá ao serviço seus próprios diretórios privados/tmpe/var/tmp.ProtectSystem=strict: Torna a maior parte do sistema de arquivos somente leitura para o serviço. UseReadWritePaths=para diretórios nos quais o serviço deve escrever.NoNewPrivileges=true: Impede que o serviço obtenha novos privilégios.
Lidando com Inicializações Complexas
Type=forkingcomPIDFile=: Para aplicações mais antigas que bifurcam, certifique-se de quePIDFile=aponte para o arquivo correto.Type=notify: Se sua aplicação suportar, esta é a maneira mais robusta para o systemd saber quando ela está realmente pronta.ExecStartPre=eExecStartPost=: Comandos para executar antes e depois deExecStart=. Úteis para tarefas de configuração ou limpeza.
Controle de Recursos
O Systemd permite limitar o uso de recursos:
CPUWeight=: Peso relativo de CPU para o serviço.MemoryMax=: Memória máxima que o serviço pode usar.IOWeight=: Peso relativo de E/S onde suportado pelo kernel e configuração do cgroup.
Exemplo:
[Service]
# ... outras diretivas ...
MemoryMax=512M
CPUWeight=50
Temporizadores vs. Cron
Os temporizadores do Systemd oferecem uma alternativa moderna aos trabalhos cron tradicionais. Eles são mais flexíveis e se integram melhor ao gerenciamento de logs e dependências do systemd.
- Cron: Tarefas agendadas definidas em arquivos
crontab. - Temporizadores do Systemd (unidades
.timer): Essas unidades agendam unidades.service. Você define um arquivo.timerque especifica quando um arquivo.servicecorrespondente deve ser executado.
Exemplo:
Para executar um script diariamente às 3h:
meu_script.service: O serviço a ser executado.[Unit] Description=Meu script diário [Service] Type=oneshot ExecStart=/opt/meus_scripts/executar_diario.sh User=scriptusermeu_script.timer: O temporizador que agenda o serviço.[Unit] Description=Executar meu script diário uma vez por dia [Timer] # Executar às 03:00 todos os dias OnCalendar=*-*-* 03:00:00 # Executar logo após a inicialização se o horário agendado foi perdido enquanto a máquina estava desligada. Persistent=true [Install] WantedBy=timers.target
Para usar isso:
- Coloque ambos os arquivos em
/etc/systemd/system/. - Execute
sudo systemctl daemon-reload. - Habilite e inicie o temporizador:
sudo systemctl enable meu_script.timeresudo systemctl start meu_script.timer.
Os temporizadores oferecem vantagens como Persistent=true (executa trabalhos perdidos na inicialização), eventos de calendário (como hourly, daily, weekly) e melhor integração com journalctl.
Solucionando Problemas Comuns
- Serviço não está iniciando: Verifique
systemctl status <nome_do_servico>ejournalctl -u <nome_do_servico>. Procure por erros de digitação, caminhos incorretos, dependências ausentes ou erros de permissão. Type=incorreto: Se um serviço falha imediatamente ou trava, oType=pode estar errado. Tentesimpleouforkinge certifique-se de quePIDFileestá correto se estiver usandoforking.- Permissão negada: Certifique-se de que o
User=eGroup=especificados tenham acesso de leitura/gravação aos arquivos e diretórios necessários. - Variáveis de ambiente: Se sua aplicação depende de variáveis de ambiente específicas, certifique-se de que elas estejam definidas corretamente usando
Environment=ouEnvironmentFile=. - Dependências: Verifique se
After=,Wants=eRequires=correspondem ao que você quer.After=ordena a inicialização; não puxa outra unidade por si só.
Antes de habilitar uma nova unidade em um host de produção, execute:
sudo systemd-analyze verify /etc/systemd/system/meu_app.service
Isso captura muitos erros de sintaxe e diretiva antes que você dependa do serviço na inicialização.
Conclusão Principal
Escreva o menor arquivo de serviço que descreva precisamente seu aplicativo, então adicione política de reinicialização, logs, restrições de segurança e limites de recursos deliberadamente. Após cada alteração, execute systemctl daemon-reload, verifique a unidade e confira systemctl status mais journalctl -u antes de confiar nele em produção.