使用 Systemd 管理服务

内容纲要

查看【Linux】专题可浏览更多内容

相关文章

systemd 提供了一个被称为「单元」的各种实体之间的依赖系统,有 11 种不同类型。

单元封装了与系统启动和维护相关的各种对象。大多数单元是在单元配置文件中配置的,但有些单元是由其他配置自动创建的,或由系统状态动态创建,或在运行时以编程方式创建。

类型描述
Service (.service)用于启动和控制守护进程以及由其组成的进程
Socket (.socket)在系统中封装本地 IPC 或网络 sockets,对 socket-based 的激活非常有用
Target (.target)对于组合单元非常有用,或者在启动过程中提供已知的同步点
Device (.device)在 systemd 中公开内核设备,可用于实现基于设备的激活
Mount (.mount)控制文件系统的挂载点
Automount (.automount)提供自动挂载功能,用于按需挂载文件系统以及并行启动
Timer (.timer)用于触发基于定时器的其他单元的激活
Swap (.swap)与 Mount 单元非常相似,封装了操作系统的内存交换分区或文件
Path (.path)当文件系统对象发生变化或被修改时,Path 单元可以用来激活其他服务
Slice (.slice)用来将管理系统进程的单元 (如 Service 和 Scope 单元) 以分层树的形式分组,以达到资源管理的目的
Scope (.scope)与 Service 单元类似,但也是管理外来进程而不是启动它们

列出单元

# 列出所有处于活动的单元
systemctl list-units

# 列出 systemd 已经加载或试图加载到内存中的所有单元,包括那些当前不活动的单元
systemctl list-units --all

# 列出系统上安装的所有单元,包括 systemd 没有试图加载到内存中的单元
systemctl list-unit-files

# 使用 --state 选项查看指定状态的单元,如未活动的单元:
systemctl list-units --all --state=inactive

# 查看置顶类型的单元,如服务单元
systemctl list-units --type=service

管理服务状态

对于服务管理任务,目标单元将是 Service 单元,其单元文件的后缀是 .service。然而对于大多数服务管理命令,实际上可以省去 .service 的后缀。

# 查看服务状态
# systemctl status app.service
# 可以省略 .service,以下命令都以 NGINX 服务为例
systemctl status nginx

# 启动服务
sudo systemctl start nginx
# 停止服务
sudo systemctl stop nginx
# 重启服务
sudo systemctl restart nginx
# 重载配置文件
sudo systemctl reload nginx
# 尝试重载若不成功则重启
sudo systemctl reload-or-restart nginx

# 开启开机启动及关闭
sudo systemctl enable nginx
sudo systemctl disable nginx

# 检查状态
# 判断服务是否处于激活、失败或开机启动状态
systemctl is-active nginx
systemctl is-failed nginx
systemctl is-enabled nginx

# 禁用或解除禁用,当禁用时将完全无法启动,无论是自动还是手动启动
sudo systemctl mask nginx
sudo systemctl unmask nginx

检查单元文件相关

# 显示单元文件
systemctl cat nginx.service

# 显示依赖项
systemctl list-dependencies nginx.service

# 显示单元设置的底层细节
systemctl show nginx.service

单元文件位置

定义 systemd 如何处理一个单元的文件可以在很多不同的地方找到,每一个地方都有不同的优先级和影响。

单元文件位置:

位置描述
/usr/lib/systemd/system/与软件安装包一起分发的 systemd 单元文件
/run/systemd/system/在运行时创建的系统单元文件
/etc/systemd/system/由 systemctl 启用创建,还包含用户手动创建的单元文件

系统的单元文件副本一般保存在 /lib/systemd/system 目录下。当软件在系统中安装单元文件时,这里是默认放置单元文件的位置。这里存储的单元文件能够在会话期间根据需要启动和停止。这将是通用的、普通的单元文件,通常由上游项目的维护者编写,应该可以在任何以标准实现方式部署 systemd 的系统上使用。不应该编辑这个目录下的文件,而是使用其他单元文件的位置来覆盖以取代该位置的文件。

运行时单元定义的位置在 /run/systemd/system,该目录下的单元文件的优先级介于 /etc/systemd/system/lib/systemd/system 之间。这个位置的文件比前者的权重低,但比后者的权重高。systemd 进程本身使用这个位置来存放运行时动态创建的单元文件。这个目录可以用来改变系统在会话期间的单元行为。当服务器重启时,在该目录下所做的所有更改都会丢失。

编辑单元文件

如果想修改某个单元的功能,最佳位置是在 /etc/systemd/system 目录中。在此目录位置中找到的单元文件优先于文件系统中的任何其他位置。如果需要修改系统中某个单元文件的副本,在这个目录下放置一个替换文件是最安全、最灵活的方法。

如果希望只覆盖系统单元文件中的特定指令,实际上可以在一个子目录中提供单元文件片段。它们将附加或修改系统副本的指令,允许仅指定要更改的选项。正确的做法是创建一个以单元文件命名的目录,并在后面加上 .d,如 nginx.service 创建名为 nginx.service.d 的子目录,在里面创建以 .conf 的文件用以覆盖或扩展系统的单元文件的属性。

通过 systemctl edit 命令可以帮助做到这些:

sudo systemctl edit nginx.service

# 编辑完整的单元文件而不是代码段
sudo systemctl edit --full nginx.service

在修改单元文件后,应该重载 systemd:

sudo systemctl daemon-reload

新建单元文件

一个单元的文件的组成结构:

  • [Unit]
    包含不依赖于单元类型的选项。选项提供单元描述,设置对其他单元的依赖关系,并指定单元的行为。
  • [Service]
    有关特定服务类型的信息
  • [Install]
    有关 systemctl 启用和禁用命令所使用的安装的信息

[Unit]

[Unit]
Description=The NGINX HTTP and reverse proxy server
Documentation=https://nginx.org/en/docs/
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
  • Description:单元文件的描述,这是将显示在 systemctl status 命令的输出中的文本;
  • Documentation:提供单元文档的 URL
  • After/Before:这定义了单元启动的顺序。这意味着该单元只能在另一个依赖单元启动之后/之前启动;
  • Requires:这将配置对单元文件的依赖关系,使用此选项指定的单元同时启动;
  • Wants:配置较小数量级的依赖项,这意味着即使指定的单元尚未启动,该单元也可以激活;
  • Conflicts:配置与 Requires 部分中定义的依赖项相反的依赖项;

[Service]

[Service] 部分是配置诸如 startstop 之类的特定命令的地方。

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
  • Type
    Type = [ simple | forking | Oneshot dbus | notify idle ]
    • simple - 默认值,以 ExecStart 启动的进程是服务的主进程
    • forking - 使用ExecStart启动的进程生成一个子进程,该子进程成为服务的主进程。启动完成后,父进程退出
    • Oneshot - 这种类型与 simple 类似,但是流程在启动后续单元之前退出
    • dbus - 这种类型与 simple 类似,但是后续单元只有在主进程获得 D-Bus 名称之后才会启动
    • notify - 这种类型与 simple 类似,但是后续单元只有在通过 sd_notify() 函数发送通知消息之后才会启动
    • idle - 与 simple 类似,服务二进制文件的实际执行被延迟,直到所有作业都完成,这避免了将状态输出和服务的 shell 输出混合在一起
  • ExecStart:用于指定将在服务启动时执行的命令。这可以是传递了其他命令的二进制文件的路径,也可以是 shell 脚本的路径。可以分别使用 ExecStartPre 和 ExecStartPost 选项添加要在单元初始化之前和之后执行的命令;
  • ExecStop:指定单元停止时要执行的命令或脚本;
  • ExecReload:指定重新加载单元时要执行的命令或脚本;
  • Restart:启用此选项后,服务将在其进程退出后重新启动;
  • RemainAfterExit:如果设置为 True,即使服务的所有进程都已退出,该服务也被认为是活动的。默认值为 False。如果配置了 Type = onesshot,则此选项特别有用;

[Install]

[Install] 以指定与服务安装相关的选项

[Install]
WantedBy=multi-user.target
  • Alias:供单元的附加名称的空格分隔列表,大多数 systemctl 命令 (不包括 systemctl able) 可以使用别名而不是实际的单元名称;
  • RequiredBy:依赖于单位的单位列表。当这个单元被启用时,在 RequredBy 中列出的单元将获得对该单元的一个 Require 依赖项;
  • WantedBy:弱依赖于单位的单位列表。启用此单元后,WantedBy 中列出的单元将获得对该单元的 Want 依赖关系;
  • Also:指定要与该单元一起安装或卸载的单元列表;
  • DefaultInstance:限制为实例化单元,此选项指定启用该单元的默认实例;

实践

新建一个有别于 NGINX 自带的服务单元:

# 新建一个空文件
sudo touch /etc/systemd/system/nginx.service
# 设置权限
sudo chmod 664 /etc/systemd/system/nginx.service
# 编辑 nginx.service
sudo vim /etc/systemd/system/nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
Documentation=https://nginx.org/en/docs/
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

在完成后别忘了重载 systemd:

sudo systemctl daemon-reload