Linux systemd 服务详解
Linux 系统中也有“服务”这一说法, 也即 systemd service unit,通过服务我们可以便捷地管理一些程序功能,也可以作为程序开机自启的一个手段之一。今天我来分享一下如何创建自己简易的 Linux 服务。本文主要讲述如何在 Linux 下自定义 service.
1. 服务文件位置介绍
/usr/lib/systemd/system/
随安装的 RPM 软件包分发的系统单元文件。该目录中包含的是软件包安装的单元,也就是说通过 yum、dnf、rpm 等软件包管理命令管理的 systemd 单元文件,都放置在该目录下。
/run/systemd/system/
这个目录一般是进程在运行时动态创建 unit 文件的目录,一般很少修改,除非是修改程序运行时的一些参数时,即 Session 级别的,才在这里做修改。
/etc/systemd/system/
Systemd 创建的单元文件 systemctl enable 以及为扩展服务而添加的单元文件。此目录优先于包含运行时单元文件的目录。
1.1. 三个文件位置的关系
包管理软件初始安装的包中,如果包含 systemd 单元文件也即服务定义文件,一般会放在/usr/lib/systemd/system/。
如果要开启该服务,会在/etc/systemd/system/目录创建一个软链接指向/usr/lib/systemd/system/中的服务定义文件。
其中/etc/systemd/system/又分为 basic.target.wants, graphical.target.wants, multi-user.target.wants 等子目录,具体服务链接至哪个子目录是由服务定义文件中的
Install 部分的 WantedBy 属性所决定的。
/run/systemd/system/则是放置程序运行时动态创建 unit 文件。
例如:
1 | $ sudo systemctl enable clamd@scan.service |
2. 新建服务文件
每一个服务在 Linux 有它自己的对应的配置文件,这个文件可以通过文本编辑器编辑,扩展名为 xxx.servive(xxx 为服务名称)。这些文件位于/usr/lib/systemd/system 目录下。
在这个目录下新建 service 文件即可创建我们的服务。文件的内容结构如下:
1 | [Unit] |
服务配置文件分为 Unit、Service 和 Install 三大部分。
Unit 部分:提供对服务的描述和定义服务的依赖关系。
Service 部分:服务(Service)类型的 Unit 特有的,用于定义服务的具体管理和操作方法
Install 部分:这个部分指定了服务在 Systemd 系统中的安装设置。
2.1. Unit 部分介绍
- Description:描述这个 Unit 文件的信息
- Documentation:指定服务的文档,可以是一个或多个文档的 URL 路径
- Requires:依赖的其它 Unit 列表,列在其中的 Unit 模板会在这个服务启动时的同时被启动。并且,如果其中任意一个服务启动失败,这个服务也会被终止
- Wants:与 Requires 相似,但只是在被配置的这个 Unit 启动时,触发启动列出的每个 Unit 模块,而不去考虑这些模板启动是否成功
- After:与 Requires 相似,但是在后面列出的所有模块全部启动完成以后,才会启动当前的服务
- Before:与 After 相反,在启动指定的任务一个模块之间,都会首先确证当前服务已经运行
- Binds To:与 Requires 相似,失败时失败,成功时成功,但是在这些模板中有任意一个出现意外结束或重启时,这个服务也会跟着终止或重启
- Part Of:一个 Bind To 作用的子集,仅在列出的任务模块失败或重启时,终止或重启当前服务,而不会随列出模板的启动而启动
- OnFailure:当这个模板启动失败时,就会自动启动列出的每个模块
- Conflicts:与这个模块有冲突的模块,如果列出的模块中有已经在运行的,这个服务就不能启动,反之亦然
2.2. Service 部分介绍
服务生命周期控制相关
Type:定义启动时的进程行为,它有以下几种值:
Type=simple:默认值,执行 ExecStart 指定的命令,启动主进程
Type=forking:以 fork 方式从父进程创建子进程,创建后父进程会立即退出
Type=oneshot:一次性进程,Systemd 会等当前服务退出,再继续往下执行
Type=dbus:当前服务通过 D-Bus 启动
Type=notify:当前服务启动完毕,会通知 Systemd,再继续往下执行
Type=idle:若有其他任务执行完毕,当前服务才会运行
RemainAfterExit:值为 true 或 false(默认)。当配置为 true 时,Systemd 只会负责启动服务进程,之后即便服务进程退出了,Systemd 也仍然会认为这个服务还在运行中。这个配置主要是提供给一些并非常驻内存,而是启动注册后立即退出,然后等待消息按需启动的特殊类型服务使用的。
ExecStart:启动当前服务的命令
ExecStartPre:启动当前服务之前执行的命令
ExecStartPos:启动当前服务之后执行的命令
ExecReload:重启当前服务时执行的命令
ExecStop:停止当前服务时执行的命令
ExecStopPost:停止当其服务之后执行的命令
RestartSec:自动重启当前服务间隔的秒数
Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括 always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog
TimeoutStartSec:启动服务时等待的秒数,这一配置对于使用 Docker 容器而言显得尤为重要,因其第一次运行时可能需要下载镜像,严重延时会容易被 Systemd 误判为启动失败杀死。通常,对于这种服务,将此值指定为 0,从而关闭超时检测
TimeoutStopSec:停止服务时的等待秒数,如果超过这个时间仍然没有停止,Systemd 会使用 SIGKILL 信号强行杀死服务的进程
服务上下文配置相关
Environment:为服务指定环境变量
EnvironmentFile:指定加载一个包含服务所需的环境变量的列表的文件,文件中的每一行都是一个环境变量的定义
Nice:服务的进程优先级,值越小优先级越高,默认为 0。其中 -20 为最高优先级,19 为最低优先级
WorkingDirectory:指定服务的工作目录
RootDirectory:指定服务进程的根目录(/ 目录)。如果配置了这个参数,服务将无法访问指定目录以外的任何文件
User:指定运行服务的用户
Group:指定运行服务的用户组
MountFlags:服务的 Mount Namespace 配置,会影响进程上下文中挂载点的信息,即服务是否会继承主机上已有挂载点,以及如果服务运行执行了挂载或卸载设备的操作,是否会真实地在主机上产生效果。可选值为 shared、slaved 或 private
shared:服务与主机共用一个 Mount Namespace,继承主机挂载点,且服务挂载或卸载设备会真实地反映到主机上
slave:服务使用独立的 Mount Namespace,它会继承主机挂载点,但服务对挂载点的操作只有在自己的 Namespace 内生效,不会反映到主机上
private:服务使用独立的 Mount Namespace,它在启动时没有任何任何挂载点,服务对挂载点的操作也不会反映到主机上
LimitCPU / LimitSTACK / LimitNOFILE / LimitNPROC 等:限制特定服务的系统资源量,例如 CPU、程序堆栈、文件句柄数量、子进程数量等
注意:如果在 ExecStart、ExecStop 等属性中使用了 Linux 命令,则必须要写出完整的绝对路径。对于 ExecStartPre 和 ExecStartPost 辅助命令,若前面有个 “-” 符号,表示忽略这些命令的出错。因为有些 “辅助” 命令本来就不一定成功,比如尝试清空一个文件,但文件可能不存在。
2.3. Install 部分介绍
在理解 Install 部分前我们需要向了解两个基本概念:
Unit 和 Target
Unit 是 Systemd 管理系统资源的基本单元,可以认为每个系统资源就是一个 Unit,并使用一个 Unit 文件定义。在 Unit 文件中需要包含相应服务的描述、属性以及需要运行的命令。
Target 是 Systemd 中用于指定系统资源启动组的方式,相当于 SysV-init 中的运行级别。
简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于”状态点”,启动某个 Target 就好比启动到某种状态。
2.3.1. Install 属性详解
这部分配置的目标模块通常是特定运行目标的 .target 文件,用来使得服务在系统启动时自动运行。这个区段可以包含三种启动约束:
- WantedBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target,当前 Unit 激活时(enable)符号链接会放入 /etc/systemd/system 目录下面以 <Target 名> + .wants 后缀构成的子目录中,如 “/etc/systemd/system/multi-user.target.wants/“
- RequiredBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target,当前 Unit 激活时,符号链接会放入 /etc/systemd/system 目录下面以 <Target 名> + .required 后缀构成的子目录中
- Also:当前 Unit enable/disable 时,同时 enable/disable 的其他 Unit
- Alias:当前 Unit 可用于启动的别名
3. Systemctl 命令
service 管理
查看当前系统的所有 Unit
1 |
|
查看 service 的状态
1 | # 显示系统状态 |
Unit 的管理
1 |
|
查看 Unit 的依赖关系
1 |
|
Target 管理
Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。
在传统的 SysV-init 启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动。
1 |
|
日志管理
Systemd 通过其标准日志服务 Journald 提供的配套程序 journalctl 将其管理的所有后台进程打印到 std:out(即控制台)的输出重定向到了日志文件。
Systemd 的日志文件是二进制格式的,必须使用 Journald 提供的 journalctl 来查看,默认不带任何参数时会输出系统和所有后台进程的混合日志。
默认日志最大限制为所在文件系统容量的 10%,可以修改 /etc/systemd/journald.conf 中的 SystemMaxUse 来指定该最大限制。
1 |
|
4. 参考文档
Linux systemd 服务详解
https://pengtech.net/linux/linux_systemd_service_explanation.html