Entendendo os Targets do Systemd: Conceitos Essenciais Explicados

Entenda os targets do systemd, targets de inicialização padrão, mapeamentos de runlevels, isolamento, targets personalizados e comandos de solução de problemas.

Entendendo os Targets do Systemd: Conceitos Essenciais Explicados

Os targets do systemd são mais fáceis de entender se você parar de pensar neles como serviços. Um serviço inicia um processo. Um target agrupa unidades em um estado nomeado do sistema. Quando uma máquina inicializa para multi-user.target, o systemd não está iniciando um programa chamado "multi-user". Ele está tentando atingir um estado onde as unidades desejadas por aquele target foram iniciadas ou pelo menos tiveram suas tentativas de início realizadas.

Essa distinção ajuda quando você está depurando problemas de inicialização. Se graphical.target está lento, o target em si raramente é o problema. Uma das unidades de exibição, login, rede, montagem ou aplicativo puxadas para aquele target está lenta ou falhando. Os targets fornecem o mapa.

O que são Targets do Systemd?

No ecossistema systemd, um target é um tipo especial de arquivo de unidade (como arquivos .service ou .socket) que serve a um propósito organizacional crítico. Ao contrário das unidades de serviço que definem como iniciar ou parar um processo específico, as unidades de target definem um estado do sistema ou uma coleção de unidades que devem estar ativas juntas. Elas atuam como pontos de agrupamento lógico e pontos de sincronização para outras unidades do systemd.

Pense nos targets como marcos na jornada operacional do sistema. Quando o systemd inicializa, ele não apenas lança uma lista de serviços arbitrariamente; ele trabalha para atingir um target específico. Este target, por sua vez, puxa todos os serviços, sockets, pontos de montagem e outros targets necessários para que esse estado seja alcançado. Esta abordagem orientada por dependências garante um processo de inicialização previsível e eficiente.

Para aqueles familiarizados com sistemas init Linux mais antigos como SysVinit, os targets do systemd são o equivalente moderno dos runlevels. Enquanto o SysVinit tinha um conjunto fixo de runlevels (por exemplo, runlevel 3 para modo texto multiusuário, runlevel 5 para modo gráfico multiusuário), os targets do systemd são mais flexíveis. Eles são nomeados, não numerados, e você pode definir targets personalizados, oferecendo maior granularidade e extensibilidade.

Como os Targets Funcionam: Agrupamento e Dependências

Os targets alcançam suas capacidades de agrupamento e definição de estado através de dependências explícitas definidas em seus arquivos de unidade. As diretivas primárias usadas para isso são Wants=, Requires=, After= e Before=.

  • Wants=: Especifica dependências "fracas". Se target A Wants= unidade B, o systemd tentará iniciar unidade B quando target A for ativado. No entanto, target A ainda será iniciado mesmo se unidade B falhar ao iniciar. Isso é comumente usado para agrupar serviços relacionados que são desejáveis, mas não estritamente essenciais.
  • Requires=: Especifica dependências "fortes". Se target A Requires= unidade B, então unidade B deve ser iniciada com sucesso para que target A ative. Se unidade B falhar, target A também falhará ou não iniciará. Isso é usado para dependências críticas.
  • After=: Define uma dependência de ordenação. Se target A tem After= unidade B, então target A só iniciará após unidade B ter iniciado. Isso não implica uma dependência de sucesso, apenas de ordem.
  • Before=: O inverso de After=. Se target A tem Before= unidade B, então unidade B só iniciará após target A ter iniciado.
  • Conflicts=: Garante que certas unidades não estejam ativas simultaneamente. Se target A Conflicts= unidade B, então ativar target A irá parar unidade B se estiver em execução, e vice-versa.

Essas diretivas permitem que os targets atuem como orquestradores robustos, puxando serviços e outros targets conforme necessário, e definindo a ordem em que devem iniciar. Por exemplo, multi-user.target tipicamente Wants= network.target e vários outros serviços, garantindo que estejam ativos quando o sistema atingir um estado multiusuário.

Você pode inspecionar o conteúdo de um arquivo de unidade de target para ver suas dependências:

systemctl cat multi-user.target

Este comando exibirá o conteúdo do arquivo de unidade multi-user.target, mostrando sua Description, Documentation e, crucialmente, suas diretivas Wants=, Requires=, After= e outras que definem o que constitui o estado multiusuário.

Targets Comuns do Systemd Explicados

O systemd fornece uma variedade de targets predefinidos, cada um correspondendo a um estado ou funcionalidade específica do sistema. Compreendê-los é crucial para a administração do sistema:

  • default.target: Este é o target mais importante, pois define o estado padrão no qual seu sistema será inicializado. Geralmente é um link simbólico para graphical.target (para desktops) ou multi-user.target (para servidores).
  • graphical.target: Este target é tipicamente usado para sistemas com um ambiente de desktop gráfico. Ele puxa multi-user.target e então adiciona serviços necessários para o gerenciador de login gráfico e servidor de exibição (por exemplo, GDM, LightDM, Xorg, Wayland).
  • multi-user.target: Este é o estado padrão para sistemas multiusuário sem uma interface gráfica. É comum para servidores e fornece todos os serviços necessários para acesso via linha de comando, rede e a maioria das operações de daemon.
  • basic.target: Um estado mínimo que inclui serviços básicos do sistema necessários para operações fundamentais, mas antes de multi-user.target. Ele tipicamente puxa sysinit.target e outros serviços essenciais.
  • sysinit.target: Este target é alcançado muito cedo no processo de inicialização. É responsável por tarefas de inicialização do núcleo do sistema, como montar sistemas de arquivos /etc/fstab (excluindo remotos), configurar swap e outras inicializações relacionadas a hardware.
  • local-fs.target: Garante que todos os sistemas de arquivos locais especificados em /etc/fstab estejam montados.
  • remote-fs.target: Garante que todos os sistemas de arquivos remotos (por exemplo, NFS, CIFS) especificados em /etc/fstab estejam montados.
  • network.target: Indica que a conectividade básica de rede está disponível (por exemplo, interfaces de rede estão ativas). Não garante conectividade total com a internet ou atribuição de endereço IP.
  • network-online.target: Um ponto de sincronização para serviços que desejam esperar até que o gerenciador de rede considere a rede online. Não prova que a internet, DNS ou uma API remota estão acessíveis, e só funciona como esperado quando o serviço wait-online relevante está habilitado.
  • rescue.target: Fornece um shell de usuário único com serviços mínimos em execução e sistemas de arquivos locais montados. Útil para recuperação do sistema e solução de problemas.
  • emergency.target: Um ambiente ainda mais mínimo que rescue.target. Fornece um shell no sistema de arquivos raiz, que é tipicamente montado como somente leitura. Nenhum outro serviço é iniciado. Para situações críticas de emergência.
  • poweroff.target, reboot.target, halt.target: Estes targets são usados para desligar, reiniciar ou parar o sistema, respectivamente. Quando ativados, eles param a maioria dos serviços e preparam o sistema para o estado de energia desejado.

Gerenciando Targets do Systemd

Interagir com targets do systemd envolve principalmente o utilitário de linha de comando systemctl.

Visualizando Targets Ativos e Padrão

Para ver em qual target seu sistema está atualmente em execução:

systemctl get-default

Para listar todas as unidades de target atualmente carregadas:

systemctl list-units --type=target

Este comando mostra targets ativos, carregados e estáticos, juntamente com suas descrições.

Alterando o Target de Inicialização Padrão

Você pode alterar o target no qual seu sistema inicializa por padrão. Por exemplo, para definir multi-user.target como padrão:

sudo systemctl set-default multi-user.target

Para reverter para graphical.target:

sudo systemctl set-default graphical.target

Este comando cria um link simbólico de /etc/systemd/system/default.target para o arquivo de target desejado.

Inicializando em um Target Diferente Temporariamente

Às vezes você precisa inicializar em um target específico apenas uma vez (por exemplo, para solução de problemas). Você pode conseguir isso anexando um parâmetro do kernel durante a inicialização. Quando o menu de inicialização GRUB aparecer, edite a entrada de inicialização (geralmente pressionando e) e adicione systemd.unit=nome_do_target.target à linha de comando do kernel.

Por exemplo, para inicializar no modo de resgate:

systemd.unit=rescue.target

Alternando Targets Durante a Execução

Você pode alternar para um target diferente enquanto o sistema está em execução usando o comando systemctl isolate. Este comando irá parar todos os serviços não exigidos pelo novo target e iniciar todos os serviços exigidos por ele.

Aviso: Usar systemctl isolate pode interromper a operação do seu sistema, especialmente se você alternar para um target de nível muito inferior, como multi-user.target a partir de graphical.target em uma máquina desktop. Use com cautela.

Para alternar de graphical.target para multi-user.target:

sudo systemctl isolate multi-user.target

Para voltar para graphical.target (assumindo que era o estado anterior):

sudo systemctl isolate graphical.target

Criando Targets Personalizados

Embora o systemd forneça muitos targets úteis, você pode encontrar situações onde criar um target personalizado é benéfico. Isso é particularmente verdadeiro para implantações de aplicativos complexos onde você precisa agrupar vários serviços que devem sempre iniciar e parar juntos, ou para definir um ambiente específico para seu aplicativo.

Para criar um target personalizado:

  1. Crie um arquivo .target: Coloque-o em /etc/systemd/system/. Por exemplo, my-application.target.
    # /etc/systemd/system/my-application.target
    [Unit]
    Description=Meu Target de Aplicativo Personalizado
    Wants=my-database.service my-webserver.service
    After=my-database.service my-webserver.service
    
    • Description: Uma descrição legível por humanos.
    • Wants=: Liste os serviços ou outros targets que este target deve puxar.
    • After=: Defina a ordem. O target iniciará após estas unidades.
  2. Crie os serviços: Certifique-se de que my-database.service e my-webserver.service (ou quaisquer serviços que você listar) existam e estejam configurados corretamente.
  3. Recarregue o systemd: Informe o systemd sobre o novo arquivo de unidade.
    
    

sudo systemctl daemon-reload 4. **Habilite e Inicie**: Agora você pode habilitar e iniciar seu target personalizado, que por sua vez iniciará seus serviços desejados. bash sudo systemctl enable my-application.target sudo systemctl start my-application.target ```

Isso permite que você gerencie um grupo de serviços relacionados como uma única unidade lógica, simplificando implantações de aplicativos complexos.

Runlevels e Targets Sem Rodeios

Se você veio do SysVinit, o mapeamento aproximado é:

Ideia antiga de runlevel Target comum do systemd
Modo de reparo de usuário único rescue.target
Modo texto multiusuário multi-user.target
Modo gráfico multiusuário graphical.target
Reinicializar reboot.target
Desligar poweroff.target

Trate isso como um auxílio de tradução, não um modelo perfeito. Os runlevels SysV eram um pequeno conjunto fixo de estados numerados. Os targets do systemd são unidades nomeadas com dependências, e pode haver muitos deles. Um pacote pode instalar seu próprio target. Você pode criar um para um fluxo de trabalho de implantação. Alguns targets são feitos para serem isolados; outros são apenas pontos de agrupamento usados durante a inicialização.

Você pode ver quais targets permitem isolamento com:

systemctl show multi-user.target -p AllowIsolate
systemctl show basic.target -p AllowIsolate

Isso é importante porque systemctl isolate não é um comando inofensivo de "alternar visualização". Ele para unidades que não fazem parte da transação do novo target. Em um desktop, isolar multi-user.target geralmente irá parar a sessão gráfica. Em um servidor remoto, isolar o target errado pode parar os serviços de rede ou login e bloquear seu acesso.

Como os Serviços se Tornam Parte de um Target

A maior parte da associação de target no dia a dia vem da seção [Install] de um arquivo de serviço:

[Install]
WantedBy=multi-user.target

Quando você executa:

sudo systemctl enable myapp.service

systemd cria um link simbólico sob um diretório como:

/etc/systemd/system/multi-user.target.wants/myapp.service

Esse link simbólico é o que faz multi-user.target querer o serviço durante a inicialização. O arquivo de serviço pode existir e ser perfeitamente válido sem estar habilitado. Nesse caso, iniciar multi-user.target não o puxará automaticamente.

É por isso que systemctl start myapp.service e systemctl enable myapp.service resolvem problemas diferentes. start executa agora. enable o conecta a um target de inicialização futuro. enable --now faz ambos.

Para verificar se um serviço está habilitado para um target:

systemctl is-enabled myapp.service
systemctl list-dependencies multi-user.target | grep myapp

Se um serviço inicia manualmente, mas não na inicialização, esta é uma das primeiras coisas a verificar.

Um Pequeno Target Personalizado Que É Realmente Útil

Targets personalizados são mais úteis quando fornecem aos operadores um comando para um grupo de unidades relacionadas. Imagine uma pilha de aplicativos simples:

app-api.service
app-worker.service
app-scheduler.service

Você pode criar:

# /etc/systemd/system/app-stack.target
[Unit]
Description=Pilha de aplicativos
Wants=app-api.service app-worker.service app-scheduler.service
After=network-online.target
Wants=network-online.target
AllowIsolate=no

Em seguida, adicione cada serviço ao target:

[Install]
WantedBy=app-stack.target

Após daemon-reload, habilite os serviços ou o target dependendo do comportamento desejado:

sudo systemctl daemon-reload
sudo systemctl enable app-api.service app-worker.service app-scheduler.service
sudo systemctl start app-stack.target

Isso fornece um agrupamento legível sem fingir que o target é um supervisor de processo. Se app-worker.service falhar, inspecione esse serviço. O target é apenas o ponto de agrupamento.

Se você quiser que parar o target pare todos os serviços da pilha, adicione PartOf=app-stack.target a cada serviço:

[Unit]
PartOf=app-stack.target

Agora systemctl stop app-stack.target se propaga para os serviços membros. Essa é frequentemente a peça que falta em exemplos de targets personalizados.

Solução de Problemas com Targets

Targets também são inestimáveis para solucionar problemas de inicialização ou falhas de serviço:

  • Identificando dependências: Se um serviço falhar ao iniciar, inspecionar o target ao qual ele pertence pode revelar dependências ausentes ou com falha. Use systemctl status <nome_do_serviço> e systemctl list-dependencies <nome_do_target>.
  • Inicialize em targets mínimos: Se seu sistema falhar ao inicializar em graphical.target ou multi-user.target, tente inicializar em rescue.target ou emergency.target usando o método de parâmetro do kernel. Isso fornece um ambiente mínimo onde você pode diagnosticar problemas sem a complexidade de muitos serviços em execução.
  • Verifique os logs: Após tentar iniciar um target ou um serviço, sempre verifique os logs do journalctl para erros:
    journalctl -b -u <nome_do_target_ou_serviço>
    

Melhores Práticas e Dicas

  • Use network-online.target com cuidado: Se seu serviço precisar de configuração de rede antes da inicialização, combine After=network-online.target com Wants=network-online.target e confirme se a unidade wait-online apropriada está habilitada. Ainda mantenha a lógica de repetição no aplicativo para dependências remotas.
  • Entenda a ordem de inicialização: Familiarize-se com o fluxo geral de sysinit.target para basic.target, depois multi-user.target/graphical.target. Isso ajuda na depuração de serviços que falham no início do processo de inicialização.
  • Seja cauteloso com default.target: Alterar default.target pode alterar significativamente o comportamento de inicialização do seu sistema. Sempre teste configurações personalizadas em um ambiente que não seja de produção primeiro.
  • Use Wants= para dependências não críticas: Para serviços que são úteis, mas não estritamente necessários para que um target seja considerado "ativo", use Wants= em vez de Requires=. Isso evita que uma única falha de serviço opcional se propague e impeça a ativação de todo o target.

O Modelo Mental a Manter

Um target é um estado nomeado, não um daemon. default.target decide o destino normal de inicialização. multi-user.target é o estado usual do servidor. graphical.target adiciona a pilha de exibição. rescue.target e emergency.target são ferramentas de reparo. Targets personalizados são ferramentas de agrupamento quando tornam as operações mais claras.

Quando algo relacionado a target quebrar, evite culpar o target primeiro. Pergunte qual unidade foi puxada, qual regra de ordenação a atrasou e qual dependência falhou. systemctl cat, systemctl list-dependencies, systemctl show e journalctl -b geralmente responderão a essas perguntas mais rápido do que ler diagramas genéricos de inicialização.