Systemd et processus d'apparition

14

Normalement, je ne poste pas ici, mais j'arrache mes cheveux par-dessus. J'ai un script Python qui bifurque lors de son lancement et est responsable du démarrage d'un tas d'autres processus. Ce script était lancé au démarrage via sysvinit, mais récemment j'ai mis à niveau vers Debian Jessie, je l'ai donc adapté pour être lancé via systemd.

Malheureusement, je rencontre un problème que je ne peux pas résoudre. Lorsque vous lancez le script directement dans un shell utilisateur, il lance correctement ses processus enfants et lorsque le script se termine, les processus enfants sont orphelins et continuent de s'exécuter.

Au lancement via systemd, si le processus parent se termine, les enfants sortent tous aussi (Eh bien, les écrans qu'ils lancent meurent et apparaissent comme morts ???)

Idéalement, je dois pouvoir redémarrer le script parent sans tuer tous les processus enfants, y a-t-il quelque chose qui me manque?

Merci!

[Unit]
Description=Server commander
After=network.target

[Service]
User=serveruser
Type=forking
PIDFile=/var/Server/Server.pid

ExecStart=/var/Server/Server.py
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

Edit: Il est probablement pertinent pour moi de souligner que le script Python est essentiellement un «contrôleur» pour ses processus enfants. Il démarre et arrête les serveurs dans les écrans GNU à la demande d'un serveur central. Il est normalement toujours en cours d'exécution, il ne génère pas de services et ne quitte pas. Il y a cependant des cas où j'aimerais pouvoir recharger le script sans tuer les processus enfants, même si cela signifie que les processus sont orphelins au pid 1. En fait, cela n'aurait même pas d'importance si le script Python a démarré les processus comme un processus parent, si cela est même possible.

Une meilleure explication de son fonctionnement:

  • Systemd apparaît /Server.py
  • Server.py fourche et écrit le fichier pid pour Systemd
  • Server.py génère ensuite les processus serveur dans l'écran GNU en fonction de ses instructions
  • Server.py continue de s'exécuter pour effectuer tous les redémarrages demandés au serveur

Lors du lancement sans Systemd, Server.py peut être redémarré et les écrans GNU qu'il lance ne sont pas affectés. Lors du lancement avec Systemd, lorsque Server.py s'arrête, au lieu que ces processus d'écran soient rendus orphelins au pid 1, ils sont tués.

Bottswana
la source
1
Il est difficile de fournir une solution sans avoir le Server.pycode et une description de la façon dont les services lancés se bifurquent (s'ils se bifurquent). Cependant, d'une manière générale, il s'agit d'un problème de non-concordance du protocole de préparation .
intelfx
BTW, le ExecStop=n'est pas nécessaire. L'action par défaut de systemd à l'arrêt est de tuer les processus. Vous voudrez peut-être jeter un œil à la documentation de KillMode=directive.
intelfx
1
Et, enfin ... S'il n'y a pas de protocole de préparation approprié (l'un simpleou forking, en fait), le dernier recours serait Type=oneshot, RemainAfterExit=yeset KillMode=control-group.
intelfx
@intelfx Essentiellement, le script Python lance un serveur dans un écran à l'aide de Subprocess.call. C'est plus compliqué que cela parce que le script reçoit des commandes d'ailleurs lui indiquant quels écrans démarrer et lesquels ne pas démarrer. Les écrans disponibles sont également dynamiques, c'est pourquoi ils ne peuvent pas être des services système à part entière. Idéalement, je ne veux pas que systemd traite ces écrans comme faisant partie du service, mais actuellement, ils sont vidés dans le même groupe de processus et meurent avec le maître s'il est redémarré.
Bottswana
Mon intuition est que systemd ne "gère" pas un processus de contrôle comme ça (il recherche juste les PID au moment du démarrage, ne reconnaît pas les derniers ...): |
rogerdpack

Réponses:

9

J'ai réussi à résoudre ce problème simplement en définissant KillMode sur process au lieu de control-group (par défaut). Merci a tous

Bottswana
la source
Cela semble être plus qu'un correctif, voir les autres réponses ... si vous faites cela et faites un "arrêt systemctl", cela ne tuera pas les processus enfants qu'ils exécuteront toujours [?] en dehors de la surveillance de systemctl?
rogerdpack
5

J'ai un script Python qui bifurque lors de son lancement et est responsable du démarrage d'un tas d'autres processus.

Ce qui indique que vous le faites mal. Plus à ce sujet dans un instant.

lorsque le script se termine, les processus enfants sont orphelins et continuent de s'exécuter.

Ce n'est pas un comportement correct des démons. Si le processus "principal" - dans ce cas l'enfant que vous avez forké, puisque vous l'avez spécifié Type=forking- se termine, systemd considère que le service a été désactivé et met fin à tout autre processus encore en cours (dans le groupe de contrôle) afin de ranger .

Parfois, la conversion des rcscripts System 5 en systemd n'est pas simple, car la bonne façon de faire les choses sous systemd est assez différente. La bonne façon de faire (disons) OpenVPN, ou OpenStack, ou OSSEC HIDS dans systemd n'est pas la même que l'on le ferait avec un rcscript. Le fait que vous ayez un script qui bifurque, puis génère toute une série de processus de petits-enfants, puis que vous vous attendez à ce que ces petits-enfants continuent de fonctionner indique que vous perpétrez le même type d'horreur que ossec-control, bien qu'avec deux niveaux de fourche de moins. Si vous vous retrouvez en train d'écrire un script "maître" qui vérifie les drapeaux "activer" et exécute des processus enfants pour les parties "activées" de votre système, alors vous faites la même erreur que l'horrible ossec-control.

Aucun mécanisme de ce type n'est nécessaire avec systemd. C'est déjà un gestionnaire de services. Par /unix//a/200365/5132 , la bonne façon de procéder à ce sujet dans systemd n'est pas d'avoir un service qui engendre une tentative loufoque et confuse d'avoir des "sous-services". C'est d'avoir chaque processus enfant en tant que service système à part entière à part entière. Ensuite, on active et désactive, et démarre et arrête les différentes parties du système à l'aide des commandes systemd normales. Comme vous pouvez le voir dans le cas OSSEC HIDS, une unité de service de modèle simple couvre presque tous les services (à l'exception de /ubuntu//a/624871/43344 ), ce qui permet de faire des choses telles que l' systemctl enable [email protected]activation d'une optionagentlessdsans aucun besoin de l’horrible mécanisme de "script maître" qui était nécessaire avec System 5 rc.

Il existe de nombreux cas, peut-être pas aussi extrêmes que OSSEC HIDS, où une telle réflexion est nécessaire. Les MTS comme exim et sendmail en sont deux. On aurait pu avoir un seul rcscript qui génère un exécuteur de file d'attente, un démon de soumission SMTP et un démon de relais SMTP, avec un tas de variables shell ad hoc dans un fichier de configuration pour contrôler exactement celles qui sont exécutées. Mais la bonne façon de le faire avec systemd est d'avoir trois unités de service appropriées (dont deux ont des unités de socket associées ) et pas de trucs ad hoc du tout, juste les mécanismes réguliers du gestionnaire de services.

JdeBP
la source
J'apprécie les commentaires à ce sujet. Bien que j'accepte que les services de sous-ensemble aient du sens, cela a été fait en Python pour une raison que je ne peux pas expliquer. Ma seule solution est de trouver un moyen de faire fonctionner cette méthode. Merci quand même. J'adorerais le faire correctement.
Bottswana
Les sous-services que le script lance ne sont que des serveurs fonctionnant dans l'écran gnu en tant qu'utilisateur spécifique. Ces serveurs changent beaucoup, certains sont ajoutés, certains sont supprimés et cela est contrôlé ailleurs, donc ils ne peuvent pas vraiment être des services réels dans systemd, car cela ajoute à beaucoup de complexité et ne peut pas être géré de manière centralisée. Le même script est également utilisé sur les serveurs non systemd.
Bottswana
systemd possède des fonctionnalités explicites pour permettre l'ajout et la suppression de services sans avoir besoin d'un accès root. Le "également utilisé sur les services non-systemd" est le seul des arguments ci-dessus qui ne peut pas être corrigé en ajoutant plus de systemd ... eh bien, sans doute, même celui-là pourrait l'être aussi. :)
Charles Duffy
0

Vous pouvez simplement dormir le parent et attendre que systemd le tue au moment de l'arrêt.

Craig Hicks
la source