Como um sinalizador de serviço systemd está pronto, para que outros serviços possam esperar que esteja pronto antes de começar?

8

Eu tenho vários serviços (digamos C0, C1... C9) que só devem ser iniciados depois que um serviço Sconcluir sua inicialização e estiver totalmente em execução e pronto para os outros serviços. Como organizo isso com o systemd?

Em Solicitando serviços com ativação de caminho e destino no systemd , supõe-se que o serviço Stenha um mecanismo para gravar algum tipo de arquivo de sinalizador. Suponha aqui, por outro lado, que eu tenho controle total sobre o programa que o serviço Sexecuta e que posso adicionar mecanismos systemd, se necessário.

JdeBP
fonte

Respostas:

7

Não é necessário necessariamente isso.

Se os Cserviços precisarem esperar para Sestarem prontos para poderem abrir uma conexão de soquete, não será necessário fazer isso. Em vez disso, pode-se tirar proveito da abertura do soquete de escuta precoce pelos gerentes de serviço.

Vários sistemas, incluindo o s6 de Laurent Bercot , o meu nosh toolset e o systemd, têm maneiras pelas quais um soquete de escuta pode ser aberto desde o início, a primeira coisa na configuração do serviço. Todos eles envolvem algo diferente do programa de serviço que abre o (s) soquete (s) de escuta e o programa de serviço, quando chamado, recebe os soquetes de escuta como descritores de arquivo já abertos.

Com o systemd, especificamente, cria-se uma unidade de soquete que define o soquete de escuta. systemd abre a unidade de soquete e a configura para que o subsistema de rede do kernel esteja escutando as conexões; e o passa para o serviço real como um descritor de arquivo aberto quando se trata de gerar o (s) processo (s) que tratam das conexões com o soquete. (Ele pode fazer isso de duas maneiras, da mesma forma que inetdpoderia, mas uma discussão dos detalhes dos serviços Accept=trueversus Accept=falseestá além do escopo desta resposta.)

O ponto importante é que não é necessário mais pedidos do que isso. O kernel agrupa as conexões do cliente em uma fila até que o programa de serviço seja inicializado e pronto para aceitá-las e conversar com os clientes.

Quando se faz, protocolos de prontidão são a coisa certa.

O systemd possui um conjunto de protocolos de prontidão que ele entende, serviço especificado por serviço com a Type=configuração na unidade de serviço. O protocolo de prontidão particular de interesse aqui é o notifyprotocolo de prontidão. Com isso, o systemd é instruído a esperar mensagens do serviço e, quando o serviço estiver pronto, envia uma mensagem que sinaliza prontidão. systemd atrasa a ativação de outros serviços até que a prontidão seja sinalizada.

Fazer uso disso envolve duas coisas:

  • Modificando o código de Smodo que chame algo como a função de Pierre-Yves Ritschard notify_systemd()ou a função de Cameron T. Norman notify_socket().
  • Configurando a unidade de serviço para o serviço com Type=notifye NotifyAccess=main.

A NotifyAccess=mainrestrição (que é o padrão) é porque o systemd precisa saber para ignorar mensagens de programas maliciosos (ou simplesmente defeituosos), porque qualquer processo no sistema pode enviar mensagens para o soquete de notificação do systemd.

Utiliza-se o código de Pierre-Yves Ritschard ou Cameron T Norman como preferência, porque não exclui a possibilidade de ter esse mecanismo no UbuntuBSD, Debian FreeBSD, real FreeBSD, TrueBS, TrueOS, OpenBSD e assim por diante; que o código fornecido pelos autores do sistema exclui.

Uma armadilha a evitar é o systemd-notifyprograma. Ele tem vários problemas importantes, e o menos importante é que as mensagens enviadas com ele podem acabar sendo descartadas sem serem processadas pelo systemd. O problema mais importante nesse caso é que ele não é executado como o processo "principal" do serviço; portanto, é necessário abrir as notificações de prontidão do serviço Spara todos os processos do sistema NotifyAccess=all.

Outra armadilha a evitar é pensar que o forkingprotocolo é mais simples. Não é. Fazê-lo corretamente envolve não bifurcar e sair do pai até que (por um lado) todos os threads de trabalho do programa estejam em execução. Isso não corresponde à forma como a esmagadora maioria dos daemons que bifurcam realmente se bifurcam.

Leitura adicional

JdeBP
fonte
1
Segundo o homem systemd.service(5), NotifyAccess=allaceitará mensagens de todos os membros do grupo de controle do serviço , o que não implica em nenhum processo invasor no sistema. Isso é seguro o suficiente para a maioria dos casos de uso. Além disso, sua preocupação com a portabilidade para outros sistemas operacionais não é relevante para o OP, pois já estamos no tópico Systemd aqui.
Amir
1

Referindo-se à página de manual para systemd.service(5), especificamente a seção sobre Type = , cada tipo de serviço possui uma maneira diferente para o Systemd determinar que está pronto para oferecer funcionalidade a outros serviços:

  • Se Type=simple, seus canais de comunicação devem ser instalados antes da inicialização do daemon (por exemplo, soquetes configurados pelo systemd, via ativação do soquete).

  • Se Type=forking, espera-se que o processo pai saia quando a inicialização estiver concluída e todos os canais de comunicação estiverem configurados.

  • Se Type=dbus, espera-se que o daemon adquira um nome no barramento D-Bus, quando o systemd prosseguirá com o início das unidades de acompanhamento.

  • Se Type=notify, espera-se que o daemon envie uma mensagem de notificação por meio de sd_notify(3)ou uma chamada equivalente quando concluir a inicialização. O systemd continuará com as unidades de acompanhamento após o envio desta mensagem de notificação.

Para a última opção (enviando uma mensagem por sd_notify), você pode usar o systemd-notifyutilitário e lembre-se de conceder acesso a ele NotifyAccess=all.

Como você tem controle sobre o serviço S, pode escolher a melhor opção para o seu caso de uso ou simplesmente a mais fácil de implementar.

Amir
fonte
1

como isso:

S.service

[Unit]
Description=My main Service

[Service]
Type=notify
ExecStart=/usr/bin/myBinary

C0.service

[Unit]
Description=Dependent service number 0
PartOf=S.service

C1.service

[Unit]
Description=Dependent service number 1
PartOf=S.service

C9.service

[Unit]
Description=Dependent service number 9
PartOf=S.service

Onde / usr / bin / myBinary faz uma chamada sd_notify READY = 1 quando a inicialização estiver concluída.

Dependendo de como você deseja que a dependência se comporte, você pode usar PartOf, Requer ou BindsTo ou outros .

code_monk
fonte