如何编写-systemd-unit-file

参考1

参考2

简介

unit 是 systemd 中的 primary object. 资源是用 unit 文件作为配置文件.

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description="StarStudio DevOps Recruit Web 2024"
After=network.target

[Service]
Type=simple
User=devops
Environment="PATH=/home/devops/.local/bin:$PATH"
WorkingDirectory=/home/devops/DevOpsReceiptSys
ExecStart=/usr/bin/make run
Restart=on-failure

[Install]
WantedBy=multi-user.target

结合 Python 虚拟环境的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=MkDocs for StarStudio DevOps team
After=network.target

[Service]
Type=simple
User=mkdocs
WorkingDirectory=/home/mkdocs/devops/
ExecStart=/home/mkdocs/mkdocs/bin/python3 /home/mkdocs/mkdocs/bin/mkdocs serve
Restart=on-failure
Environment=/home/mkdocs/mkdocs/bin:$PATH

[Install]
WantedBy=multi-user.target

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
systemctl get-default

设置 default target:

1
systemctl set-default multi-user.target

在不重新开机的情况下, 切换当前 target, 这里以, 关掉图形界面, 改为纯文本模式为例:

1
systemctl isolate multi-user.target

Unit file 解析

Unit file 用 sections 来组织, 每一个 section 用 [] 包裹的名称开始.

1
2
3
[Section]
Directive1=value
Directive2=value

General Characteristics of Unit Files

Section 的名称是 well-defined 和 case-sensitive 的.

如果想添加非标准的 section 来让 systemd 以外的程序使用, 需要在 section name 之前添加 a X- prefix.

在 section 之中, 通过 key-value 的格式来定义 unit behavior 和 metadata.

1
2
3
[Section]
Directive1=value
Directive2=value

在一个 override file 中想要 reset 一个 directive, 用空字符串给其赋值:

1
Directive1=

一些 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 unit
  • Wants=: 和 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 用另一个名称来 enable
  • Also=: 允许 unit 作为一个 set 被 enable
  • DefaultInstance=:

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 stopped
  • ExecStopPost=: 在 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost]# cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH Daemon
Wants=sshdgenkeys.service
After=sshdgenkeys.service
After=network.target

[Service]
ExecStart=/usr/bin/sshd -D
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=always

[Install]
WantedBy=multi-user.target
  • [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 的指令, 可以用 ExecStartPreExecStartPost 来设置额外的指令, 但这里不支持许多 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
2
3
4
5
6
7
8
9
10
[Unit]
Description=backup my server timer2

[Timer]
OnCalendar=Sun *-*-* 02:00:00
Persistent=True
Unit=backup.service

[Install]
WantedBy=multi-user.target

然后:

1
2
3
systemctl daemon-reload
systemctl enable backup2.timer --now
systemctl show backup2.timer

[Timer] 的常用条目

  • OnActiveSec, 当 timers.target 启动多久后执行此 unit
  • OnBootSec, 当开机完成多久后执行
  • OnStartupSec, 当 systemd 第一次启动后多久执行
  • OnUnitActiveSec, 在 timer 所管理的 unit 服务最后一次启动后, 隔多久执行一次
  • OnUnitInactiveSec, 在 timer 所管理的 unit 服务最后一次停止后, 隔多久执行一次
  • OnCalendar, 指定实际时间 (非循环) 来启动服务
  • Unit, 一般不需要指明, 只需要按照 sname.serversname.timer 的配对一般即可, 也可以指定是哪一个 service unit
  • Persistent, 当使用 OnCalendar 时, 指定该功能要不要持续进行

OnCalendar 的时间指定格式

基本语法为:

1
weekday YYYY-MM-DD HH:MM:SS

如:

1
Thu 2015-08-13 13:40:00

也可以直接指定时间间隔, 如:

1
2
3
3h
10s 300m
100m 5day

(小单位写前面)

常用单位有:

  • 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

如何编写-systemd-unit-file
http://example.com/2022/10/19/如何编写-systemd-unit-file/
作者
Jie
发布于
2022年10月19日
许可协议