Faz com que um script seja executado após o início da rede?

87

Sou relativamente novo no systemd e estou aprendendo sua arquitetura.

Neste momento, estou tentando descobrir como fazer com que um script de shell personalizado seja executado. Este script precisa ser executado após a camada de rede ser iniciada.

Estou executando o Arch, usando o systemd e o netctl.

Para testar, escrevi um script simples que simplesmente executa ip addr list > /tmp/ip.txt . Eu criei o seguinte arquivo de serviço para este script.

(/etc/systemd/system/test.service)
[Unit]
Description=test service

[Service]
ExecStart=/root/test.script

[Install]
WantedBy=multi-user.target

Eu, então, habilitei o script com

systemctl enable test

Ao reiniciar, o script é executado, mas é executado antes da rede ser iniciada. Em outras palavras, a saída em ip.txt não exibe nenhum endereço IPv4 atribuído à interface principal. No momento em que faço o login, o endereço IPv4 foi realmente atribuído e a rede está ativa.

Suponho que eu possa alterar o ponto em que o script é executado, mexendo no parâmetro WantedBy , mas não sei como fazer isso.

Alguém poderia me apontar na direção certa?

    
por fdmillion 22.04.2014 / 20:03

7 respostas

104

Em dependências de configuração de rede do systemd

É muito fácil afetar a ordem das unidades do systemd. Por outro lado, você precisa ter cuidado com o que uma unidade completa garante.

Configure seu serviço

Nos sistemas atuais, ordenar após network.target apenas garante que o serviço de rede foi iniciado, não que exista alguma configuração real. Você precisa pedir após network-online.target e puxá-lo para conseguir isso.

[Unit]
Wants=network-online.target
After=network-online.target

Para compatibilidade com sistemas mais antigos, talvez seja necessário fazer o pedido após network.target também.

[Unit]
Wants=network-online.target
After=network.target network-online.target

Isso é para o arquivo de unidade do seu serviço e para o systemd.

Implementação nas versões atuais do software

Agora, você precisa garantir que network-online.target funcione como esperado (ou que, pelo menos, você possa usar network.target ).

A versão atual do NetworkManager oferece o NetworkManager-wait-online.service que é puxado por network-online.target e, portanto, pelo seu serviço. Esse serviço especial garante que seu serviço esperará até que todas as conexões configuradas para serem iniciadas sejam bem-sucedidas, falhem ou expirem automaticamente.

A versão atual do systemd-networkd bloqueia seu serviço até que todos os dispositivos sejam configurados conforme solicitado. É mais fácil porque atualmente suporta apenas configurações que são aplicadas no momento da inicialização (mais especificamente o tempo de inicialização de 'systemd-networkd.service).

Por questões de integridade, o serviço /etc/init.d/network no Fedora, conforme interpretado pelas versões atuais do systemd, bloqueia network.target e, portanto, indiretamente bloqueia network-online.target e seu serviço. É um exemplo de implementação baseada em script.

Se a sua implementação, com base no daemon ou script, se comportar como um dos serviços de gerenciamento de rede acima, isso atrasará o início do serviço até que a configuração da rede seja concluída com êxito, falhe por um bom motivo ou expirada após um prazo razoável para concluir.

Você pode querer verificar se o netctl funciona da mesma maneira e que a informação seria um acréscimo valioso para essa resposta.

Implementações em versões mais antigas do software

Eu não acho que você verá uma versão suficientemente antiga do systemd onde isso não funcionaria bem. Mas você pode verificar se pelo menos network-online.target existe e se é ordenado depois de network.target .

Anteriormente, o NetworkManager garantiu apenas que pelo menos uma conexão fosse aplicada. E mesmo para que isso funcione, você teria que ativar o NetworkManager-wait-online.service explicitamente. Isso foi corrigido por muito tempo no Fedora, mas só recentemente foi aplicado a upstream.

systemctl enable NetworkManager-wait-online.service

Notas sobre as implementações network.target e network-online.target

Você nunca deve precisar fazer com que seu software dependa de NetworkManager.service ou NetworkManager-wait-online.service nem de outros serviços específicos. Em vez disso, todos os serviços de gerenciamento de rede devem se autogerir antes de network.target e, opcionalmente, network-online.target .

Um serviço de gerenciamento de rede simples baseado em script deve concluir a configuração de rede antes de sair e deve se autogerir antes de network.target e, portanto, indiretamente antes de network-online.target .

[Unit]
Before=network.target

[Service]
Type=oneshot
ExecStart=...
RemainAfterExit=yes

Um serviço de gerenciamento de rede baseado no daemon também deve se ordenar antes de network.target , mesmo que não seja muito útil.

[Unit]
Before=network.target

[Service]
Type=simple
ExecStart=...

Um serviço que espera que o daemon termine deve fazer o pedido após o serviço específico e antes de network-online.target . Ele deve usar Requisite no serviço daemon para que ele falhe imediatamente se o respectivo serviço de gerenciamento de rede não estiver sendo usado.

[Unit]
Requisite=...
After=...
Before=network-online.target

[Service]
Type=oneshot
ExecStart=...
RemainAfterExit=yes

O pacote deve instalar um link simbólico para o serviço em espera no diretório wants para network-online.target , para que seja puxado pelos serviços que desejam esperar pela rede configurada.

ln -s /usr/lib/systemd/system/... /usr/lib/systemd/system/network-online.target.wants/

Documentação relacionada

Notas finais

Espero não só ter ajudado a responder sua pergunta no momento em que você a solicitou, mas também contribuído para melhorar a situação nas distribuições upstream e Linux, para que agora eu possa dar uma resposta melhor do que era possível no momento em que escrevo o original.

    
por 23.04.2014 / 17:40
9

Você pode usar a seção After in [Unit] para definir um serviço que deve ser iniciado antes de seu serviço ser iniciado. Por exemplo, se você estiver usando o NetworkManager, poderá fazer com que seu serviço inicie depois que o NetworkManager for iniciado.

[Unit]
Description=test service
After=NetworkManager.service
    
por 22.04.2014 / 20:15
7

Se o seu serviço fornecer um servidor, que pode esperar passivamente que alguém se conecte a ele, use este:

[Unit]
After=network.target

Seu serviço deve ser vinculado na interface curinga. Se usar a ativação de soquete (recomendado) ou se for local somente, você poderá ignorar totalmente os destinos de rede.

Se o seu serviço atua como um cliente ou é ponto a ponto, isso é mais apropriado:

[Unit]
After=network-online.target
Requires=network-online.target

Antes de systemd 213 , o network-online.target precisa da solução alternativa Pavel mencionou (você precisa ativar manualmente um serviço que esperará que a rede esteja ativa). A partir do systemd 213 isso é feito por padrão. systemd-networkd-wait-online esperará que pelo menos um endereço (roteavel ou link-local) seja configurado em uma interface que não seja de loopback.

Configurar systemd-networkd, NetworkManager ou equivalente é uma tarefa independente. DHCP (para IPv4) e NDP (para IPv6) tendem a funcionar fora da caixa, mas você deve configurá-los para que sua definição precisa de "a rede está ativa" seja o que aciona network-online.target .

Documentação:

por 16.12.2014 / 14:07
4

I'm guessing I could alter the point at which the script runs by messing with the WantedBy parameter

Isso terá o efeito oposto do que você deseja. De man systemd.unit :

WantedBy=, RequiredBy=

[...] A symbolic link is created in the .wants/ or .requires/ directory of each of the listed units when this unit is installed by systemctl enable. This has the effect that a dependency of type Wants= or Requires= is added from the listed unit to the current unit.

Com base nisso, podemos ver que a opção apropriada de unidade é "Quer" ou "Requer"; com base na descrição deles, "Requer" provavelmente está correto, com a adição de "Depois" para garantir não apenas que o serviço de rede seja executado, mas que seja executado antes desta unidade.

Nenhuma das opções de unidade, AFAIK, pode incluir a estipulação de que uma pré-qualificação iniciada deve ter sido concluída ou atingido um determinado ponto (a rede é provavelmente um serviço daemon), apenas que inicie primeiro. Com isso em mente, convém tornar seu script Type=forking e lançar um atraso saudável (digamos, 30 segundos) ou algum tipo de loop de saída ao sucesso, incluindo um atraso, para garantir que você tenha um DHCP arrendar primeiro.

    
por 22.04.2014 / 20:21
3

Use After na seção [Unit] para especificar o que deve ser iniciado antes de seu próprio serviço. (Esta parte da resposta anterior está correta.)

Para iniciar seu serviço depois que a rede estiver ativa, use o destino da rede, que deve ser aplicado se você usar o NetworkManager, o sistema conf.d / netctl no Arch ou algum outro serviço que o systemd conheça.

[Unit]
#.....
After=network.target

Uma breve olhada confirmará que todo serviço outro em seu sistema que depende de conectividade de rede contém esta diretiva.

Também é portátil para qualquer distribuição que use o systemd. Seu arquivo unitário será o mesmo para Arch, Fedora, RHEL 7, versões futuras do Debian ...

Serviços que iniciam uma conexão de rede, como os scripts do Arch ou os seus próprios, devem ser especificados em seus próprios arquivos .

[Unit]
Wants=network.target
Before=network.target
    
por 23.04.2014 / 05:16
1

Eu queria adicionar um ponto a este artigo. Atualmente (verão de 2015) no RHEL7 / CentOS 7, network-online.target é definido incorretamente antes da rede IPv6 ser ativada, portanto, daemons que possuem

Wants=network-online.target
After=network-online.target

em sua definição de serviço que também se liga explicitamente a endereços IPv6 provavelmente será iniciado antes que o IPv6 esteja ativo e em execução, fazendo com que eles falhem.

    
por 16.07.2015 / 20:27
0
[Unit]
After=systemd-networkd.service

funciona para mim.

    
por 15.02.2016 / 12:55