Назад | Перейти на главную страницу

Запустить N процессов с одним служебным файлом systemd

Я нашел этот служебный файл systemd для запуска autossh, чтобы поддерживать туннель 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

Есть ли способ настроить systemd для запуска нескольких туннелей в один служба.

Я не хочу создавать N системных служебных файлов, так как я хочу избежать копирования + вставки.

Все служебные файлы будут идентичны, за исключением того, что "remote.example.com" будет заменен другими именами хостов.

1,5 года спустя ...

Я задал этот вопрос примерно 1,5 года назад.

Мое мнение изменилось. Да, это хорошо, что вы можете сделать это с помощью systemd, но в будущем я буду использовать управление конфигурацией.

Почему systemd должен реализовывать язык шаблонов и заменять% h? .. Думаю, в этом нет смысла.

Несколько месяцев спустя я думаю, что этот цикл и шаблоны должны быть решены на другом уровне. Я бы сейчас использовал для этого Ansible или TerraForm.

Ну, если предположить, что только то, что меняется на единицу файла, - это remote.example.com часть, вы можете использовать Созданный обслуживание.

Из systemd.unit страница руководства:

Необязательно, единицы могут быть созданы из файла шаблона во время выполнения. Это позволяет создавать несколько единиц из одного файла конфигурации. Если systemd ищет файл конфигурации модуля, он сначала ищет буквальное имя модуля в файловой системе. Если это не приведет к успеху и имя модуля содержит символ «@», systemd будет искать шаблон модуля с таким же именем, но с удаленной строкой экземпляра (т.е. частью между символом «@» и суффиксом). Пример: если запрашивается служба getty@tty3.service и файл с таким именем не найден, systemd будет искать getty @ .service и создавать экземпляр службы из этого файла конфигурации, если он найден.

По сути, вы создаете единичный файл, содержащий переменную (обычно %i), где возникают различия, а затем они связываются, когда вы «включаете» эту службу.

Например, у меня есть файл модуля с именем /etc/systemd/system/autossh@.service это выглядит так:

[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

Что я затем включил

[user@anotherhost ~]$ sudo systemctl enable autossh@somehost.example.com
ln -s '/etc/systemd/system/autossh@.service' '/etc/systemd/system/multi-user.target.wants/autossh@somehost.example.com.service'

И может взаимодействовать с

[user@anotherhost ~]$ sudo systemctl start autossh@somehost.example.com
[user@anotherhost ~]$ sudo systemctl status autossh@somehost.example.com
autossh@somehost.example.service - AutoSSH service for ServiceABC on somehost.example
   Loaded: loaded (/etc/systemd/system/autossh@.service; 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/autossh@somehost.example.com.service
           ├─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 autossh@somehost.example.com
[user@anotherhost ~]$ sudo systemctl status autossh@somehost.example.com
autossh@somehost.example.com.service - AutoSSH service for ServiceABC on somehost.example.com
   Loaded: loaded (/etc/systemd/system/autossh@.service; 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.

Как видите, все экземпляры %i в файле модуля заменить на somehost.example.com.

Есть еще куча спецификаторы что вы можете использовать в модульном файле, но я нахожу %i лучше всего работать в таких случаях.

Вот пример Python, который я искал. В @ в имени файла службы позволяет запускать N процессов:

$ cat /etc/systemd/system/my-worker@.service

[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

Различные способы его вызова

Например, включение различных подсчетов:

  • Включите 30 воркеров:

    sudo systemctl enable my-worker\@{1..30}.service
    
  • Включите 2 воркера:

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

Затем обязательно перезагрузите:

sudo systemctl daemon-reload

Теперь вы можете запускать / останавливать потом разными способами:

  • Начало 1:

    sudo systemctl start my-worker@2.service
    
  • Начать несколько:

    sudo systemctl start my-worker@{1..2}
    
  • Остановить несколько:

    sudo systemctl stop my-worker@{1..2}
    
  • Проверить статус:

    sudo systemctl status my-worker@1
    

ОБНОВИТЬ: Чтобы управлять экземплярами как одной службой, вы можете сделать что-то вроде этого:

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

[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

И теперь вы можете управлять всеми экземплярами с sudo systemctl some-worker (start|restart|stop)

Вот шаблон для вашего 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()

Ответ GregL мне очень помог. Вот пример шаблона модуля, который я использовал в своем коде, используя приведенный выше пример для сервера заданий gearman. Я сделал сценарий оболочки, который позволяет мне создать X количество «рабочих», используя этот единственный шаблон.

[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

Я искал решение аналогичной задачи и на самом деле нашел одно, которое, как мне кажется, легче выполнить, но должно быть хакерский. (и здесь не упоминается)

Мне нужно было создать несколько ssh-соединений после того, как vpn создаст туннель, поэтому я создал службу, которая зависит от устройства tun и вызывает сценарий оболочки с соответствующими командами.

Сервис /etc/systemd/system/ssh_tunnel.service:

[Unit]
Description=Reverse SSH Service to access hidden services
ConditionPathExists=|/usr/bin
Wants=sys-devices-virtual-net-tun0.device
After=network.target sys-devices-virtual-net-tun0.device

[Service]
Type=forking
ExecStart=/bin/sh /etc/openvpn/ssh_tunnels.sh 
RemainAfterExit=yes
TimeoutSec=0
GuessMainPID=no

[Install]
WantedBy=multi-user.target

/etc/openvpn/ssh_tunnels.sh:

!/bin/bash
#sleep 15

echo 'Tunelling some ports'
killall -HUP ssh

su - user -c 'ssh -f admin@172.171.1.1 -p 9999 -L :3690:svn.newbox.ru:3690 -L :8888:10.1.20.55:80 -L :8181:10.1.10.10:80 -N -vvv'

ssh -i /home/user/.ssh/id_rsa -f admin@172.171.1.1 -p 9999 -L :587:mail.domain.ru:587 -L :995:mail.newbox.ru:995 -L :22:10.1.2.1:22 -N -vvv &

exit 0

Результат:

# systemctl status ssh_tunnel.service
● ssh_smartex.service - Reverse SSH Service to access hidden services
     Loaded: loaded (/etc/systemd/system/ssh_tunnel.service; enabled; vendor preset: disabled)
     Active: active (running) since Fri 2020-03-20 16:01:07 UTC; 22min ago
    Process: 156 ExecStart=/bin/sh /etc/openvpn/ssh_tunnel.sh (code=exited, status=0/SUC>
      Tasks: 2 (limit: 4915)
     Memory: 3.8M
     CGroup: /system.slice/ssh_tunnel.service
             ├─166 ssh -f admin@172.171.1.1 -p 9999 -L :3690:svn.newbox.ru:3690 -L :8888:10.1.20.55:80 ->
             └─168 ssh -i /home/user/.ssh/id_rsa -f admin@172.171.1.1 -p 9999 -L :587:mail.newbox.ru:5>
...

Однако я еще не проверял, как он выживает после перезапуска vpn, но это уже другая тема.