使用一个systemd服务文件启动N个进程


36

我发现此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?

几个月后,我认为应该使用可自动配置的工具来解决此循环和模板问题。我现在在维基百科上使用此列表中的一种工具。


换句话说,您是说使用配置管理系统来生成多个几乎相同的服务文件来完成此任务?嗯,也许吧。与大多数此类问题一样,没有明确的分隔线将它们分开。
pgoetz

@pgoetz配置管理对我来说仍然是新手,但是如果您看一下这个问题的主题,它会带来好处:如果您看一下配置管理的结果,那么所有知道systemd服务文件的人都会理解它:普通和简单的服务文件。我认为学习和使用配置管理系统更有意义,因为知识可以用于/ etc中的所有配置,而不仅仅是systemd。
guettli

Answers:


47

好吧,假设每个单元文件中唯一更改的是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在这种情况下效果最好。


哇,systemd很棒。
guettli 2015年

您没有显示如何在启动时自动启动,包括启动哪个。
Craig Hicks

使用Systemd,该enable操作是使单元/服务在引导时启动的原因。
GregL

我可以独立启用/禁用实例吗?
Soumya Kanti,

是的,启用/禁用它们就是您要做的。
GregL

15

这是我想要的一个python示例。将@在该服务名让你开始数处理:

$ 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()

@radek:我不明白两件事:首先,%i仅用于单元文件的描述。启动命令如何知道要开始什么?其次,如何systemctl some-worker (start|restart|stop)知道要在哪些实例上工作?
U. Windl

%i是@以服务文件名的输出。第二部分已经在答案中说明,请参阅Now you can start/stop then in various ways
radtek

我认为如果不涉及脚本,他的答案是不完整的。大多数“魔术”是在缺少的脚本内完成的。
U. Windl

实际上,我在这里提供了完整的工作解决方案。您指的是哪些“脚本”?/path/to/my/script.py可以是您想要的任何内容,如果需要,可以是“ hello world”。它将一直运行直到收到终止信号。请注意,该问题并非特定于python。
radtek

哇,让您一次开始多个?令人
震惊

1

GregL的回答对我很有帮助。这是我在代码中使用的单位模板的示例,上面的示例用于Gearman作业服务器。我制作了一个Shell脚本,使我可以使用一个模板创建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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.