编写Systemd service 配置文件

# man systemd.service

中文参考手册 systemd.unit
https://kinvolk.io/docs/flatcar-container-linux/latest/setup/systemd/environment-variables/

Systemd 单元文件中的以 “#” 开头的行后面的内容会被认为是注释

Systemd 下的布尔值,1、yes、on、true 都是开启,0、no、off、false 都是关闭。

Systemd 下的时间单位默认是秒,所以要用毫秒(ms)分钟(m)等请显式说明。

配置文件存放位置

/usr/lib/systemd/system/ 
/etc/systemd/system

Systemd 单元类型有:

类型描述
TargetA group of units that defines a synchronization point. The synchronization point is used at boot time to start the system in a particular state.
ServiceA unit of this type starts, stops, restarts or reloads a service daemon such as Apache webserver.
SocketA unit of this type activates a service when the service receives incoming traffic on a listening socket.
DeviceA unit of this type implements device-based activation such as a device driver.
MountA unit of this type controls the file-system mount point.
AutomountA unit of this type provides and controls on-demand mounting of file systems.
SwapA unit of this type encapsulates/activates/deactivates swap partition.
PathA unit of this type monitors files/directories and activates/deactivates a service if the specified file or directory is accessed.
TimerA unit of this type activates/deactivates specified service based on a timer or when the set time is elapsed.
SnapshotA unit that creates and saves the current state of all running units. This state can be used to restore the system later.
SliceA group of units that manages system resources such as CPU, and memory.
ScopeA unit that organizes and manages foreign processes.
busnameA unit that controls DBus system.

可以使用systemctl -t help 命令列出所有支持的单元。

依赖关系

  • Requires 这个单元启动了,那么Requires单元也会被启动; Requires单元被停止了,这个单元也会停止,是强依赖关系。Requires并不能控制启动顺序,通常是本单元和Requires单元并行启动的。这个选项不能作为启动顺序保证。
  • RequiresOverridableRequires 类似。只不过是如果这个服务是用户手动启动的,那么 RequiresOverridable 即使启动不成功也不报错。如果不是手动启动的,和Requires一样。
  • Requisite 强势版本的 Requires,Requisite必须已经处于全部成功,否则本单元会马上进入启动失败的状态。且系统不会尝试去启动未成功启动的Requisite单元。
  • Wants:本单元启动后,want单元也会被启动。但是本单元与wants是弱依赖关系。
  • Conflicts 当此单元启动的时候,Conflicts的所有单元都将被停止。
  • OnFailure 当该单元进入失败状态时,将会启动OnFailure中的单元

启动顺序

Before/After 用于强制指定单元之间的先后顺序。且停止顺序与启动顺序正好相反。这两个选项仅用于指定先后顺序,而与 Requires, Wants, BindsTo 这些选项没有任何关系。

启动类型

  • simple 如果设为 simple (当设置了 ExecStart= 、但是没有设置 Type=BusName= 时,这是默认值),那么 ExecStart= 进程就是该服务的主进程,并且 systemd 会认为在创建了该服务的主服务进程之后,该服务就已经启动完成。如果此进程需要为系统中的其他进程提供服务,那么必须在该服务启动之前先建立好通信渠道(例如套接字),这样,在创建主服务进程之后、执行主服务进程之前,即可启动后继单元,从而加快了后继单元的启动速度。这就意味着对于 simple 类型的服务来说, 即使不能成功调用主服务进程(例如 User= 不存在、或者二进制可执行文件不存在),systemctl start也仍然会执行成功。
  • forking 标准 Unix Daemon 使用的启动方式。启动程序后会调用 fork() 函数,把必要的通信频道都设置好之后父进程退出,留下守护精灵的子进程。你要是使用的这种方式,最好也指定下 PIDFILE=,以帮助 systemd 准确可靠的定位该服务的主进程。systemd 将会在父进程退出之后立即开始启动后继单元。
  • oneshot oneshotsimple 类似,不同之处在于,只有在该服务的主服务进程退出之后,systemd 才会认为该服务启动完成,才会开始启动后继单元。此种类型的服务通常需要设置 RemainAfterExit= 选项。当 Type=ExecStart= 都没有设置时, Type=`oneshot` 就是默认值。
  • dbus 这个程序启动时需要获取一块 DBus 空间,所以需要和 BusName= 一起用。只有它成功获得了 DBus 空间,依赖它的程序才会被启动。
  • notify notifyexec 类似,不同之处在于,该服务将会在启动完成之后通过sd_notify(3)之类的接口发送一个通知消息。systemd 将会在启动后继单元之前,首先确保该进程已经成功的发送了这个消息。如果设为此类型,那么下文的 NotifyAccess=将只能设为非 none 值。如果未设置NotifyAccess= 选项、或者已经被明确设为 none ,那么将会被自动强制修改为main 。注意,目前 Type=notify 尚不能与PrivateNetwork=yes 一起使用。
  • idle idlesimple 类似,不同之处在于,服务进程将会被延迟到所有活动任务都完成之后再执行。这样可以避免控制台上的状态信息与shell脚本的输出混杂在一起。注意:(1)仅可用于改善控制台输出,切勿将其用于不同单元之间的排序工具;(2)延迟最多不超过5秒,超时后将无条件的启动服务进程。

定义流程

定义控制单元 [Unit]

[Unit]
Description= 描述这个单元

定义服务体 [Service]

[Service]
Type= 启动类型

安装服务 [Install]

[Install]
WantedBy=multi-user.target 

Alias=给你自己的别名,这样 systemctl command xxx.service 的时候就可以不输入完整的单元名称。比如你给 NetworkManager 一个别名叫 Alias=nm,那你就可以 systemctl status nm.service 查看实际是 NetworkManager.service 的服务了。

Also=安装本服务的时候还要安装别的什么服务。比如我们的 He.net 脚本按理应该需要一个 iproute2.service 作为 also,但是 iproute2 实际上不需要 systemd 控制,所以就没写。它和 [Unit] 定义里面的依赖关系相比,它管理的不是运行时依赖,而是安装时。安装好了之后启动谁先谁后,谁依赖谁,和 Also= 都没有关系。

示例

Network Manager

[Unit]
Description=Network Manager
Documentation=man:NetworkManager(8)
Wants=network.target
After=network-pre.target dbus.service
Before=network.target 

[Service]
Type=dbus
BusName=org.freedesktop.NetworkManager
ExecReload=/usr/bin/busctl call org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.NetworkManager Reload u 0
#ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/sbin/NetworkManager --no-daemon
Restart=on-failure
# NM doesn't want systemd to kill its children for it
KillMode=process
CapabilityBoundingSet=CAP_NET_ADMIN CAP_DAC_OVERRIDE CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_MODULE CAP_AUDIT_WRITE CAP_KILL CAP_SYS_CHROOT

ProtectSystem=true
ProtectHome=read-only

[Install]
WantedBy=multi-user.target
Also=NetworkManager-dispatcher.service

# We want to enable NetworkManager-wait-online.service whenever this service
# is enabled. NetworkManager-wait-online.service has
# WantedBy=network-online.target, so enabling it only has an effect if
# network-online.target itself is enabled or pulled in by some other unit.
Also=NetworkManager-wait-online.service