如何编写 systemd 的 .service 文件

一、背景知识

systemd 是用来替换老的 sysvinit 启动脚本的,它有很多优点。.service 文件是它的本地配置文件,类似于 sysvinit 中的 /etc/init.d 里脚本的作用。.service 文件和桌面文件 (.desktop)或 Windows 的 ini 文件有一样的结构。systemd 的手册可以点这里访问。

二、一个简单的例子

下面是一个简单的 .service 文件的例子,它会使用参数启动一个 VDE 程序。把下面的内容保存成 /usr/lib/systemd/system/vde.service(注意:/etc/systemd/system的文件优先权更高

 

[Unit]
Description=Virtual Distributed Ethernet

[Service]
ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \
 --dirmode 0750 --group qemu

[Install]
WantedBy=multi-user.target

 

注意,你可以看到它和传统的 init 脚本之间的差异,为了简单起见,vde_switch 并没有启动为守护进程,实际,systemd 也可以启动成真正的守护进程,你只需要把 vde.service 改成下面的样子。

[Unit]
Description=Virtual Distributed Ethernet

[Service]
Type=forking
# The PID file is optional, but recommended in the manpage
# "so that systemd can identify the main process of the daemon"
PIDFile=/var/run/vde.pid
ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \
 --dirmode 0750 --group qemu \
 --daemon --pidfile /var/run/vde.pid

[Install]
WantedBy=multi-user.target

对于这两种方式,如果只是单独的 vde ,基本没有区别,他们的主要区别在于会影响 systemd 处理依赖关系的方式。如果其它一些服务依赖于 vde,那么第一种方式下,systemd 会在 vde 服务启动时候尽快的运行这些服务,第二种方式下,systemd 会等待 vde 服务启动完成,因为 vde 服务会建立 control socket。而第一种方式下,有可能在 vde 的 control socket 没有建立的时候就连接它这个服务。

三、自动重启动

我们还可以添加适当的依赖关系,比如让 vde 在 syslog 服务后启动,还可以告诉 systemd 如果 vde 崩溃了,那么重新启动它。

[Unit]
Description=Virtual Distributed Ethernet
After=syslog.target

[Service]
Type=forking
PIDFile=/var/run/vde.pid
ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \
 --dirmode 0750 --group qemu \
 --daemon --pidfile /var/run/vde.pid
Restart=on-abort

[Install]
WantedBy=multi-user.target

现在试试启动 vde 服务,然后让它崩溃,就会有下面的结果。

home ~ # systemctl start vde.service
home ~ # systemctl status vde.service
vde.service - Virtual Distributed Ethernet
   Loaded: loaded (/etc/systemd/system/vde.service)
   Active: active (running) since Tue, 04 Jan 2011 22:08:10 +0500;
15s ago
  Process: 31434 (/usr/bin/vde_switch --tap tap0...,
code=exited, status=0/SUCCESS)
 Main PID: 31435 (vde_switch)
   CGroup: name=systemd:/system/vde.service
    └ 31435 /usr/bin/vde_switch --tap tap0...
home ~ # kill -SEGV 31435
home ~ # systemctl status vde.service
vde.service - Virtual Distributed Ethernet
   Loaded: loaded (/etc/systemd/system/vde.service)
   Active: failed since Tue, 04 Jan 2011 22:11:27 +0500;
4s ago
  Process: 31503 (/usr/bin/vde_switch --tap tap0...,
code=exited, status=0/SUCCESS)
 Main PID: 31504 (code=exited, status=1/FAILURE)
   CGroup: name=systemd:/system/vde.service

可以看到,重启动虽然开始了,但是没有把 vde 服务启动起来,系统日志会告诉我们原因。

Jan 4 22:11:27 home vde_switch[31504]: Error in pidfile creation: File exists

pid 文件仍然存在,所以无法启动 vde。有两个办法解决它,要么告诉 systemd 运行之前先删掉原来的 PID 文件,要么放弃使用 PID 文件。第一种的 .service 文件写法如下:
[Unit]
Description=Virtual Distributed Ethernet
After=syslog.target

[Service]
Type=forking
PIDFile=/var/run/vde.pid
# Note the -f: don't fail if there is no PID file
ExecStartPre=/bin/rm -f /var/run/vde.pid
ExecStart=/usr/bin/vde_switch --tap tap0 --mode 0660 \
 --dirmode 0750 --group qemu \
 --daemon --pidfile /var/run/vde.pid
Restart=on-abort

[Install]
WantedBy=multi-user.target

第二种的不写了。

四、配置文件

许多启动脚本有配置文件,以便用户可以定制他们自己的启动参数。举个例子,一些用户可以给 vde_switch 传递 –hub 参数,其它人可能想启用管理 socket。在 Gentoo 中的传统脚本配置是这样的(Magic 的老脚本也是一样):

# load the tun module
VDE_MODPROBE_TUN="yes"
# virtual tap networking device to be used for vde
VDE_TAP="tap0"
# mode and group for the socket
VDE_SOCK_CHMOD="770"
VDE_SOCK_CHOWN=":qemu"

# This is the actual options string passed to VDE.
# Change this at your own risk.
VDE_OPTS="--tap ${VDE_TAP} -daemon"

systemd 通常使用一个键值 EnvironmentFile 来提供一个配置文件,让用户可以按自己的需要定制服务。

传统的脚本是 source 配置文件,因此,任何/bin/sh支持的语法都可以用在配置文件中。所以请尽量不要在传统的脚本和 systemd 的配置中使用同样的配置文件(这个对 Magic 来说基本不是问题)。因为 systemd 和传统脚本的处理方法是不同的。当然,上面的文件是没办法直接在 systemd 中使用的,因为 systemd 的配置文件不支持变量插值的。假如我们举一个例子,在 /etc/conf.d/vde2 中写下如下的内容:

VDE_OPTS="--tap tap0 --mode 0660 --dirmode 0750 --group qemu"

然后修改 vde.service 为下面的样子:

[Unit]
Description=Virtual Distributed Ethernet
After=syslog.target

[Service]
Type=forking
EnvironmentFile=/etc/conf.d/vde2
ExecStart=/usr/bin/vde_switch --daemon $VDE_OPTS
Restart=on-abort

[Install]
WantedBy=multi-user.target

运行以后,会得不到想要的结果,因为 systemd 会把双引号内的部分做为一个参数传递,所以,在systemd的配置文件中,变量就无需使用双引号,像下面这样就行了:

VDE_OPTS=–tap tap0 –mode 0660 –dirmode 0750 –group qemu

这和 bash是不同的。

注:这篇文章基本上是翻译的这里的文章,感谢原作者。

发表评论