Como criar um serviço systemd virtual para parar / iniciar várias instâncias juntos?

12

Pretendo hospedar várias instâncias do mesmo aplicativo Web para clientes que estiverem usando systemd. Eu gostaria de ser capaz de stope startcada instância do cliente usando systemd, bem como o tratamento de toda a coleção de instâncias do cliente como um único serviço que pode ser parado e iniciado em conjunto.

systemdparece fornecer os blocos de construção que eu preciso usar PartOfe os arquivos da unidade de modelo, mas fui eu paro o serviço pai, o serviço ao cliente filho não está parado. Como posso fazer isso funcionar com o systemd? Aqui está o que eu tenho até agora.

O arquivo da unidade pai app.service:

[Unit]
Description=App Web Service

[Service]
# Don't run as a deamon (because we've got nothing to do directly)
Type=oneshot
# Just print something, because ExecStart is required
ExecStart=/bin/echo "App Service exists only to collectively start and stop App instances"
# Keep running after Exit start finished, because we want the instances that depend on this to keep running
RemainAfterExit=yes
StandardOutput=journal

Um arquivo de modelo de unidade chamado [email protected], usado para criar instâncias do cliente:

[Unit]
Description=%I Instance of App Web Service

[Service]
PartOf=app.service
ExecStart=/home/mark/bin/app-poc.sh %i
StandardOutput=journal

Meu app-poc.shscript (Prova de conceito que apenas imprime no arquivo de log em um loop):

#!/bin/bash
# Just a temporary code to fake a full daemon.
while :
do
  echo "The App PoC loop for $@"
  sleep 2;
done

Para a prova de conceito, eu tenho os arquivos da unidade systemd ~/.config/systemd/user.

Em seguida, inicio o pai e uma instância com base no modelo (depois systemctl --user daemon-reload):

systemctl --user start app
systemctl --user start [email protected]

Ao usar journalctl -f, posso ver que ambos iniciaram e que a instância do cliente continua em execução. Agora eu espero que desligar o pai pare o filho (porque eu usei PartOf), mas não. Além disso, iniciar o pai também não está iniciando o filho conforme o esperado.

systemctl --user stop app

Obrigado!

(Estou usando o Ubuntu 16.04 com systemd 229).

Mark Stosberg
fonte
1
"PartOf = Configura dependências semelhantes a Requer =, mas limitadas à parada e reinicialização de unidades." Se você deseja começar a trabalhar, não precisa usá-lo Requires=?
Fonte # jedi

Respostas:

10

Você precisa mover a linha

PartOf=app.service

fora [Service]e dentro da [Unit]seção, e adicionar à [Unit]da app.servicelista de clientes para começar, por exemplo,

[email protected] [email protected]

ou como sourcejedi disse nos comentários, Requires=a mesma coisa. Você pode manter os PartOfserviços para interromper, iniciados manualmente, que não estão na lista acima, como systemctl --user start [email protected].

meuh
fonte
Eu confirmei que você estava certo PartOf. Obrigado. Vou lidar com o "Quer" através de um link simbólico, que se torna a única ação que preciso executar para ativar um novo cliente com o systemd. Para o meu caso de teste: `ln -s /home/mark/.config/systemd/user/[email protected] / home / mark / .config / systemd / user / app.service.wants / unity @ foo.service`
Mark Stosberg 17/05
13

Aprendi que para isso servem as "Unidades de destino". Ao usar uma unidade de destino, recebo os benefícios que desejo sem precisar criar a [Service]seção falsa que tinha acima. Um exemplo prático de arquivo "Unidade de destino" se parece com isso:

# named like app.target
[Unit]
Description=App Web Service

# This collection of apps should be started at boot time.
[Install]
WantedBy=multi-user.target

Em seguida, cada instância do cliente deve incluir PartOfna [Unit]seção (como fora apontado por @meuh), e também deve ter uma [Install]seção para que enablee disablevai trabalhar no serviço específico:

# In a file name like [email protected]
[Unit]
Description=%I Instance of App Web Service
PartOf=app.target

[Service]
ExecStart=/home/mark/bin/app-poc.sh %i
Restart=on-failure
StandardOutput=journal

# When the service runs globally, make it run as a particular user for added security
#User=myapp
#Group=myapp

# When systemctl enable is used, make this start when the App service starts
[Install]
WantedBy=app.target

Para abrir a instância do cliente e iniciar quando o destino é iniciado, este comando de ativação único é usado:

 systemctl enable app

Agora, neste momento eu posso usar stope startem app@customerque para uma instância específica, ou posso usar start appe stop appparar todos os aplicativos juntos.

Mark Stosberg
fonte
E quanto ao status? Não consigo encontrar uma maneira simples de obter o status de todos os serviços que o App queria. Eu sei como eu pode script isso, mas ...
Tommi Kyntola
1
Quero dizer, obter o status de aplicativos nesse grupo-alvo sem listar todos os que fazem parte dele, curingas ou não, de preferência usando o nome desse grupo e nem se importando com o que é feito.
Tommi Kyntola 6/06/06
2
Não é tão simples assim. A qual pacote esse script pertenceria? Teria que ser modificado toda vez que um novo componente fosse adicionado. Esqueça isso e a implantação / manutenção dá errado. Obviamente, o que eu gostaria é adicionar um novo pacote com a configuração partOf indicando sua presença nesse grupo e não modificar algum script persistente. E então pare e o início desse destino funcionará como antes. Isso funciona, mas o status parece estar fora desse escopo. Não consigo nem encontrar uma maneira de obter uma lista de unidades que estão presentes no tempo de execução em um destino. Este caso de uso não é coberto pelo systemd.
Tommi Kyntola
2
@TommiKyntola Aqui está uma festa de uma linha que você não precisa de atualização como o alvo dependências mudança:systemctl status $(systemctl list-dependencies --plain otp.target)
Mark Stosberg
2
@ TommiKyntola Concordo que systemdpoderia melhorar a usabilidade aqui. Eu abri um pedido de recurso para sugerir melhoria do estado para alvos.
Mark Stosberg