DevOps & CI/CD

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

14 min de leitura

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

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

<h2>Entendendo o systemd: O Coração do Linux Moderno</h2>

<p>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.</p>

<p>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.</p>

<h2>Unit Files: A Estrutura de Configuração</h2>

<p>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 é <code>/etc/systemd/system/</code> para customizações locais e <code>/usr/lib/systemd/system/</code> para arquivos padrão da distribuição. Cada unit file segue uma estrutura de seções entre colchetes, com pares chave-valor simples.</p>

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

<pre><code class="language-ini"># /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</code></pre>

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

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

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

<h3>Operações Básicas com Serviços</h3>

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

<pre><code class="language-bash">sudo systemctl daemon-reload</code></pre>

<p>Para iniciar o serviço imediatamente:</p>

<pre><code class="language-bash">sudo systemctl start meu-app</code></pre>

<p>Para parar:</p>

<pre><code class="language-bash">sudo systemctl stop meu-app</code></pre>

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

<pre><code class="language-bash">sudo systemctl enable meu-app</code></pre>

<p>Para desabilitá-lo:</p>

<pre><code class="language-bash">sudo systemctl disable meu-app</code></pre>

<p>Para ver o status atual:</p>

<pre><code class="language-bash">sudo systemctl status meu-app</code></pre>

<p>Para ver logs em tempo real:</p>

<pre><code class="language-bash">sudo journalctl -u meu-app -f</code></pre>

<h2>journald: Logging Centralizado e Poderoso</h2>

<p>O journald é o subsistema de logging do systemd. Diferentemente do syslog tradicional que escreve em arquivos de texto plano em <code>/var/log/</code>, o journald armazena logs em um formato binário estruturado em <code>/var/log/journal/</code>. 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.</p>

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

<h3>Consultando Logs com journalctl</h3>

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

<pre><code class="language-bash"># 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 &quot;30 minutes ago&quot;

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 &quot;2024-01-15&quot;</code></pre>

<h3>Configurando a Retenção de Logs</h3>

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

<pre><code class="language-ini"># /etc/systemd/journald.conf

[Journal]

Storage=persistent

Compress=yes

MaxRetentionSec=30day

MaxDiskUse=2G

ForwardToSyslog=no</code></pre>

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

<pre><code class="language-bash">sudo systemctl restart systemd-journald</code></pre>

<h3>Enviando Logs Customizados para o Journal</h3>

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

<pre><code class="language-python">from systemd import journal

import logging

Usando logging padrão do Python

logger = logging.getLogger(&#039;meu-app&#039;)

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(&quot;Aplicação iniciada&quot;)

logger.error(&quot;Erro crítico detectado&quot;)

logger.debug(&quot;Informação de debug&quot;)</code></pre>

<p>Após isso, você pode consultar os logs com:</p>

<pre><code class="language-bash">journalctl -u python -f</code></pre>

<h2>Gerenciamento Avançado de Processos</h2>

<p>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.</p>

<h3>Controle de Recursos com cgroups</h3>

<p>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:</p>

<pre><code class="language-ini"># /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</code></pre>

<p>Você pode verificar os limites aplicados:</p>

<pre><code class="language-bash"># 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</code></pre>

<h3>Gerenciamento de Dependências e Ordering</h3>

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

<pre><code class="language-ini"># /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</code></pre>

<pre><code class="language-ini"># /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</code></pre>

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

<h3>Sockets e Ativação por Demanda</h3>

<p>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:</p>

<pre><code class="language-ini"># /etc/systemd/system/meu-daemon.socket

[Unit]

Description=Meu Daemon Socket

Before=meu-daemon.service

[Socket]

ListenStream=9000

Accept=no

[Install]

WantedBy=sockets.target</code></pre>

<pre><code class="language-ini"># /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</code></pre>

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

<h2>Debugging e Solução de Problemas</h2>

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

<pre><code class="language-bash"># 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 &quot;1 hour ago&quot;

Ver se há erros de sintaxe no unit file

systemd-analyze verify /etc/systemd/system/meu-app.service</code></pre>

<p>Se um serviço está marcado como &quot;failed&quot;, investigue com:</p>

<pre><code class="language-bash"># Obter mais detalhes sobre o status

systemctl show meu-app

Reiniciar e observar logs

systemctl restart meu-app &amp;&amp; journalctl -u meu-app -f</code></pre>

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

<pre><code class="language-bash"># 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 &gt; boot.svg</code></pre>

<p>Se um serviço entra em loop de restart, configure <code>StartLimitBurst</code> e <code>StartLimitIntervalSec</code>:</p>

<pre><code class="language-ini">[Service]

Type=simple

ExecStart=/opt/app/run

Restart=on-failure

StartLimitBurst=3

StartLimitIntervalSec=60s</code></pre>

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

<h2>Conclusão</h2>

<p>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 &quot;inicia e para&quot; serviços para um profissional capaz de projetar arquiteturas robustas e resilientes no Linux moderno.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html" target="_blank" rel="noopener noreferrer">Documentação Oficial do systemd</a></li>

<li><a href="https://www.freedesktop.org/software/systemd/man/journalctl.html" target="_blank" rel="noopener noreferrer">Manual do journalctl</a></li>

<li><a href="https://access.redhat.com/articles/3359321" target="_blank" rel="noopener noreferrer">Red Hat: Working with systemd</a></li>

<li><a href="https://wiki.archlinux.org/title/systemd" target="_blank" rel="noopener noreferrer">Arch Linux Wiki: systemd</a></li>

<li><a href="https://man7.org/tlpi/" target="_blank" rel="noopener noreferrer">The Linux Programming Interface - Michael Kerrisk (Capítulo sobre systemd)</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em DevOps & CI/CD

Boas Práticas de Cron, at e Automação de Tarefas Agendadas no Linux para Times Ágeis
Boas Práticas de Cron, at e Automação de Tarefas Agendadas no Linux para Times Ágeis

Entendendo Agendamento de Tarefas no Linux O agendamento de tarefas é um dos...

Boas Práticas de Services, Ingress e Exposição de Aplicações em Kubernetes para Times Ágeis
Boas Práticas de Services, Ingress e Exposição de Aplicações em Kubernetes para Times Ágeis

Services: A Base da Comunicação em Kubernetes Um Service em Kubernetes é um r...

Container Registry: Docker Hub, GHCR e Registry Privado com Harbor: Do Básico ao Avançado
Container Registry: Docker Hub, GHCR e Registry Privado com Harbor: Do Básico ao Avançado

O que é um Container Registry Um Container Registry é um repositório centrali...