Ordenação no systemd

É comum precisarmos ordenar unidades, sejam do tipo service, target, etc. O systemd oferece dois tipos de configuração para o propósito: ordenação (After=, Before=) e requerimento (Wants=, Requires=). As duas classes de dependências são independentes. Nota: existem outras opções menos usadas, porém não comentarei sobre elas aqui.

After= e Before= dizem respeito à ordem, como esperado. São dependências de mão dupla: se você especificar After=yyy.service no serviço xxx.service, está implícita naquele uma dependência reversa Before=xxx.service.

     xxx.service                           yyy.service
+--------------------+               +--------------------+
|                    |   Implícito   |                    |
| After=yyy.service  | ------------> | Before=xxx.service |
|                    |               |                    |
+--------------------+               +--------------------+

Não tão claro é o papel de Wants= e Requires=.

Suponhamos bbb.service com estas opções:

[Unit]
Description=Serviço BBB
After=aaa.service

[Service]
...

After=aaa.service diz que bbb.service será iniciado depois que aaa.service tenha completado sua inicialização. Não é o suficiente? Talvez não, pois existe um detalhe: e se aaa.service estiver parado (esteja habilitado ou não)? bbb.service iniciará sem aaa.service! Lembremos que desabilitado/habilitado e iniciado/parado são coisas totalmente independentes.

Entram as dependências de requerimento. Novo bbb.service:

[Unit]
Description=Serviço BBB
Wants=aaa.service
After=aaa.service

[Service]
...

Agora, com Wants= e After=, você diz ao systemd para iniciar bbb.service depois de aaa.service e, se aaa.service estiver parado, que inicie-o primeiro para só depois bbb.service iniciar. Aí está a diferença.

Wants= e Requires= são configurações parecidas. Wants= permite falha na dependência, enquanto Requires= não. Trocando Wants=aaa.service por Requires=aaa.service no serviço acima, caso aaa.service falhe por algum motivo, bbb.service não iniciará. Em outras palavras: Requires= é uma exigência e Wants= é um desejo.

As dependências reversas implícitas existem igualmente para Wants= e Requires= (WantedBy= e RequiredBy=). É pertinente esclarecer que WantedBy= e RequiredBy=, ao contrário da relação existente entre After= e Before=, apenas podem ser adicionadas através de links como explicado no parágrafo abaixo. Não é possível colocá-las na seção [Unit].

       bbb.service                          aaa.service
+----------------------+             +------------------------+
|                      |  Implícito  |                        |
|  Wants=aaa.service   | ----------> |  WantedBy=bbb.service  |
|                      |             |                        |
+----------------------+             +------------------------+


       bbb.service                          aaa.service
+----------------------+             +------------------------+
|                      |  Implícito  |                        |
| Requires=aaa.service | ----------> | RequiredBy=bbb.service |
|                      |             |                        |
+----------------------+             +------------------------+

WantedBy=<nome>.<tipo> e RequiredBy=<nome>.<tipo> são também opções da seção [Install], que criam, quando systemctl enable ou systemctl preset habilitam uma unidade, link simbólico apontando para a mesma nos diretórios /etc/systemd/system/<nome>.<tipo>.wants ou /etc/systemd/system/<nome>.<tipo>.requires (serão criados caso necessário).

Têm efeito igual a Wants= e Requires= diretamente nos arquivos. As distribuições podem fornecer unidades pré-habilitadas em seus pacotes, criando links em /usr/lib/systemd/system/<nome>.<tipo>.wants e /usr/lib/systemd/system/<nome>.<tipo>.requires. Geralmente esse tipo de configuração é usada para lidar com unidades do tipo target e fica automatizada com systemctl enable|disable|preset. No papel de administrador, você lidará quase sempre com Wants= e Requires=.

Outra opção útil é PartOf=. De novo, é independente das demais e serve para criar uma dependência exclusiva de reinicialização e parada. Voltemos ao nosso serviço hipotético bbb.service:

[Unit]
Description=Serviço BBB
Wants=aaa.service
After=aaa.service
PartOf=aaa.service

[Service]
...

PartOf=aaa.service faz com que, ao aaa.service ser parado ou reiniciado, bbb.service também o seja. PartOf= é uma dependência de mão única. Se bbb.service reiniciar ou parar, aaa.service não será afetado. Caso queira o mesmo comportamento nos dois sentidos, faz-se necessário editar aaa.service e adicionar lá PartOf=bbb.service.

Relacionado:
Modificar parâmetros dos arquivos de unidade do systemd
Básico dos arquivos de unidade do systemd
Olho de Cylon do systemd
systemctl cat/edit
systemctl enable|disable|mask --now
systemctl revert

Comentários