DevOps Admin

Como Usar Gerenciamento de Processos no Linux: systemd, journald e Serviços em Produção Já leu

Entendendo o systemd: O Coração do Linux Moderno O systemd é o sistema de inicialização e gerenciador de serviços que substituiu o antigo init (SysVinit) na maioria das distribuições Linux modernas. Ele vai muito além de simplesmente iniciar processos: gerencia dependências entre serviços, controla recursos de sistema, coordena montagens de filesystem e fornece logging centralizado através do journald. A razão pela qual o systemd se tornou dominante é sua abordagem paralela na inicialização. Enquanto o init antigo executava scripts sequencialmente, o systemd inicia múltiplos serviços em paralelo, respeitando as dependências declaradas. Isso resulta em tempo de boot significativamente menor e maior flexibilidade no controle de processos. O systemd usa um arquivo de configuração declarativo (unit files) escrito em um formato INI simples, o que o torna muito mais legível e manutenível que scripts shell. Unit Files: A Estrutura de Configuração Um unit file é um arquivo de texto que descreve como o systemd deve gerenciar um serviço, socket, timer ou

Entendendo o systemd: O Coração do Linux Moderno

O systemd é o sistema de inicialização e gerenciador de serviços que substituiu o antigo init (SysVinit) na maioria das distribuições Linux modernas. Ele vai muito além de simplesmente iniciar processos: gerencia dependências entre serviços, controla recursos de sistema, coordena montagens de filesystem e fornece logging centralizado através do journald.

A razão pela qual o systemd se tornou dominante é sua abordagem paralela na inicialização. Enquanto o init antigo executava scripts sequencialmente, o systemd inicia múltiplos serviços em paralelo, respeitando as dependências declaradas. Isso resulta em tempo de boot significativamente menor e maior flexibilidade no controle de processos. O systemd usa um arquivo de configuração declarativo (unit files) escrito em um formato INI simples, o que o torna muito mais legível e manutenível que scripts shell.

Unit Files: A Estrutura de Configuração

Um unit file é um arquivo de texto que descreve como o systemd deve gerenciar um serviço, socket, timer ou outro recurso do sistema. A localização padrão para unit files do sistema é /etc/systemd/system/ para customizações locais e /usr/lib/systemd/system/ para arquivos padrão da distribuição. Cada unit file segue uma estrutura de seções entre colchetes, com pares chave-valor simples.

Vamos criar um exemplo prático. Imagine que você tem uma aplicação Python que deve rodar como serviço:

# /etc/systemd/system/meu-app.service
[Unit]
Description=Minha Aplicação Python
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/meu-app
ExecStart=/usr/bin/python3 /opt/meu-app/main.py
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Agora vamos dissecar cada seção. A seção [Unit] define metadados sobre o serviço. A diretiva Description é apenas para legibilidade humana. After=network.target especifica que este serviço deve iniciar apenas depois que o target network.target for alcançado. Wants=network-online.target declara uma dependência fraca (se falhar, não afeta este serviço).

A seção [Service] é onde a mágica acontece. Type=simple significa que o systemd considera o serviço ativo quando o processo principal é iniciado (sem forking). Outros tipos incluem forking, oneshot e notify. User=appuser executa o serviço com esse usuário específico. ExecStart é o comando que inicia o serviço. Restart=on-failure faz o systemd reiniciar automaticamente se o processo terminar com código de erro não-zero. StandardOutput=journal e StandardError=journal redirecionam a saída para o journal (que veremos em detalhes na próxima seção).

A seção [Install] define como este unit é ativado. WantedBy=multi-user.target significa que quando você ativa este serviço, um symlink será criado em /etc/systemd/system/multi-user.target.wants/, fazendo com que ele inicie no boot do sistema.

Operações Básicas com Serviços

Depois de criar o arquivo acima, você deve recarregar a configuração do systemd:

sudo systemctl daemon-reload

Para iniciar o serviço imediatamente:

sudo systemctl start meu-app

Para parar:

sudo systemctl stop meu-app

Para habilitá-lo no boot (criar o symlink em targets.wants):

sudo systemctl enable meu-app

Para desabilitá-lo:

sudo systemctl disable meu-app

Para ver o status atual:

sudo systemctl status meu-app

Para ver logs em tempo real:

sudo journalctl -u meu-app -f

journald: Logging Centralizado e Poderoso

O journald é o subsistema de logging do systemd. Diferentemente do syslog tradicional que escreve em arquivos de texto plano em /var/log/, o journald armazena logs em um formato binário estruturado em /var/log/journal/. Esta abordagem oferece inúmeras vantagens: busca eficiente, menor consumo de disco (com compressão), retenção automática baseada em tempo ou tamanho, e campos estruturados para consultas complexas.

Cada entrada de log no journal é um conjunto de pares chave-valor. Além do texto da mensagem, há campos padrão como PRIORITY, SYSLOG_IDENTIFIER, _PID, _UID, _GID, e muitos outros. Campos que começam com underscore são adicionados pelo sistema, enquanto campos customizados podem ser adicionados por aplicações.

Consultando Logs com journalctl

O comando journalctl é sua ferramenta principal para interagir com o journal. Vamos aos exemplos práticos:

# Ver todos os logs (a partir do mais antigo)
journalctl

# Ver apenas logs de hoje
journalctl --since today

# Ver logs dos últimos 30 minutos
journalctl --since "30 minutes ago"

# Ver logs de um serviço específico
journalctl -u nginx

# Ver logs de um PID específico
journalctl _PID=1234

# Ver logs de um usuário específico
journalctl _UID=1000

# Ver logs de um boot específico
journalctl -b -1  # boot anterior

# Ver logs seguindo em tempo real (como tail -f)
journalctl -f

# Ver logs em formato JSON (ideal para parsing)
journalctl -u meu-app -o json

# Ver logs de prioridade error ou superior
journalctl -p err

# Combinar múltiplos critérios com AND
journalctl -u nginx _HOSTNAME=servidor1 --since "2024-01-15"

Configurando a Retenção de Logs

A retenção padrão do journal é geralmente 10% do tamanho do disco ou 4GB, o que for menor. Você pode customizar isso em /etc/systemd/journald.conf:

# /etc/systemd/journald.conf
[Journal]
Storage=persistent
Compress=yes
MaxRetentionSec=30day
MaxDiskUse=2G
ForwardToSyslog=no

Storage=persistent garante que os logs persistem entre boots. Compress=yes ativa compressão zstd. MaxRetentionSec=30day limita a retenção a 30 dias. MaxDiskUse=2G restringe o uso de disco a 2GB. Após modificar, reinicie o journal:

sudo systemctl restart systemd-journald

Enviando Logs Customizados para o Journal

Aplicações podem enviar logs diretamente para o journal usando a biblioteca libsystemd. Em Python, use o módulo systemd.journal:

from systemd import journal
import logging

# Usando logging padrão do Python
logger = logging.getLogger('meu-app')
handler = journal.JournalHandler()
logger.addHandler(handler)

# Configurar nível de log
logger.setLevel(logging.DEBUG)

# Agora todos os logs vão para o journal
logger.info("Aplicação iniciada")
logger.error("Erro crítico detectado")
logger.debug("Informação de debug")

Após isso, você pode consultar os logs com:

journalctl -u python -f

Gerenciamento Avançado de Processos

O systemd oferece recursos sofisticados de gerenciamento de recursos e comportamento de processos que vão além do controle básico de inicialização e parada.

Controle de Recursos com cgroups

O systemd integra control groups (cgroups) do kernel para limitar recursos consumidos por serviços. Você pode limitar CPU, memória, I/O de disco e muito mais:

# /etc/systemd/system/recurso-intensivo.service
[Unit]
Description=Serviço com Limite de Recursos
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/meu-processador-pesado
Restart=on-failure

# Limite de memória: máximo 512MB
MemoryLimit=512M

# Limite de CPU: máximo 50% de um core
CPUQuota=50%

# Limite de I/O de disco
IOWeight=200

# Timeout de parada: se não parar em 30s, mata o processo
TimeoutStopSec=30s

[Install]
WantedBy=multi-user.target

Você pode verificar os limites aplicados:

# Ver informações de cgroups do serviço
systemctl status recurso-intensivo

# Ver uso atual de memória
systemctl show recurso-intensivo -p MemoryCurrent

# Ver limite de memória
systemctl show recurso-intensivo -p MemoryLimit

Gerenciamento de Dependências e Ordering

O systemd permite declarar relacionamentos complexos entre units através de Before, After, Requires, Wants e BindsTo. Vamos a um exemplo prático com múltiplos serviços:

# /etc/systemd/system/banco-dados.service
[Unit]
Description=PostgreSQL Database
After=network.target

[Service]
Type=simple
User=postgres
ExecStart=/usr/lib/postgresql/13/bin/postgres -D /var/lib/postgresql/13/main
Restart=on-failure

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/api-backend.service
[Unit]
Description=API Backend
After=banco-dados.service
Requires=banco-dados.service

[Service]
Type=simple
User=appuser
ExecStart=/opt/api/start.sh
Restart=on-failure
TimeoutStartSec=60s

[Install]
WantedBy=multi-user.target

Neste cenário, api-backend.service especifica que banco-dados.service é obrigatório (Requires). Se o banco de dados falhar, a API será parada automaticamente. After=banco-dados.service garante que o banco será iniciado primeiro.

Sockets e Ativação por Demanda

Um recurso poderoso do systemd é iniciar serviços apenas quando necessário. Você pode definir um socket unit que ativa o serviço quando uma conexão chega:

# /etc/systemd/system/meu-daemon.socket
[Unit]
Description=Meu Daemon Socket
Before=meu-daemon.service

[Socket]
ListenStream=9000
Accept=no

[Install]
WantedBy=sockets.target
# /etc/systemd/system/meu-daemon.service
[Unit]
Description=Meu Daemon
Requires=meu-daemon.socket
After=meu-daemon.socket

[Service]
Type=simple
ExecStart=/opt/daemon/server
StandardInput=socket

Agora o daemon só inicia quando alguém conecta na porta 9000. Isso economiza recursos em servidores com múltiplos serviços.

Debugging e Solução de Problemas

Quando algo dá errado, o systemd fornece várias ferramentas para diagnóstico. Primeiro, sempre verifique o status detalhado:

# Ver status com output recente
systemctl status meu-app

# Ver logs específicos do serviço
journalctl -u meu-app -n 50  # últimas 50 linhas

# Ver logs em tempo real
journalctl -u meu-app -f

# Ver todo o output do último start/stop
journalctl -u meu-app --since "1 hour ago"

# Ver se há erros de sintaxe no unit file
systemd-analyze verify /etc/systemd/system/meu-app.service

Se um serviço está marcado como "failed", investigue com:

# Obter mais detalhes sobre o status
systemctl show meu-app

# Reiniciar e observar logs
systemctl restart meu-app && journalctl -u meu-app -f

Um comando muito útil para verificar o tempo de boot e a ordem de inicialização é:

# Ver análise de boot
systemd-analyze

# Ver serviços mais lentos no boot
systemd-analyze blame

# Ver gráfico visual das dependências
systemd-analyze plot > boot.svg

Se um serviço entra em loop de restart, configure StartLimitBurst e StartLimitIntervalSec:

[Service]
Type=simple
ExecStart=/opt/app/run
Restart=on-failure
StartLimitBurst=3
StartLimitIntervalSec=60s

Isso faz o systemd dar up se o serviço falhar 3 vezes em 60 segundos.

Conclusão

O domínio efetivo do systemd repousa em três pilares fundamentais: entender unit files como configurações declarativas que descrevem o comportamento desejado (não imperativos como scripts), saber consultar e interpretar logs no journal para debugging eficiente, e conhecer os recursos de controle de recursos e dependências para arquiteturas complexas de múltiplos serviços. Estes conhecimentos transformam você de alguém que apenas "inicia e para" serviços para um profissional capaz de projetar arquiteturas robustas e resilientes no Linux moderno.

Referências


Artigos relacionados