Démarrer N processus avec un fichier de service systemd

36

J'ai trouvé ce fichier de service Systemd pour démarrer autossh afin de conserver un tunnel ssh: https://gist.github.com/thomasfr/9707568

[Unit]
Description=Keeps a tunnel to 'remote.example.com' open
After=network.target

[Service]
User=autossh
# -p [PORT]
# -l [user]
# -M 0 --> no monitoring
# -N Just open the connection and do nothing (not interactive)
# LOCALPORT:IP_ON_EXAMPLE_COM:PORT_ON_EXAMPLE_COM
ExecStart=/usr/bin/autossh -M 0 -N -q -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -p 22 -l autossh remote.example.com -L 7474:127.0.0.1:7474 -i /home/autossh/.ssh/id_rsa

[Install]
WantedBy=multi-user.target

Existe-t-il un moyen de configurer systemd pour démarrer plusieurs tunnels dans un service.

Je ne veux pas créer de fichiers de service système N, car je veux éviter le copier-coller.

Tous les fichiers de service seraient identiques à l'exception de "remote.example.com" qui serait remplacé par d'autres noms d'hôte.

1,5 an plus tard ...

J'ai posé cette question il y a environ un an et demi.

Mon esprit a un peu changé. Oui, c'est bien, vous pouvez le faire avec systemd (je l'utilise toujours), mais j'utiliserai la gestion de la configuration à l'avenir.

Pourquoi systemd devrait-il implémenter un langage de template et substituer% h?

Plusieurs mois plus tard, je pense que cette boucle et ces modèles devraient être résolus avec un outil qui automatise la configuration. J'utilise un outil de cette liste sur wikipedia maintenant.

Guettli
la source
En d'autres termes, vous dites que vous utilisez un système de gestion de la configuration pour générer plusieurs fichiers de service presque identiques afin d'accomplir cette tâche? Hmmm, peut-être. Comme dans la plupart des cas, il n'y a pas de ligne de démarcation claire entre eux.
Pgoetz
@pgoetz La gestion de la configuration est encore nouvelle pour moi, mais elle présente un avantage si vous examinez le sujet de cette question: si vous examinez le résultat de la gestion de la configuration, tous ceux qui connaissent les fichiers de service Systemd vont la comprendre: fichiers de service simples et clairs . Je pense qu'il est plus logique d'apprendre et d'utiliser un système de gestion de configuration, car les connaissances peuvent être utilisées pour toutes les configurations dans / etc, et pas seulement pour systemd.
guettli

Réponses:

47

Eh bien, en supposant que la seule chose qui change par fichier d'unité est la remote.example.compièce, vous pouvez utiliser un service instancié .

De la systemd.unitpage de manuel:

Facultativement, les unités peuvent être instanciées à partir d'un fichier de modèle au moment de l'exécution. Cela permet la création de plusieurs unités à partir d'un seul fichier de configuration. Si systemd recherche un fichier de configuration d'unité, il recherchera d'abord le nom d'unité littéral dans le système de fichiers. Si cela n'aboutit pas et que le nom de l'unité contient un caractère "@", systemd recherchera un modèle d'unité qui partage le même nom mais avec la chaîne d'instance (c'est-à-dire la partie entre le caractère "@" et le suffixe) supprimée. Exemple: si un service [email protected] est demandé et qu'aucun fichier portant ce nom n'est trouvé, systemd recherchera getty @ .service et instanciera un service à partir de ce fichier de configuration, le cas échéant.

Fondamentalement, vous créez un fichier unité unique, qui contient une variable (généralement %i) où se produisent les différences, puis elles sont liées lorsque vous "activez" ce service.

Par exemple, j'ai un fichier d'unité appelé /etc/systemd/system/[email protected]qui ressemble à ceci:

[Unit]
Description=AutoSSH service for ServiceABC on %i
After=network.target

[Service]
Environment=AUTOSSH_GATETIME=30 AUTOSSH_LOGFILE=/var/log/autossh/%i.log AUTOSSH_PIDFILE=/var/run/autossh.%i.pid
PIDFile=/var/run/autossh.%i.pid
#Type=forking
ExecStart=/usr/bin/autossh -M 40000 -NR 5000:127.0.0.1:5000 -i /opt/ServiceABC/.ssh/id_rsa_ServiceABC -l ServiceABC %i

[Install]
WantedBy=multi-user.target

Que j'ai ensuite activé

[user@anotherhost ~]$ sudo systemctl enable [email protected]
ln -s '/etc/systemd/system/[email protected]' '/etc/systemd/system/multi-user.target.wants/[email protected]'

Et peut interagir avec

[user@anotherhost ~]$ sudo systemctl start [email protected]
[user@anotherhost ~]$ sudo systemctl status [email protected]
[email protected] - AutoSSH service for ServiceABC on somehost.example
   Loaded: loaded (/etc/systemd/system/[email protected]; enabled)
   Active: active (running) since Tue 2015-10-20 13:19:01 EDT; 17s ago
 Main PID: 32524 (autossh)
   CGroup: /system.slice/system-autossh.slice/[email protected]
           ├─32524 /usr/bin/autossh -M 40000 -NR 5000:127.0.0.1:5000 -i /opt/ServiceABC/.ssh/id_rsa_ServiceABC -l ServiceABC somehost.example.com
           └─32525 /usr/bin/ssh -L 40000:127.0.0.1:40000 -R 40000:127.0.0.1:40001 -NR 5000:127.0.0.1:5000 -i /opt/ServiceABC/.ssh/id_rsa_ServiceABC -l ServiceABC somehost.example.com

Oct 20 13:19:01 anotherhost.example.com systemd[1]: Started AutoSSH service for ServiceABC on somehost.example.com.
[user@anotherhost ~]$ sudo systemctl status [email protected]
[user@anotherhost ~]$ sudo systemctl status [email protected]
[email protected] - AutoSSH service for ServiceABC on somehost.example.com
   Loaded: loaded (/etc/systemd/system/[email protected]; enabled)
   Active: inactive (dead) since Tue 2015-10-20 13:24:10 EDT; 2s ago
  Process: 32524 ExecStart=/usr/bin/autossh -M 40000 -NR 5000:127.0.0.1:5000 -i /opt/ServiceABC/.ssh/id_rsa_ServiceABC -l ServiceABC %i (code=exited, status=0/SUCCESS)
 Main PID: 32524 (code=exited, status=0/SUCCESS)

Oct 20 13:19:01 anotherhost.example.com systemd[1]: Started AutoSSH service for ServiceABC on somehost.example.com.
Oct 20 13:24:10 anotherhost.example.com systemd[1]: Stopping AutoSSH service for ServiceABC on somehost.example.com...
Oct 20 13:24:10 anotherhost.example.com systemd[1]: Stopped AutoSSH service for ServiceABC on somehost.example.com.

Comme vous pouvez le constater, toutes les instances du %ifichier unité sont remplacées par somehost.example.com.

Il y a beaucoup d'autres spécificateurs que vous pouvez utiliser dans un fichier unité, mais je trouve que cela %ifonctionne mieux dans des cas comme celui-ci.

GregL
la source
Wow, systemd est génial.
Guettli
Vous ne montrez pas comment démarrer automatiquement au démarrage, y compris ceux pour lesquels démarrer.
Craig Hicks
Avec Systemd, c'est l' enableaction qui fait qu'une unité / un service démarre au démarrage.
GregL
Puis-je activer / désactiver indépendamment les instances?
Soumya Kanti
Oui, c'est ce que vous faites lorsque vous les activez / les désactivez.
GregL
15

Voici un exemple en python, ce que je recherchais. Le @nom de fichier du service vous permet de démarrer N processus:

$ cat /etc/systemd/system/[email protected]

[Unit]
Description=manages my worker service, instance %i
After=multi-user.target

[Service]
PermissionsStartOnly=true
Type=idle
User=root
ExecStart=/usr/local/virtualenvs/bin/python /path/to/my/script.py
Restart=always
TimeoutStartSec=10
RestartSec=10

Diverses méthodes pour l'appeler

Activer différents comptes, par exemple:

  • Activer 30 travailleurs:

    sudo systemctl enable my-worker\@{1..30}.service
    
  • Activer 2 travailleurs:

    sudo systemctl enable my-worker\@{1..2}.service
    

Alors assurez-vous de recharger:

sudo systemctl daemon-reload

Vous pouvez maintenant démarrer / arrêter de différentes manières:

  • Début 1:

    sudo systemctl start [email protected]
    
  • Commencer plusieurs:

    sudo systemctl start my-worker@{1..2}
    
  • Stop Multiple:

    sudo systemctl stop my-worker@{1..2}
    
  • Vérifier l'état:

    sudo systemctl status my-worker@1
    

UPDATE : Pour gérer les instances en tant que service unique, vous pouvez procéder comme suit:

/etc/systemd/system/[email protected]:

[Unit]
Description=manage worker instances as a service, instance %i
Requires=some-worker.service
Before=some-worker.service
BindsTo=some-worker.service

[Service]
PermissionsStartOnly=true
Type=idle
User=root
#EnvironmentFile=/etc/profile.d/optional_envvars.sh
ExecStart=/usr/local/virtualenvs/bin/python /path/to/my/script.py
TimeoutStartSec=10
RestartSec=10

[Install]
WantedBy=some-worker.service

/usr/bin/some-worker-start.sh:

#!/bin/bash
systemctl start some-worker@{1..10}

/etc/systemd/system/some-worker.service:

[Unit]
Description=manages some worker instances as a service, instance

[Service]
Type=oneshot
ExecStart=/usr/bin/sh /usr/bin/some-worker-start.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Et maintenant, vous pouvez gérer toutes les instances avec sudo systemctl some-worker (start|restart|stop)

Voici un passe-partout pour votre script.py:

#!/usr/bin/env python

import logging


def worker_loop():
    shutdown = False
    while True:

        try:
            if shutdown:
                break

            # Your execution logic here.
            # Common logic - i.e. consume from a queue, perform some work, ack message
            print("hello world")

        except (IOError, KeyboardInterrupt):
            shutdown = True
            logging.info("shutdown received - processing will halt when jobs complete")
        except Exception as e:
            logging.exception("unhandled exception on shutdown. {}".format(e))


if __name__ == '__main__':
    worker_loop()
radtek
la source
@radek: Deux choses que je ne comprends pas: Premièrement,% i n'est utilisé que dans la description du fichier unité. Comment la commande start sait-elle quoi commencer? Deuxièmement, comment systemctl some-worker (start|restart|stop)savoir sur quelles instances travailler?
U. Windl
% i est la sortie de @ dans le nom du fichier de service. La deuxième partie est déjà expliquée dans la réponse, voir Now you can start/stop then in various ways.
Radtek
Je pense que sa réponse est incomplète sans que les scripts soient impliqués. La plupart des "magie" se fait à l'intérieur des scripts qui manquent.
U. Windl
J'ai fourni une solution de travail complète ici en fait. De quels "scripts" faites-vous référence? /path/to/my/script.py peut être ce que vous voulez, un "bonjour le monde" si vous voulez. Quelque chose qui restera actif jusqu'à ce qu'il reçoive un signal de mise à mort. Veuillez noter que la question n'est pas spécifique à Python.
Radtek
Wow, il vous permet de commencer plusieurs à la fois? l'esprit soufflé ...
rogerdpack
1

La réponse de GregL m'a beaucoup aidé. Voici un exemple de modèle d'unité que j'ai utilisé dans mon code en utilisant l'exemple ci-dessus pour un serveur de travaux d'engrenage. J'ai créé un script shell qui me permet de créer une quantité X de "travailleurs" à l'aide de ce modèle.

[Unit]
Description=az gearman worker
After=gearman-job-server.service

[Service]
PIDFile=/var/run/gearman_worker_az%i.pid
Type=simple
User=www-data
WorkingDirectory=/var/www/mysite.com/jobs/
ExecStart=/usr/bin/php -f gearman_worker_az.php > /dev/null 2>&1
Restart=on-success
KillMode=process

[Install]
WantedBy=multi-user.target
Kyle Anderson
la source