如何编写 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是不同的。
注:这篇文章基本上是翻译的这里的文章,感谢原作者。