Noções básicas sobre systemd opção “Requires =”

3

De systemd.unit(5) , a opção Requires= significa:

Configures requirement dependencies on other units. If this unit gets activated, the units listed here will be activated as well. If one of the other units gets deactivated or its activation fails, this unit will be deactivated.

Eu fiz um pequeno experimento sobre isso. Eu criei dois serviços: a.service e b.service :

# cat a.service
[Service]
ExecStart=/bin/false

# cat b.service
[Unit]
Requires=a.service

[Service]
ExecStart=/bin/sleep 1000

Depois de fazer

systemctl start b.service

Eu esperava ver ambos a.service e b.service falharem porque a.service falha com /bin/false e b.service falha com a.service falha.

No entanto, b.service está em execução:

root@john-ubuntu:/etc/systemd/system# systemctl status a.service b.service
● a.service
   Loaded: loaded (/etc/systemd/system/a.service; static; vendor preset: enabled)
   Active: failed (Result: exit-code) since Thu 2017-09-07 16:38:39 CST; 2s ago
  Process: 1245 ExecStart=/bin/false (code=exited, status=1/FAILURE)
 Main PID: 1245 (code=exited, status=1/FAILURE)

Sep 07 16:38:39 john-ubuntu systemd[1]: Started a.service.
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Main process exited, code=exited, status=1/FAILURE
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Unit entered failed state.
Sep 07 16:38:39 john-ubuntu systemd[1]: a.service: Failed with result 'exit-code'.

● b.service
   Loaded: loaded (/etc/systemd/system/b.service; static; vendor preset: enabled)
   Active: active (running) since Thu 2017-09-07 16:38:39 CST; 2s ago
 Main PID: 1244 (sleep)
    Tasks: 1
   Memory: 88.0K
      CPU: 696us
   CGroup: /system.slice/b.service
           └─1244 /bin/sleep 1000

Sep 07 16:38:39 john-ubuntu systemd[1]: Started b.service.

Eu senti falta de algo? Obrigado.

    
por 林自均 07.09.2017 / 10:48

2 respostas

1

Isso é complicado. A principal coisa a entender é que o systemd inicia tudo em paralelo, a menos que seja dito que não. Isso é realmente indicado na página man na seção "Requer":

requirement dependencies do not influence the order in which services are started or stopped

O que você quer é "esperar que o serviço seja iniciado antes de iniciar o serviço". Para isso, você precisa das opções "Requer" e "Depois" no seu arquivo b.service:

[Unit]
Requires=a.service
After=a.service

[Service]
ExecStart=/bin/sleep 1000

= UPDATE =

OK, vejo o que está errado: no seu arquivo a.service, você coloca o comando ExecStart e não especificou o tipo. Isso significa que o Type será padronizado como 'Simple'. Você precisa do tipo 'Bifurcação' para que isso funcione. Na página do manual systemd.service:

If set to simple (the default if neither Type= nor BusName=, but ExecStart= are specified), it is expected that the process configured with ExecStart= is the main process of the service. In this mode, if the process offers functionality to other processes on the system, its communication channels should be installed before the daemon is started up (e.g. sockets set up by systemd, via socket activation), as systemd will immediately proceed starting follow-up units.

If set to forking, it is expected that the process configured with ExecStart= will call fork() as part of its start-up. The parent process is expected to exit when start-up is complete and all communication channels are set up. The child continues to run as the main daemon process. This is the behavior of traditional UNIX daemons. If this setting is used, it is recommended to also use the PIDFile= option, so that systemd can identify the main process of the daemon. systemd will proceed with starting follow-up units as soon as the parent process exits.

Portanto, você deve atualizar seu arquivo a.service para incluir 'Type = Forking':

[Service]
Type=forking
ExecStart=/bin/false

Isso funcionará. :)

    
por 07.09.2017 / 23:45
1

Se você estiver usando o CentOS 7 com systemd-219 (que tem uma página systemd.unit(5) man correspondente à seção citada na pergunta), isso parece ser parcialmente devido a um erro de documentação. Talvez o mesmo se aplique a algumas outras distribuições e versões do systemd.

Esta frase citada no comentário:

If one of the other units gets deactivated or its activation fails, this unit will be deactivated.

sugeriria que systemctl start b.service faria com que ambos os serviços fossem ativados, mas depois que a.service falhasse no retorno de /bin/false , b.service seria automaticamente desativado. Assim como a questão afirma que esse não foi o comportamento observado, não observei esse comportamento no CentOS 7.

A frase citada foi substituída por essas frases no link :

If one of the other units fails to activate, and an ordering dependency After= on the failing unit is set, this unit will not be started. Besides, with or without specifying After=, this unit will be stopped if one of the other units is explicitly stopped.

A documentação atualizada concorda com a afirmação do @gerard de que você precisa de uma configuração After= em b.service e corresponde ao comportamento que observei no CentOS 7.

Então, como diz @gerard, quando o systemd inicia um serviço Type=simple , ele "continuará imediatamente iniciando as unidades de acompanhamento". Observe que Type=forking não é a única configuração que pode ser usada para resolver isso, você também pode definir Type=notify ou um dos outros tipos descritos na página systemd.service(5) man (diferente de Type=idle ). Quando você ultrapassar o estágio de testar o tratamento de falhas, verifique se o serviço realmente se comporta conforme necessário, considerando seu Type , por exemplo, chama fork() , sd_notify() , etc.

Esteja ciente também de que há muitos casos de ponta no manuseio de falhas no systemd-219, por exemplo, link

    
por 25.07.2018 / 08:43

Tags