Comment créer un service systemd virtuel pour arrêter / démarrer plusieurs instances ensemble?

12

Je prévois d'héberger plusieurs instances de la même application Web pour les clients utilisant systemd. Je voudrais pouvoir stopet startchaque instance de client à l' aide systemd, ainsi que le traitement de toute la collection des instances de clients comme un service unique qui peut être arrêté et démarré ensemble.

systemdsemble fournir les blocs de construction dont j'ai besoin PartOfet les fichiers d'unité de modèle, mais je suis allé arrêter le service parent, le service client enfant n'est pas arrêté. Comment puis-je faire fonctionner cela avec systemd? Voici ce que j'ai jusqu'à présent.

Le fichier de l' unité mère, 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

Un fichier de modèle d'unité nommé [email protected], utilisé pour créer des instances client:

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

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

Mon app-poc.shscript (preuve de concept qui s'imprime simplement dans un fichier journal en boucle):

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

Pour la preuve de concept, j'ai les fichiers d'unité systemd dedans ~/.config/systemd/user.

Je lance ensuite le parent et une instance basée sur le modèle (après systemctl --user daemon-reload):

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

En utilisant, journalctl -fje peux voir que les deux ont démarré et que l'instance client continue de s'exécuter. Maintenant, je m'attends à ce que l'arrêt du parent arrête l'enfant (parce que je l'ai utilisé PartOf), mais ce n'est pas le cas. De plus, démarrer le parent ne démarre pas l'enfant comme prévu non plus.

systemctl --user stop app

Merci!

(J'utilise Ubuntu 16.04 avec systemd 229).

Mark Stosberg
la source
1
"PartOf = Configure des dépendances similaires à Requiert =, mais limitées à l'arrêt et au redémarrage des unités." Si vous voulez commencer à travailler, n’avez-vous pas besoin d’utiliser à la Requires=place?
sourcejedi

Réponses:

10

Vous devez déplacer la ligne

PartOf=app.service

sur [Service]et dans la [Unit]section, et ajouter à la [Unit]de app.servicela liste des clients pour commencer, par exemple ,

[email protected] [email protected]

ou comme l'a dit sourcejedi dans les commentaires, Requires=la même chose. Vous pouvez conserver les PartOfservices d'arrêt que vous démarrez manuellement, qui ne figurent pas dans la liste ci-dessus, comme systemctl --user start [email protected].

meuh
la source
J'ai confirmé que tu avais raison PartOf. Merci. Je vais gérer les "Wants" via un lien symbolique, qui devient la seule action que je dois prendre pour activer un nouveau client avec systemd. Pour mon scénario de test: `ln -s /home/mark/.config/systemd/user/[email protected] / home / mark / .config / systemd / user / app.service.wants / unit @ foo.service`
Mark Stosberg
14

J'ai appris que c'est à cela que servent les «unités cibles» systémiques. En utilisant une unité cible, j'obtiens les avantages que je veux sans avoir besoin de créer la fausse [Service]section que j'avais ci-dessus. Un exemple de fichier de travail "Unité cible" ressemble à ceci:

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

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

Ensuite, chaque instance client doit inclure PartOfdans la [Unit]section (comme indiqué par @meuh), et doit également avoir une [Install]section pour que enableet disablefonctionne sur le service spécifique:

# 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

Pour afficher l'instance client et la faire démarrer au démarrage de la cible, cette commande d'activation unique est utilisée:

 systemctl enable app

À ce stade, je peux utiliser stopet startcontinuer app@customerpour une instance spécifique, ou je peux utiliser start appet stop apparrêter toutes les applications ensemble.

Mark Stosberg
la source
Et le statut? Je ne peux pas trouver un moyen simple d'obtenir un statut de tous les services que l'application voulait. Je sais comment je peux l'écrire, mais ...
Tommi Kyntola
1
Je veux dire comme obtenir le statut des applications dans ce groupe cible sans énumérer tout ce qui en fait partie, des caractères génériques ou non, de préférence en utilisant ce nom de groupe et sans même se soucier de quoi il est fait.
Tommi Kyntola
2
Ce n'est pas aussi simple. À quel paquet ce script appartiendrait-il? Il devrait être modifié chaque fois qu'un nouveau composant est ajouté. Oubliez cela et le déploiement / maintenance devient détraqué. Ce que je voudrais évidemment, c'est simplement ajouter un nouveau paquet avec le paramètre partOf indiquant sa présence dans ce groupe et ne pas modifier ensuite un script qui traîne. Et puis arrêter et démarrer cette cible fonctionne comme avant. Cela fonctionne, mais le statut semble sortir de cette portée. Je ne peux même pas trouver un moyen d'obtenir une liste des unités qui sont présentes dans une cible d'exécution. Ce cas d'utilisation n'est pas couvert par systemd.
Tommi Kyntola
2
@TommiKyntola Voici un bash one-liner que vous n'avez pas besoin de mettre à jour car les dépendances cibles changent:systemctl status $(systemctl list-dependencies --plain otp.target)
Mark Stosberg
2
@TommiKyntola Je suis d'accord que cela systemdpourrait améliorer la convivialité ici. J'ai ouvert une demande de fonctionnalité pour suggérer un statut amélioré pour les cibles.
Mark Stosberg