Causar a execução de um script após o início da rede?

102

Eu sou relativamente novo no systemd e estou aprendendo sua arquitetura.

No momento, estou tentando descobrir como fazer com que um script de shell personalizado seja executado. Esse script precisa ser executado após a inicialização da camada de rede.

Estou executando o Arch, usando systemd e netctl.

Para testar, escrevi um script simples que simplesmente é executado 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 habilitei o script com,

systemctl enable test

Na reinicialização, o script realmente é executado, mas é executado antes da inicialização da rede. Em outras palavras, a saída em ip.txtnão exibe nenhum endereço IPv4 atribuído à interface principal. Quando eu entro, o endereço IPv4 já foi atribuído e a rede está ativa.

Suponho que poderia alterar o ponto em que o script é executado mexendo com o WantedByparâmetro, mas não tenho certeza de como fazer isso.

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

fdmillion
fonte

Respostas:

126

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

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

Configure seu serviço

Nos sistemas atuais, o pedido network.targetapenas garante que o serviço de rede foi iniciado, não que exista alguma configuração real. Você precisa pedir depois network-online.targete puxá-lo para conseguir isso.

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

Para compatibilidade com sistemas mais antigos, pode ser necessário solicitar também o network.target.

[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.targetfuncione conforme o esperado (ou que você pelo menos possa usar network.target).

A versão atual do NetworkManager oferece o NetworkManager-wait-online.serviceque é atraído network-online.targete, portanto, pelo seu serviço. Esse serviço especial garante que o serviço espere até que todas as conexões configuradas para serem iniciadas tenham êxito, falhem ou atingem o tempo limite automaticamente.

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

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

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

Você pode verificar se o netctl funciona da mesma maneira e se essas informações seriam uma adição valiosa a esta 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.targetexiste e se é solicitado depois network.target.

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

systemctl enable NetworkManager-wait-online.service

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

Você nunca precisa fazer com que seu software dependa de NetworkManager.serviceou de NetworkManager-wait-online.servicenenhum outro serviço específico. Em vez disso, todos os serviços de gerenciamento de rede devem solicitar antes network.targete opcionalmente network-online.target.

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

[Unit]
Before=network.target

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

Um serviço de gerenciamento de rede baseado em daemon também deve ser solicitado antes, network.targetmesmo que não seja muito útil.

[Unit]
Before=network.target

[Service]
Type=simple
ExecStart=...

Um serviço que aguarda a conclusão do daemon deve solicitar a si próprio após o serviço específico e antes network-online.target. Ele deve ser usado Requisiteno serviço daemon para que 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 no serviço em espera no wantsdiretório para network-online.targetque seja puxado pelos serviços que desejam aguardar 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 apenas ter ajudado a responder sua pergunta no momento em que você a fez, mas também ter 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 escrevi a original. .

Pavel Šimerda
fonte
Você quer dizer a opção de conexão automática com "aguarde até que todas as conexões configuradas para serem iniciadas tenham êxito automaticamente"? Posso aproveitar isso quando defino no-auto-default = *, mas tenho conexão automática = yes em uma das minhas conexões? E a última pergunta - não entendo - a opção aguardar inicialização da página nm-online e manual não ajuda muito. Obrigado por este artigo, muito apreciado!
Lzap 11/11/2015
Até onde eu sei, o nm-online não se importa no-auto-defaultapenas auto. Você tem alguma pergunta específica? Na minha opinião, a página de manual nm-online afirma claramente que, com -sisso, espera que todas as conexões automáticas sejam tentadas, ou seja, conectadas ou com falha.
Pavel Šimerda 11/11/2015
Depois de mexer nessa porcaria por uma hora, encontrei a solução: apt-get install sysv-init. :-) A complexidade que o systemd adiciona como um substituto para alguns scripts de shell é incompreensível.
Alguém
@ Alguém temo que initscripts não sejam uma resposta neste caso. Se você estiver usando o NetworkManager ou qualquer outra ferramenta de configuração dinâmica, os initscripts não poderão se ordenar após uma rede totalmente configurada. Você pode obter uma configuração dinâmica limitada usando /etc/init.d/networkou similar, mas isso não funciona universalmente.
Pavel Šimerda 23/03
O @Pavel Šimerda Init é executado em série, e um script init adequado não retornará até que termine de fazer o que os scripts subsequentes precisam confiar. Para redes, isso significaria ter todos os adaptadores aplicáveis ​​instalados e prontos. A menos que tenha sido apenas um momento de sorte, o NM se comporta bem nesse contexto. O verdadeiro problema, é claro, é o NM reinventar o manuseio da rede, em vez de desenvolver estruturas simples, testadas e testadas. As pessoas da área de trabalho parecem não ter noção dos perigos da complexidade. ;-)
Alguém
9

Você pode usar Afterna [Unit]seção para definir um serviço que deve ser iniciado antes do início do serviço. Por exemplo, se você estiver usando o NetworkManager, poderá iniciar o serviço após o NetworkManager ser iniciado.

[Unit]
Description=test service
After=NetworkManager.service
phoops
fonte
BindsTonão é tão apropriado aqui, pois o serviço é um evento único e não um serviço persistente (a menos que também inclua um ExecStoprecurso para acionar quando a rede cair).
GOLDILOCKS
removidaBindsTo
phoops
Você pode adicionar algo para substituir BindsTo, por exemplo Requires, se você quiser que o serviço seja executado apenas se o NetworkManager o executar . Afterna verdade não faz isso - apenas significa que , se o NM também estiver em execução, execute-o posteriormente. Se o NM não for executado, o serviço será executado em um ponto arbitrário.
GOLDILOCKS
4
After = network.target é melhor que After = NetworkManager.service, pois é mais genérico.
Pavel Šimerda
7
Observe que especificar After=fooirá não causar a foounidade para iniciar, se não tiver sido iniciado, ele será única dizer systemd como requisitar as unidades se eles são ambos iniciados ao mesmo tempo . Usar ambos After=foo, bem como Wants=fooou Requires=footerá o efeito de puxar foose não for iniciado e também fazer com que o sistema ordene as unidades corretamente.
Emil Lundberg
8

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

[Unit]
After=network.target

Seu serviço deve ligar na interface curinga. Se ele usa a ativação do soquete (recomendado) ou se é apenas local, você pode ignorar completamente os destinos de rede.

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

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

Antes do systemd 213 , o network-online.target precisa da solução alternativa mencionada pelo Pavel (você precisa ativar manualmente um serviço que aguardará a inicialização da rede). A partir do systemd 213, isso é feito por padrão. systemd-networkd-wait-onlineaguardará a configuração de pelo menos um endereço (roteável ou local de link) em uma interface sem loopback.

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

Documentação:

Tobu
fonte
Apenas curioso por que uma nova resposta e não apenas pequenas melhorias na resposta existente e (espero) bem estruturada.
Pavel Šimerda
Os dois primeiros links da documentação estão atualmente desativados.
27578 Peter
Por que usar Requer em vez de Deseja?
9788 Karl Morrison
4

Acho que poderia alterar o ponto em que o script é executado, mexendo no parâmetro WantedBy

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

WantedBy =, RequiredBy =

[...] Um link simbólico é criado no diretório .wants / ou .requires / de cada uma das unidades listadas quando esta unidade é instalada pelo systemctl enable. Isso tem o efeito de que uma dependência do tipo Quer = ou Requer = é adicionada da unidade listada à unidade atual .

Com base nisso, podemos ver que a opção de unidade apropriada é "Quer" ou "Requer"; com base na descrição desses, "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 da unidade, AFAIK, pode incluir a estipulação de que um requisito iniciado deve ter sido concluído ou atingido um determinado ponto (a rede provavelmente é um serviço daemon), apenas o início inicial. Com isso em mente, convém criar seu script Type=forkinge gerar um atraso saudável (digamos 30 segundos), ou algum tipo de loop de saída com êxito, incluindo um atraso, para garantir que você tenha uma concessão de DHCP primeiro.

Cachinhos Dourados
fonte
1
Nem WantedBy nem RequiredBy afetam a ordem.
Pavel Šimerda
1
@ PavelŠimerda: Ninguém aqui alegou que sim. É por esse motivo que mencionei explicitamente Afterem conjunto com Requires"para garantir não apenas que o serviço de rede seja executado, mas que seja executado antes desta unidade".
Goldilocks
1
Sim, Aftertrabalha em conjunto Wantsou Requiresdessa maneira. Por outro lado, atrasos explícitos são um mau hábito em ferramentas baseadas em dependências, especialmente quando há uma maneira explícita de esperar até que a rede seja configurada especificada pela documentação do systemd, por isso tenho que insistir no voto negativo.
Pavel Šimerda
3

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

Para iniciar o serviço após o funcionamento da rede, use o destino da rede, que deve ser aplicado se você usa o NetworkManager, o sistema conf.d / netctl no Arch ou algum outro serviço que o systemd esteja ciente.

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

Uma breve olhada confirmará que todos os outros serviços do seu sistema que dependem da conectividade de rede contêm esta diretiva.

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


Os serviços que iniciam uma conexão de rede, como os scripts do Arch ou os seus, devem especificar isso em seus próprios arquivos de unidade.

[Unit]
Wants=network.target
Before=network.target
Michael Hampton
fonte
Não gosto totalmente da Wantsparte porque tem efeitos colaterais em outros pacotes. Olhe para a minha resposta, por favor.
Pavel Šimerda
Só percebi que Wantsem network.targeté uma boa idéia aqui.
Pavel Šimerda
você realmente deseja usar o network-online.target. ref
Edward Torvalds
1

Eu queria acrescentar um ponto a este artigo. Atualmente (verão de 2015) no RHEL7 / CentOS 7, o network-online.target está configurado incorretamente antes da instalação da rede IPv6, portanto, daemons que possuem

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

na definição de serviço que também se liga explicitamente aos endereços IPv6 provavelmente será iniciada antes da instalação e execução do IPv6, causando falhas.

Colo Host
fonte
Acho que esse é o caso apenas da configuração automática do IPv6 baseada no kernel, que é falha de qualquer maneira. Se você deseja encomendar corretamente após o IPv6, definitivamente deve usar o NetworkManager em vez de /etc/init.d/network. Se você tiver o mesmo problema, mesmo com o NM, seria um bom motivo para registrar uma solicitação de recurso. Não verifiquei com o RHEL / CentOS, posso ajudá-lo com os detalhes, se você estiver interessado.
Pavel Šimerda
0
[Unit]
After=systemd-networkd.service

funciona para mim.

Zhenxiao Hao
fonte
Não tenho certeza se funciona em alguns casos especiais, mas está errado por alguns motivos. Uma delas é que networkdfornece seu próprio serviço / espera online /. Receber e solicitar network-online.targeté o caminho certo a seguir com qualquer serviço que suporte isso.
Pavel Šimerda