Systemctl之systemd自定义系统服务

警告
本文最后更新于 2023-01-30,文中内容可能已过时。

1. [Unit]

Unit 定义启动顺序与依赖关系

单元(Unit)是 Systemd 的最小功能单位,是单个进程的描述。一个个小的单元互相调用和依赖,组成一个庞大的任务管理系统

其他的单元类型

https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files
Systemd 根据他们描述的资源类型对单位进行分类。确定单元类型的最简单方法是使用其类型后缀,该后缀附加到资源名称的末尾。
以下列表描述了可用于以下各项的单位类型systemd:

  • .service: 服务单元描述如何管理服务器上的服务或应用程序。这将包括如何启动或停止服务,应在何种情况下自动启动服务,以及相关软件的依赖关系和订购信息。
  • .socket: 套接字单元文件描述网络或IPC套接字,或systemd用于基于套接字的激活的FIFO缓冲区。这些.service文件始终具有一个关联文件,该文件将在本单元定义的套接字上看到活动时启动。
  • .device: 描述已被指定为需要systemd管理的设备udevsysfs文件系统的单元。并非所有设备都有.device文件。.device可能需要单元的一些场景是用于订购,安装和访问设备。
  • .mount: 此单元定义要由其管理的系统上的挂载点systemd。这些以安装路径命名,斜杠更改为破折号。其中的条目/etc/fstab可以自动创建单位。
  • .automount: 一个.automount单元配置将自动挂载的挂载点。这些必须以它们引用的挂载点命名,并且必须具有匹配.mount单元以定义挂载的细节。
  • .swap: 此单元描述系统上的交换空间。这些单元的名称必须反映空间的设备或文件路径。
  • .target: 目标单元用于在启动或更改状态时为其他单元提供同步点。它们还可用于使系统进入新状态。其他单位指定它们与目标的关系以与目标的操作联系起来。
  • .path: 此单元定义可用于基于路径的激活的路径。默认情况下,.service当路径达到指定状态时,将启动相同基本名称的单元。这用于inotify监视更改的路径。
  • .timer: .timer单元定义将由其管理的计时器systemd,类似于cron延迟或计划激活的作业。达到计时器时将启动匹配单元。
  • .snapshot: 命令.snapshot自动创建一个单元systemctl snapshot。它允许您在进行更改后重建系统的当前状态。快照不会跨会话生存,并用于回滚临时状态。
  • .slice: .slice单元与Linux控制组节点关联,允许限制资源或将资源分配给与该片关联的任何进程。该名称反映了它在cgroup树中的层次结构位置。默认情况下,单位会根据其类型放置在某些切片中。
  • .scope: 范围单元systemd由从其总线接口接收的信息自动创建。这些用于管理外部创建的系统进程集。
1
2
3
4
5
6
Description=当前服务的描述  
Documentation=给出文档的位置,一般就是服务启动命令的帮助文档  
After=表示如果此字段标记的服务若需要启动,那么当前定义的服务需要在此标记服务器启动之后.  
Before=表示如果此字段标记的服务若需要启动,那么当前定义的服务需要在此标记服务器启动之前.  
Wants=表示此字段标记的服务与当前定义服务存在"弱依赖"关系,即表示当前定义的节点服务启动失败或者停止运行,不影响当前定义的服务继续执行.  
Requires=表示此字段标记的服务与当前定义服务存在"强依赖"关系,即表示当前定义的节点服务启动失败或者停止运行,那么当前定义的服务也必须停止.  

2. [Service]

Service区块定义如何启动当前服务。
注意:[Service]部分的启动、重启、停止命令全部要求使用绝对路径,使用相对路径则会报错!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Environment=指定当前服务运行的环境参数,该值使用key=value健值对
;Environment=LANG=C 
EnvironmentFile=指定当前服务的环境参数文件。该文件内部的key=value键值对,可以用$key的形式,在当前配置文件中获取。 
Type=字段定义服务启动类型
;Type=simple(默认值): ExecStart字段启动的进程为主进程
;Type=exec: 类似于simple,simple表示当fork()函数返回时,即表示启动完成,而exec则表示仅在fork()和execve()函数都执行成功时,才算启动完成.
;Type=forking:ExecStart字段将以fork()方式启动,此时父进程将会退出,子进程将成为主进程
;Type=oneshot:类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务
;Type=dbus:类似于simple,但会等待 D-Bus 信号后启动
;Type=notify:类似于simple,启动结束后会发出通知信号,然后 Systemd 再启动其他服务
;Type=idle:类似于simple,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合
TimeoutStopSec=停止服务时的等待的秒数,如果超过这个时间服务仍然没有停止,systemd 会使用 SIGKILL 信号强行杀死服务的进程。 
TimeoutStartSec=启动服务时的等待的秒数,如果超过这个时间服务任然没有执行完所有的启动命令,则 systemd 会认为服务自动失败。 
ExecStart=启动服务时执行的命令
ExecReload=重启服务时执行的命令
ExecStop=停止服务时执行的命令
ExecStartPre=启动服务之前执行的命令
ExecStartPost=启动服务之后执行的命令
ExecStopPost=停止服务之后执行的命令
User=指定运行服务的用户,会影响服务对本地文件系统的访问权限。
Group=指定运行服务的用户组,会影响服务对本地文件系统的访问权限。
RootDirectory=指定服务进程的根目录(默认: / ),如果配置了这个参数后,服务将无法访问指定目录以外的任何文件。
Nice=服务的进程优先级,值越小优先级越高,默认为0。-20为最高优先级,19为最低优先级 
KillMode=表示systemd如何停止当前定义服务
;KillMode=control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
;KillMode=process:只杀主进程
;KillMode=mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
;KillMode=none:没有进程会被杀掉,只是执行服务的 stop 命令。
Restart=定义了当前定义服务退出后,Systemd的重启方式。
;Restart=no(默认值):退出后不会重启
;Restart=on-success:只有正常退出时(退出状态码为0),才会重启
;Restart=on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启
;Restart=on-abnormal:只有被信号终止和超时,才会重启
;Restart=on-abort:只有在收到没有捕捉到的信号终止时,才会重启
;Restart=on-watchdog:超时退出,才会重启
;Restart=always:不管是什么退出原因,总是重启
RestartSec=12s 表示 Systemd 重启服务之前,需要等待的秒数。

3. [Install]

Install区块,定义如何安装这个配置文件,即怎样做到开机启动。

1
2
WantedBy=表示该服务所在的Target
; Target的含义是服务组,表示一组服务,一般来说,常用的 Target 有两个:一个是multi-user.target,表示多用户命令行状态;另一个是graphical.target,表示图形用户状态,它依赖于multi-user.target 

4. systemd 的定时任务(.timer)

所谓定时任务,就是未来的某个或多个时点,预定要执行的任务,比如每五分钟收一次邮件、每天半夜两点分析一下日志等等。
Linux 系统通常都使用 cron 设置定时任务,但是 Systemd 也有这个功能,而且优点显著

  • 自动生成日志,配合 Systemd 的日志工具,很方便除错
  • 可以设置内存和 CPU 的使用额度,比如最多使用50%的 CPU
  • 任务可以拆分,依赖其他 Systemd 单元,完成非常复杂的任务

每个单元都有一个单元描述文件,它们分散在三个目录。

  • /lib/systemd/system:系统默认的单元文件
  • /etc/systemd/system:用户安装的软件的单元文件
  • /usr/lib/systemd/system:用户自己定义的单元文件

systemd 定时任务分为两个部分

  1. 任务执行部分.service
    • 用于定义如何执行该任务,无需配置如何安装(即定义Install)
  2. 定时执行部分.timer

5. systemd 常用相关命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 重启系统
$> sudo systemctl reboot

# 关闭系统,切断电源
$> sudo systemctl poweroff

# CPU停止工作
$> sudo systemctl halt

# 暂停系统
$> sudo systemctl suspend

# 让系统进入冬眠状态
$> sudo systemctl hibernate

# 让系统进入交互式休眠状态
$> sudo systemctl hybrid-sleep

# 启动进入救援状态(单用户状态)
$> sudo systemctl rescue
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看启动耗时
$> systemd-analyze

# 查看每个服务的启动耗时
$> systemd-analyze blame

# 显示瀑布状的启动过程流
$> systemd-analyze critical-chain

# 显示指定服务的启动流
$> systemd-analyze critical-chain atd.service
1
2
3
4
5
6
7
8
# 列出当前session
$> loginctl list-sessions

# 列出当前登录用户
$> loginctl list-users

# 列出显示指定用户的信息
$> loginctl show-user ruanyf
1
2
3
4
5
6
7
8
# 显示某个 Unit 是否正在运行
$> systemctl is-active application.service

# 显示某个 Unit 是否处于启动失败状态
$> systemctl is-failed application.service

# 显示某个 Unit 服务是否建立了启动链接
$> systemctl is-enabled application.service
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# 查看所有日志(默认情况下 ,只保存本次启动的日志)
$> sudo journalctl

# 查看内核日志(不显示应用日志)
$> sudo journalctl -k

# 查看系统本次启动的日志
$> sudo journalctl -b
$> sudo journalctl -b -0

# 查看上一次启动的日志(需更改设置)
$> sudo journalctl -b -1

# 查看指定时间的日志
$> sudo journalctl --since="2012-10-30 18:17:16"
$> sudo journalctl --since "20 min ago"
$> sudo journalctl --since yesterday
$> sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00"
$> sudo journalctl --since 09:00 --until "1 hour ago"

# 显示尾部的最新10行日志
$> sudo journalctl -n

# 显示尾部指定行数的日志
$> sudo journalctl -n 20

# 实时滚动显示最新日志
$> sudo journalctl -f

# 查看指定服务的日志
$> sudo journalctl /usr/lib/systemd/systemd

# 查看指定进程的日志
$> sudo journalctl _PID=1

# 查看某个路径的脚本的日志
$> sudo journalctl /usr/bin/bash

# 查看指定用户的日志
$> sudo journalctl _UID=33 --since today

# 查看某个 Unit 的日志
$> sudo journalctl -u nginx.service
$> sudo journalctl -u nginx.service --since today

# 实时滚动显示某个 Unit 的最新日志
$> sudo journalctl -u nginx.service -f

# 合并显示多个 Unit 的日志
$> journalctl -u nginx.service -u php-fpm.service --since today

# 查看指定优先级(及其以上级别)的日志,共有8级
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
$> sudo journalctl -p err -b

# 日志默认分页输出,--no-pager 改为正常的标准输出
$> sudo journalctl --no-pager

# 以 JSON 格式(单行)输出
$> sudo journalctl -b -u nginx.service -o json

# 以 JSON 格式(多行)输出,可读性更好
$> sudo journalctl -b -u nginx.service -o json-pretty

# 显示日志占据的硬盘空间
$> sudo journalctl --disk-usage

# 指定日志文件占据的最大空间
$> sudo journalctl --vacuum-size=1G

# 指定日志文件保存多久
$> sudo journalctl --vacuum-time=1years

  1. 对于[Timer]节点, Systemd 提供以下一些字段。
    • OnActiveSec:定时器生效后,多少时间开始执行任务
    • OnBootSec:系统启动后,多少时间开始执行任务
    • OnStartupSec``:Systemd 进程启动后,多少时间开始执行任务
    • OnUnitActiveSec:该单元上次执行后,等多少时间再次执行(s/h)
    • OnUnitInactiveSec: 定时器上次关闭后多少时间,再次执行
    • OnCalendar:基于绝对时间,而不是相对时间执行
    • AccuracySec:如果因为各种原因,任务必须推迟执行,推迟的最大秒数,默认是60秒
    • Unit:真正要执行的任务,默认是同名的带有.service后缀的单元
    • Persistent:如果设置了该字段,即使定时器到时没有启动,也会自动执行相应的单元
    • WakeSystem:如果系统休眠,是否自动唤醒系统