自 2010 年 3 月推出以来,人们对 systemd 的看法不一。 但是你不能否认的一件事是 systemd 现在几乎所有的 Linux 发行版中都存在!
当然,有一些 Linux 发行版明确宣传使用不同于 systemd 的不同 init 系统,但这种情况非常罕见。 乌班图, Fedora、RHEL、Debian、Pop!_OS、openSUSE、Arch,它们都默认附带 systemd。
由于 systemd 的使用如此广泛,因此值得一看并学习如何创建 systemd 服务。
什么是系统服务?
简单来说,服务就是一个“后台”进程,根据特定情况启动或停止。 您不需要手动启动和/或停止它。 “systemd 服务”是一个以 systemd 能够解析和理解它的格式编写的文件,然后按照您的要求执行操作。
从技术上讲,我所说的“systemd service”文件实际上称为“systemd unit”文件,但由于本教程是关于创建服务的,我将继续将其称为“systemd service”文件。
了解 systemd 服务文件的基本结构
systemd 服务文件作为三个重要且必要的部分。 他们是 [Unit]
, [Service]
和 [Install]
部分。 systemd 服务文件的扩展名是 .service
我们使用英镑/哈希符号 (#
) 征求意见。
下面是一个 example systemd 服务文件。 请注意,这不是一个实际的 systemd 服务文件。 我已经对其进行了修改,以帮助您理解。
[Unit]
Description=Apache web server
After=network.target
Before=nextcloud-web.service
[Service]
ExecStart=/usr/local/apache2/bin/httpd -D FOREGROUND -k start
ExecReload=/usr/local/apache2/bin/httpd -k graceful
Type=notify
Restart=always
[Install]
WantedBy=default.target
RequiredBy=network.target
这是 systemd 服务文件的最基本结构。 让我们了解这些词中的每一个的含义。
这 [Unit]
部分
单元部分包含有关单元本身的详细信息和描述。 在我们的例子中,它包含有关服务的详细信息。 诸如“它的描述是什么”、“它的依赖项是什么”等详细信息。
以下是 Unit 部分的字段:
Description
:- systemd 服务的可读标题。After
:- 设置对服务的依赖。 为了 example如果你正在配置 Apache web 服务器,您希望服务器在网络在线后启动。 这通常包括目标或其他服务。Before
:- 在指定服务之前启动当前服务。 在这个 example,我说“我想要 Apache Web 服务器在 Nextcloud 服务启动之前运行”。这是因为,在我的情况下,Nextcloud 服务器依赖于 Apache 网络服务器。 这也像After
,包括目标或其他服务。
这 [Service]
部分
服务部分包含有关服务执行和终止的详细信息。
以下是“服务”部分具有的字段:
ExecStart
:- 服务启动时需要执行的命令。 在我们的例子中,我们想要 Apache 服务器启动。ExecReload
:- 这是个可选的选项。 它指定如何重新启动服务。 对于执行磁盘 I/O(尤其是写入磁盘,如数据库)的服务,最好优雅地终止它们并重新启动。 如果您希望有特定的重新启动机制,请使用此字段。Type
:- 此 systemd 服务的进程启动类型。 选项是simple
,exec
,forking
,oneshot
,dbus
,notify
和idle
. (更多信息 这里)Restart
:- 这是另一个可选字段,但您很可能会使用。 这指定是否应该重新启动服务(取决于情况)。 可用的选项是no
,on-success
,on-failure
,on-abnormal
,on-watchdog
,on-abort
和always
.
这 [Install]
部分
顾名思义,安装部分处理 systemd 服务/单元文件的安装。 这在您运行时使用 systemctl enable
和 systemctl disable
启用/禁用服务的命令。
以下是安装部分的字段:
WantedBy
:- 这类似于After
和Before
字段,但主要区别在于它用于指定与 systemd 等效的“运行级别”。 这default.target
是当所有系统初始化完成时——当用户被要求登录时。大多数面向用户的服务(如 Apachecron, GNOME-stuff 等)使用这个目标。RequiredBy
:- 这个字段可能感觉非常类似于WantedBy
,但主要区别在于该字段指定了硬依赖项。 这意味着,如果是依赖项,则此服务将失败。
我上面列出的只是最小的 example. 根据您的环境和需求,您可以转动大量的旋钮来定制您的服务。
有关 systemd 的完整文档,请 参考这个页面. 这实际上已经记录了所有内容!
创建自己的 systemd 服务
现在您已经了解了基本 systemd 服务文件的结构,让我们深入了解如何创建您自己的 systemd 服务。
为了这 example,我将创建一个简单的 systemd 服务来执行 bash 启动脚本。 这是我的 bash 脚本,命名 check-file-system.sh
,放置在 ~/.scripts/
目录。
#!/usr/bin/env bash
ROOT_PARTITION=$(mount | grep '/ ' | awk '{print $1}')
PHYSICAL_DISK=$(lsblk -no pkname ${ROOT_PARTITION} | head -n 1)
if [ ${EUID} -ne 0 ]
then
exit 1 # this is meant to be run as root
fi
fsck /dev/${PHYSICAL_DISK}
⚠️这个 bash 脚本将立即失败,因为我们正在 fsck-ing 的设备是作为 rootfs 挂载的。 此脚本仅用于演示目的;)
让我解释一下我在这个脚本中所做的事情。 我正在查找以 root 身份安装的磁盘分区(/
)。 那可以是 /dev/sda1
或者 /dev/sdb2
,或其他任何东西。 使用它,我正在找出实际的驱动器名称(sda
)。
有了这些信息,我正在该驱动器上使用 fsck 命令运行文件系统检查。 我希望在每次启动时运行这个脚本。
为此,我将创建一个 systemd 服务文件,如下所示:
[Unit]
Description=Checking filesytems on boot
After=multi-user.target
[Service]
ExecStart=/usr/bin/bash /home/pratham/.scripts/check-file-system.sh
Type=simple
[Install]
WantedBy=multi-user.target
出色的! 我们的 systemd 服务文件现已准备就绪。
为我们的 systemd 服务文件选择一个位置
现在我们的启动脚本和 systemd 服务文件已经准备好了……我在哪里保存 systemd 服务文件,以及如何启用它?
在我回答这个问题之前,我需要先问一个不同的问题。 “这个即将执行的 systemd 服务,谁来执行?它需要超级用户权限吗?它需要作为特定用户执行吗?”
以下是您可以放置 systemd 服务文件的位置,具体取决于要执行它的用户。
root
:/etc/systemd/system/
pratham
:/home/pratham/.config/systemd/user/
为了这 example,我的用户名为 pratham
,但你的肯定会和我的不同。
所以,如果一个服务需要超级用户权限,比如每 6 小时生成一个文件系统数据库,它就需要超级用户权限。 因此,这必须放在 /etc/systemd/system/
目录。
?由普通用户执行的systemd服务将需要 --user
显式传递的选项。 假设只有用户 pratham
有一个服务叫 greet-pratham.service
,要重新启动它,我将使用命令 systemctl --user restart greet-pratham.service
对于没有任何特殊权限的服务,最好由普通用户执行,例如 pratham
. 所以它必须放在 ~/.config/systemd/user/
目录。
在我们的 example,我们正在执行文件系统检查,这需要超级用户权限。 所以,我将把我的 systemd 服务文件放在 /etc/systemd/system/
目录。 我的 systemd 服务文件的绝对路径是 /etc/systemd/system/fsck-on-boot.service
最后的步骤
现在已经创建了一个 systemd 服务文件并将其放置在正确的位置,您需要告诉 systemd 重新加载单元文件。 这是因为您创建了一个新服务,并且在启动时检测到服务。 您已经启动,因此您需要手动执行此操作。
要告诉 systemd 重新加载单元文件,请使用以下命令(对于需要超级用户权限的服务):
sudo systemctl daemon-reload
或者,如果您创建了用户服务,请使用以下命令:
systemctl --user daemon-reload
完成后,您可以启用该服务。 我将启用我的服务 fsck-on-boot.service
像这样:
sudo systemctl enable fsck-on-boot.service
启用服务后,使用 systemctl status
命令。
$ sudo systemctl status fsck-on-boot.service
× fsck-on-boot.service - Checking filesytems on boot
Loaded: loaded (/etc/systemd/system/fsck-on-boot.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Tue 2022-09-06 09:57:04 IST; 8s ago
Main PID: 130145 (code=exited, status=8)
CPU: 82ms
Sep 06 09:57:04 vidhyaa systemd[1]: Started Checking filesytems on boot.
Sep 06 09:57:04 vidhyaa bash[130153]: fsck from util-linux 2.37.2
Sep 06 09:57:04 vidhyaa bash[130155]: e2fsck 1.46.5 (30-Dec-2021)
Sep 06 09:57:04 vidhyaa bash[130155]: /dev/sda is in use.
Sep 06 09:57:04 vidhyaa bash[130155]: e2fsck: Cannot continue, aborting.
Sep 06 09:57:04 vidhyaa systemd[1]: fsck-on-boot.service: Main process exited, code=exited, status=8/n/a
Sep 06 09:57:04 vidhyaa systemd[1]: fsck-on-boot.service: Failed with result 'exit-code'.
正如我之前提到的,脚本失败了。 但更重要的是, 服务已启用. 这才是更重要的。
现在,您可以通过停止、启动、重新启动、禁用、重新启用等方式将此服务视为任何常规服务…
结论
本文讨论了创建自己的 systemd 服务的一些关键方面。 剖析 systemd 服务文件,将其放置在何处,重新加载 systemd 以使其了解您新创建的服务,启用该服务,最后检查其状态。