如何编写-systemd-unit-file
简介
unit 是 systemd 中的 primary object. 资源是用 unit 文件作为配置文件.
示例
1 |
|
结合 Python 虚拟环境的示例
1 |
|
Unit Files 的位置
/usr/lib/systemd/system/
默认位于 /usr/lib/systemd/system/
目录下. 软件安装 unit file 就会放到这里. 不该在这个目录下编辑文件, 可以重写文件.
/etc/systemd/system/
最好是在 /etc/systemd/system/
目录下编写, 其拥有最高的优先级.
对于一个 unit, 可以给其创建子目录, 格式为 (如果 unit 叫 example.services
): example.services.d
, 在子目录下编写 .conf
文件即可.
/run/systemd/system/
run-time unit definitions 位于 /run/systemd/system/
其优先级介于 /usr/lib/systemd/system/
和 /etc/systemd/system/
之间.
任何在其中的修改在重启后都会丢失. 其用于在 runtime 动态创建 unit file.
Unit 的类型
参考鸟哥:
有一些 unit file 是用来 trigger 其他的 unit.
target 类型
target 类型的 unit 可以理解用于启用一个操作环境, 如 multi-user.target
会启用纯文本模式, graphical.target
会启用 Graphical Interface. 一个 target 相当于是一堆 service 的集合 (其会启用很多 service, target).
也注意对 target 用 start
, stop
, enable
这些指令没用.
常见的 target 主要有:
graphical.target
, 该 target 包含了multi-user.target
, 用于启用图形界面multi-user.target
, 纯文本模式rescue.target
, 在 root 无法登录时, systemd 在开机时额外增加的临时系统emergency.target
, 在需要 root 登录, 但无法使用rescue.target
时shutdown.target
, 关机流程getty.target
, 设置 tty
获取以及修改当前 target
获取当前 target:
1 |
|
设置 default target:
1 |
|
在不重新开机的情况下, 切换当前 target, 这里以, 关掉图形界面, 改为纯文本模式为例:
1 |
|
Unit file 解析
Unit file 用 sections 来组织, 每一个 section 用 []
包裹的名称开始.
1 |
|
General Characteristics of Unit Files
Section 的名称是 well-defined 和 case-sensitive 的.
如果想添加非标准的 section 来让 systemd
以外的程序使用, 需要在 section name 之前添加 a X-
prefix.
在 section 之中, 通过 key-value 的格式来定义 unit behavior 和 metadata.
1 |
|
在一个 override file 中想要 reset 一个 directive, 用空字符串给其赋值:
1 |
|
一些 boolean 表达:
- 真: 1, yes, on, true
- 假: o, no, off, false
[Unit] Section 的 Directives
第一个 Section 一般是 [Unit]
. 用来定义 unit 的一些 metadata 以及配置其和其他 unit 的关系.
尽管 section 的顺序对于 systemd
来说无关紧要. [Unit]
section 一般还是放在 unit file 的顶部.
一些常见的 directives:
Description=
: 可以用来描述这个 unit 的名称和基本用途Documentation=
: 提供可查阅的 document 的位置, 该值用systemctl status
可以返回, 如:Documentation=man:systemd.special(7)
Requires=
: 列出需要依赖的其他 unit, 这些 unit 在默认情况下会被 started in parallel with the current unitWants=
: 和Requires=
类似, 但没那么严格,systemd
会尝试启动 any units listed here, 如果没找到或启动失败, 当前 unit 仍然可以工作.BindsTo=
: 和Requires=
类似, 但在 其他 unit 终止时, 当前 unit 也会停止Before=
: 当前 unit 在其列出的 unit 启动前启动After=
: 在当前 unit 在其列出的 unit 启动后启动Conflicts=
: 列出的 unit 不能和当前 unit 同时运行, 启动其列出的 unit 会导致当前 unit 停止Condition...=
:Assert...=
:
[Install] Section Directives
Unit file 的最后一个 section 一般是 [Install]
section. 这个 section 是可选的, 用来定义一个 unit 在 enable 之后的行为, enable 即开机自动启动.
因此, 只有 units that can be enabled 才会有这个 section.
这个 section 的 directives:
WantedBy=
: 表明 how a unit should be enabled. 其允许你像Wants=
一样指明一个依赖关系, 当拥有这个 directive 的 unit 被 enable 后, 其会在/etc/systemd/system/
目录下创建一个后缀为.wants
的目录. 之后会创建一个 symbolic link 到当前文件.RequiredBy=
: 和WantedBy=
类似, 但在依赖不存在时仍和开启, 会创建.requires
的目录Alias=
: 允许这个 unit 用另一个名称来 enableAlso=
: 允许 unit 作为一个 set 被 enableDefaultInstance=
:
Unit-specific Section Directives
The [Service] Section
用来提供配置.
最基本的是设置 Type=
, 其有如下选项:
- simple: 设置了之后, service 的主要进程会在 start line 中显示. 当
Type=
和Busname=
未设置,ExecStart
设置了的时候, 其是默认值. - forking: 当 service fork 了一个 child process 是使用. 其告诉
systemd
当 parent exit 的时候程序仍然在运行. - oneshot: 指明进程会 short-lived, 然后
systemd
应该等待进程退出 before continuing on with other units, 其为Type=
和ExecStart=
未设置时的默认值 - dbus: 指明 unit will take a name on the D-Bus bus
- notify: 指明 service will issue a notification when it has finished starting up
- idle:
其他的 directive:
ExecStart=
: 指明开启进程的命令的 full path and arguments, 只能指定一个, 如果命令之前 preceded by a dash-
character, 会使命令在返回非零值时程序不会停止ExecStartPre=
: 在 mian process 启动之前执行, 可多次使用, 同样, 如果命令之前 preceded by a dash-
character, 会使命令在返回非零值时程序不会停止ExecStartPost=
: 之后ExecReload=
: 可选, 指明用来 reload 配置文件的命令ExecStop=
: 用来 stop service 的命令, 如果没有指明, the main process will be killed immediately when the service is stoppedExecStopPost=
: 在 stop command execute 之后执行RestartSec=
: 指明尝试重启前需要等待的时间Restart=
: 可以设置 “always”, “on-success”, “on-failure”, “on-abnormal”, “on-abort”, “on-watchdog” 等TimeoutSec=
: 在你 stop 一个 service 时,systemd
将其标记为 failed 或 forcefully killing 的等待时间.
其他的参考博文即可.
从 Template Unit Files 创建实例
Template and Instance Unit Names
用 @
标识 Template unit files.
如: example@.service
当一个 Instance 是从一个 template 创建的, 其 instance identifier 放在 @
和 unit type 之间: example@instance1.service
, 这里的 instance1
可以在 unit 中以 %I
或 %i
来访问到. 可以用来指定配置文件.
一个 instance file 常常是 template file 的 symbolic link.
systemctl 针对 service 类型的配置示例
以 sshd.service
为例.
1 |
|
[Unit]
section, 包含当前 unit 的解释, 执行服务的依赖有关[Service]
,[Socket]
,[Timer]
,[Mount]
,[Path]
, 不同的 unit type 使用相对应的 section. 与 unit 实际执行的指令及参数有关, 包括命令的路径, 配置文件位置, 重启方式等[Install]
section, 说明此 unit 要安装到哪个 target 之下
设置的条目需要注意:
- 可以重复, 但是后者会取代前面
- 设置布尔值时, 1, yes, true, on 都表示启用, 0, no, false, off 都表示关闭
#
之后的为注释
[Unit] 常用条目
Description
, 使用systemctl list-units
以及systemctl status
时输出的说明Documentation
, 提供进一步的查询, 比如提供一个网址, 或者 man 命令After
, 说明此 unit 最好是哪个 daemon 启动之后才启动 (但并不强制, 如果After
设置的 unit 没有启用, 当前 unit 还是能启用)Before
, 在此 unit 启动之前最好是启用的服务Requires
, 明确要求此 unit 需要在某个 daemon 启动后才能启动 (若设置的 service 没有启动成功, 则此 unit 不会被启动)Wants
, 规定此 unit 启动后最好还要启动什么服务Conflicts
, 代表有冲突的服务, 如果这个服务启动, 则此 unit 不能被启动
[Service] 常用条目
Type
, 说明这个 daemon 的启动方式, 会影响到ExecStart
, 常用值有:simple
, 默认值, 表明 daemon 由ExecStart
接的指令来启动, 启动后常驻于内存forking
, 由ExecStart
启动的程序通过spawns
延伸出子程序来作为此 daemon 的主要服务, 在原生的父程序结束后停止oneshot
, 和simple
类似, 但不会常驻内存dbus
, 和simple
类似, 但必须取得一个D-Bus
名称后才会继续运行idle
, 和simple
类似, 但其会在所有的工作都执行完毕后才会执行
EnvironmentFile
, 指定启动脚本来配置环境ExecStart
, 实际执行此 daemon 的指令, 可以用ExecStartPre
和ExecStartPost
来设置额外的指令, 但这里不支持许多 bash 语法, 如>
,<
,|
等ExecStop
, 与systemctl stop
的执行相关ExecReload
, 与systemctl reload
有关的命令的执行相关Restart
, 当设置Restart=1
时, 则当此 daemon 服务终止后, 会被再次启用RemainAfterExit
, 当设置为RemainAfterExit=1
时, 当这个 daemon 所属的所有程序都终止之后, 会尝试启动TimeoutSec
, 当程序无法顺利 “正常启动或正常结束” 时, 需要等待多久进入 “强制结束” 的时间KillMode
, 其值可以为:process
, 此时终止 daemon, 只会终止ExecStart
后接的程序control-group
, 此时由 daemon 产生的其他程序也会被关闭none
, 表示没有程序会被关闭
RestartSec
, 与Restart
相关, 当服务关闭, 需要重启时, 需要 sleep 的时间
[Install] 常用条目
WantedBy
, 表明此 unit 挂载到哪一个 target unit 之下, 一般都附挂在multi-user.target
下Also
, 当此 unit 被enable
时,Also
后接的 unit 也会被enable
Alias
, 当运行systemctl enable
, 创建的链接别名
systemctl 针对 timer 的配置示例
除了 crond
之外, 还可以使用 systemd
内置的 time, 即 systemd.timer
来处理任务. 使用原因有:
systemd
服务产生的信息都会被 log- 各 timer 可以用
systemd
来管理 - 各 timer 可以和 control group 结合
若想使用 systemd
的 timer, 需要:
- 启用
timer.target
- 有
sname.service
(sname
为自己指定的名称) - 有
sname.timer
的时间启动服务存在
示例:
1 |
|
然后:
1 |
|
[Timer] 的常用条目
OnActiveSec
, 当timers.target
启动多久后执行此 unitOnBootSec
, 当开机完成多久后执行OnStartupSec
, 当systemd
第一次启动后多久执行OnUnitActiveSec
, 在 timer 所管理的 unit 服务最后一次启动后, 隔多久执行一次OnUnitInactiveSec
, 在 timer 所管理的 unit 服务最后一次停止后, 隔多久执行一次OnCalendar
, 指定实际时间 (非循环) 来启动服务Unit
, 一般不需要指明, 只需要按照sname.server
和sname.timer
的配对一般即可, 也可以指定是哪一个 service unitPersistent
, 当使用OnCalendar
时, 指定该功能要不要持续进行
OnCalendar 的时间指定格式
基本语法为:
1 |
|
如:
1 |
|
也可以直接指定时间间隔, 如:
1 |
|
(小单位写前面)
常用单位有:
- us/usec
- ms/msec
- s/sec/second/seconds
- m/min/minute/minutes
- h/hr/hour/hours
- d/day/days
- w/week/weeks
- month/months
- y/year/years
也有口语化的时间单位:
- now
- today
- tomorrow
- hourly
- daily
- weekly
- monthly
- +3h10m
- 2015-08-16