作者选择了自由开源基金会作为 Write for Donations 计划的捐赠对象。
简介
在这个两部分的教程中,你将学习如何使用 systemd
配置 Linux 服务,在重启或崩溃后自动重启。
第一部分涵盖了一般的 Linux 服务管理概念,比如 init
守护进程和运行级别。它以 systemd
中的服务管理演示结束。在这里,你将研究 targets
、wants
、requires
和 unit
文件。
第二部分提供了一个逐步完成实际和常见 systemd
任务的教程。具体来说,你将配置一个 MySQL 数据库服务器,在崩溃或重启后自动启动。
先决条件
要完成本教程,你需要:
运行 CentOS 8 的服务器,包括一个具有 sudo 权限的非根用户。要设置所有这些,包括防火墙,你可以创建一个运行 CentOS 8 的 DigitalOcean Droplet,然后按照我们的初始服务器设置指南进行操作。介绍服务管理守护进程
Linux 服务可以通过改变它们被服务管理守护进程(也称为 init
守护进程)处理的方式来实现自我修复。
init
是 Linux 系统中在机器启动并且内核加载到内存后启动的第一个进程。它决定用户进程或系统服务应该以什么顺序加载,是否应该自动启动等等。
随着 Linux 的发展,init
守护进程的行为也在不断演变。最初,Linux 使用的是 System V init
,与 Unix 中使用的相同。此后,Linux 实现了 Upstart init
守护进程(由 Ubuntu 创建),现在是 systemd init
守护进程(由 Fedora 首次实现)。
大多数现代 Linux 发行版已逐渐迁移到 systemd,而旧式的 init
(如果使用)仅用于向后兼容。FreeBSD,UNIX 的一个变种,使用了一个不同的 System V 实现,称为 BSD init
。
在本文中,我们将介绍 systemd,因为这是今天 Linux 发行版中最常用的服务管理器。但是,在必要时我们也会谈论 System V 和 Upstart,并看看 systemd 是如何从这里演变而来的。为了让你有一个概念:
System V 是最古老的init
系统,用于 Debian 6 及更早版本Ubuntu 9.04 及更早版本CentOS 5 及更早版本 Upstart 在 System V 之后出现,用于 Ubuntu 9.10 到 Ubuntu 14.10,包括 Ubuntu 14.04CentOS 6 systemd 是最新的 Linux 服务管理器,用于 Debian 7 及更高版本Ubuntu 15.04 及更高版本CentOS 7 及更高版本 为了理解 init
守护进程,让我们从一个叫做 runlevel 的东西开始。
运行级别
运行级别表示 Linux 系统的当前状态。例如,运行级别可以是 Linux 服务器的关闭状态、单用户模式、重启模式等。每种模式都会决定在该状态下可以运行哪些服务。
一些服务可以在一个或多个运行级别下运行,但在其他运行级别下不能运行。运行级别用 0 到 6 之间的值表示。以下列表显示了每个级别的含义:
运行级别 0:系统关闭运行级别 1:单用户,救援模式运行级别 2、3、4:多用户,启用网络的文本模式运行级别 5:多用户,启用网络的图形模式运行级别 6:系统重启运行级别 2、3 和 4 因发行版而异。例如,一些 Linux 发行版不实现运行级别 4,而其他发行版则实现了。一些发行版在这三个级别之间有明显的区别。一般来说,运行级别 2、3 或 4 意味着 Linux 以多用户、启用网络的文本模式启动。
当你启用一个服务自动启动时,Linux 实际上是将它添加到一个运行级别中。例如,在 System V 中,操作系统将以特定的运行级别启动;当它启动时,它将尝试启动与该运行级别相关联的所有服务。在 systemd 中,运行级别已经变成了目标,当一个服务被设置为自动启动时,它被添加到一个目标中。我们将在本文后面讨论目标。
介绍 System V init
守护进程
System V 使用一个 inittab
文件,后来的 init
方法为了向后兼容性而保留了它。让我们来看看 System V 的启动顺序:
init
守护进程是从二进制文件 /sbin/init 创建的init
守护进程读取的第一个文件是 /etc/inittab此文件中的一个条目决定了机器应该启动到哪个运行级别。例如,如果运行级别的值被指定为 3,Linux 将以多用户、启用网络的文本模式启动。(这个运行级别被称为默认运行级别)接下来,init
守护进程进一步查看 /etc/inittab 文件,并读取它需要为该运行级别运行的 init
脚本 因此,当 init
守护进程找到了为给定运行级别需要运行的 init
脚本时,它实际上是找出了需要启动的服务。这些 init
脚本是你可以为单个服务配置启动行为的地方。
在 System V 中,init
脚本控制着特定服务。服务的 init
脚本要么由应用程序的供应商提供,要么与 Linux 发行版一起提供(用于本地服务)。你也可以为自己创建的服务创建自己的 init
脚本。
当一个进程或服务(比如 MySQL 服务器)在 System V 下启动时,它的二进制程序文件必须加载到内存中。根据服务的配置方式,这个程序可能会持续在后台执行(并接受客户端连接)。启动、停止或重新加载这个二进制应用程序的工作由服务的 init
脚本处理。它被称为 init
脚本,因为它初始化了服务。
在 System V 中,init
脚本是一个 shell 脚本。它们也被称为 rc
(运行命令)脚本。这些脚本位于 /etc/init.d
目录下。这些脚本被符号链接到 /etc/rc
目录下。在 /etc
目录下,有许多带有数字名称的 rc
目录。这些数字代表不同的运行级别。所以我们有 /etc/rc0.d
、/etc/rc1.d
、/etc/rc2.d
等等。
要使一个服务在崩溃或重启后重新启动,你通常可以在 init
脚本中添加这样一行:
ms:2345:respawn:/bin/sh /usr/bin/service_name
要在系统启动时启用 System V 服务,运行以下命令:
sudo chkconfig service_name on
要禁用它,运行以下命令:
sudo chkconfig service_name off
要检查状态(运行或停止),运行以下命令
sudo service service_name status
介绍 Upstart 守护进程
随着 System V init
加载作业和服务的串行方式变得越来越耗时和复杂,Upstart 守护进程被引入,以实现更快速的操作系统加载、崩溃服务的优雅清理以及系统服务之间可预测的依赖关系。
Upstart init
在几个方面优于 System V init
:
为了保持简单,Upstart 兼容于 System V。/etc/init.d/rc
脚本仍然运行以管理本地 System V 服务。它的主要区别在于它允许将多个事件与一个服务关联起来的方式。这种基于事件的架构使得 Upstart 成为了一个灵活的服务管理器。在 Upstart 中,每个事件都可以触发一个负责处理该事件的 shell 脚本。这些事件包括:
在这些事件之间,一个服务可以处于多种状态,比如等待、预启动、启动中、运行中、预停止、停止中等。Upstart 也可以针对每个状态采取行动,从而创建一个非常灵活的架构。
在启动时,Upstart 会正常运行任何 System V init
脚本。然后它会查找 /etc/init
目录并执行每个服务配置文件中的 shell 命令。除其他事项外,这些文件控制了服务的启动行为。
这些文件的命名风格为 service_name.conf
,它们具有不同部分(称为 stanzas)的纯文本内容。每个部分描述了服务的不同方面以及它应该如何行为。要使服务在崩溃或重启后自动启动,可以在其服务配置文件中添加 respawn
命令,如下所示,用于 cron 服务。
...description "regular background program processing daemon"start on runlevel [2345]stop on runlevel [!2345]expect fork**respawn**exec cron
介绍 systemd
守护进程
Linux 中最新的 init
守护进程是 systemd。事实上,它不仅仅是一个 init
守护进程:systemd 是一个涵盖了现代 Linux 系统许多组件的框架。
它的功能之一是作为 Linux 的系统和服务管理器。在这个容量下,systemd 控制服务在崩溃或机器重启时应该如何行为。您可以在这里阅读有关 systemd 的 systemctl 的信息。
systemd 兼容于 System V 命令和初始化脚本。这意味着任何 System V 服务也将在 systemd 下运行。这是可能的,因为大多数 Upstart 和 System V 管理命令已经被修改以在 systemd 下工作。
systemd 配置文件:单元文件
systemd 的核心是单元文件。每个单元文件代表一个特定的系统资源。关于资源的信息被记录在单元文件中。服务单元文件是简单的文本文件(就像 Upstart 的 .conf 文件),具有声明性语法。这使得文件易于理解和修改。
systemd 和其他两种 init
方法的主要区别在于 systemd 负责初始化服务守护进程和其他类型的资源,比如设备操作系统路径、挂载点、套接字等。单元文件的命名风格是 service_name.unit_type
。因此,您会看到像 dbus.service
、sshd.socket
或 home.mount
这样的文件。
目录结构
在基于 Red Hat 的系统(如 CentOS)中,单元文件位于两个位置。主要位置是 /lib/systemd/system/
。由系统管理员创建的自定义单元文件或修改过的现有单元文件将位于 /etc/systemd/system
。
如果两个位置都存在同名的单元文件,systemd 将使用 /etc
下的那个。假设一个服务被启用以在启动时启动或任何其他目标/运行级别。那么将在 /etc/systemd/system
的适当目录下为该服务单元文件创建一个符号链接。/etc/systemd/system
下的单元文件实际上是指向 /lib/systemd/system
下同名文件的符号链接。
systemd init
序列:目标单元
一种特殊类型的单元文件是目标单元。
目标单元的文件名后缀为 .target。目标单元与其他单元文件不同,因为它们不代表一个特定的资源。相反,它们通过分组和启动应该属于该状态的多个单元文件来代表系统的状态。因此,systemd 目标可以松散地与 System V 运行级别进行比较,尽管它们并不相同。
每个目标都有一个名称而不是一个数字。例如,我们有 multi-user.target
而不是 runlevel 3
,或者 reboot.target
而不是 runlevel 6
。当 Linux 服务器使用 multi-user.target
启动时,它实质上将服务器带到了 runlevel 2, 3 或 4
,即启用了带有网络功能的多用户文本模式。
它将服务器带到该阶段的方式是不同的地方。与 System V 不同,systemd 不会按顺序启动服务。在启动过程中,它可以检查其他服务或资源的存在,并决定它们的加载顺序。这使得服务可以并行加载。
目标单元和运行级别之间的另一个区别是,在 System V 中,Linux 系统只能存在于一个运行级别。您可以更改运行级别,但系统将仅存在于新的运行级别。而在 systemd 中,目标单元可以是包容性的,这意味着当目标单元激活时,它可以确保其他目标单元作为其一部分被加载。
例如,使用图形用户界面启动的 Linux 系统将激活 graphical.target
,这将自动确保 multi-user.target
也被加载和激活。在 System V 术语中,这就像同时激活了运行级别 3 和 5。
下表比较了运行级别和目标单元:
运行级别(System V init) | 目标单元(Systemd) |
---|---|
runlevel 0 | poweroff.target |
runlevel 1 | resuce.target |
runlevel 2, 3, 4 | multi-user.target |
runlevel 5 | graphical.target |
runlevel 6 | reboot.target |
systemd 默认目标
systemd 的 default.target
相当于 System V 的默认运行级别。
System V 中默认运行级别是在一个叫做 inittab 的文件中定义的。在 systemd 中,该文件被 default.target 取代。默认目标单元文件位于 /etc/systemd/system 目录下。它是指向 /lib/systemd/system 下的目标单元文件之一的符号链接。
当你改变默认目标时,实质上是重新创建了该符号链接并改变了系统的运行级别。
在 System V 中的 inittab 文件还指定了 Linux 将从中执行其 init
脚本的目录:它可以是任何 rcn.d 目录之一。在 systemd 中,默认目标单元确定了在启动时将加载哪些资源单元。
当单元被激活时,它们可以并行激活,也可以按顺序激活。一个资源单元的加载方式可能取决于它想要或需要的其他资源单元。
systemd 依赖关系:Wants 和 Requires
systemd 中的 wants 和 requires 控制着 systemd 在服务守护程序之间的依赖关系。
如前所述,Upstart 使用配置文件确保服务的并行加载。在 System V 中,一个服务可以在特定运行级别启动,但也可以被设置为等待直到另一个服务或资源可用。类似地,systemd 服务可以被设置为在一个或多个目标中加载,或者等待直到另一个服务或资源激活。
在 systemd 中,一个需要另一个单元的单元将不会启动,直到所需的单元被加载和激活。如果所需的单元由于某种原因失败,而第一个单元处于活动状态,那么第一个单元也将停止。
这确保了系统的稳定性。因此,一个需要特定目录存在的服务可以被设置为等待,直到该目录的挂载点处于活动状态。另一方面,一个想要另一个单元的单元将不会施加这样的限制。如果所需的单元在调用者处于活动状态时停止,它不会停止。这种情况的一个例子是在图形目标模式下启动的非必要服务。
实际示例:理解 systemd 启动顺序
为了理解 systemd 下服务的启动行为,我们使用了一个 CentOS 8.3 Droplet。我们将尽可能地跟随 .target 的路径。systemd 的启动顺序遵循一长串依赖关系。
首先,让我们运行以下命令来列出默认目标单元文件:
sudo ls -l /etc/systemd/system/default.target
这将显示如下输出:
lrwxrwxrwx. 1 root root 37 Dec 4 17:42 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target
如你所见,默认目标实际上是指向 /lib/systemd/system/ 下的 multi-user 目标文件的符号链接。因此,系统应该在 multi-user.target 下启动,这类似于 System V init 中的运行级别 3。
multi-user.target.wants
接下来,让我们运行以下命令来检查 multi-user.target 文件想要的所有服务:
sudo ls -l /etc/systemd/system/multi-user.target.wants/*.service
这应该显示一个符号链接文件列表,指向 /usr/lib/systemd/system/ 下的实际单元文件:
lrwxrwxrwx. 1 root root 38 Dec 4 17:38 /etc/systemd/system/multi-user.target.wants/auditd.service -> /usr/lib/systemd/system/auditd.servicelrwxrwxrwx. 1 root root 39 Dec 4 17:39 /etc/systemd/system/multi-user.target.wants/chronyd.service -> /usr/lib/systemd/system/chronyd.servicelrwxrwxrwx. 1 root root 37 Dec 4 17:38 /etc/systemd/system/multi-user.target.wants/crond.service -> /usr/lib/systemd/system/crond.servicelrwxrwxrwx. 1 root root 42 Dec 4 17:39 /etc/systemd/system/multi-user.target.wants/irqbalance.service -> /usr/lib/systemd/system/irqbalance.servicelrwxrwxrwx. 1 root root 37 Dec 4 17:41 /etc/systemd/system/multi-user.target.wants/kdump.service -> /usr/lib/systemd/system/kdump.service...
除了 multi-user.target
,还有不同类型的目标,如 system-update.target
或 basic.target
。要查看 multi-user 目标依赖的目标,运行以下命令:
sudo systemctl show --property "Requires" multi-user.target | fmt -10
输出显示:
Requires=basic.target
basic.target
您可以运行以下命令来查看 basic.target 是否需要任何必需的单元:
sudo systemctl show --property "Requires" basic.target | fmt -10
结果显示,basic.target 需要 sysinit.target:
Requires=sysinit.target-.mount
它还需要一些其他的目标:
sudo systemctl show --property "Wants" basic.target | fmt -10
该命令将返回以下内容:
Wants=slices.targetpaths.targettimers.targetmicrocode.servicesockets.targetsysinit.target
递归进行,您可以查看 sysinit.target 是否还需要运行其他目标:
sudo systemctl show --property "Requires" sysinit.target | fmt -10
将不会有任何。然而,sysinit.target 会需要其他目标:
systemctl show --property "Wants" sysinit.target | fmt -10
将会出现以下输出:
Wants=systemd-random-seed.servicedev-mqueue.mountrngd.servicesystemd-modules-load.serviceproc-sys-fs-binfmt_misc.automountlocal-fs.targetsys-fs-fuse-connections.mountsystemd-sysusers.servicesystemd-update-done.servicesystemd-update-utmp.servicesystemd-journal-flush.servicedev-hugepages.mountdracut-shutdown.serviceswap.targetsystemd-udevd.serviceimport-state.servicesys-kernel-debug.mountnis-domainname.servicesystemd-journald.serviceselinux-autorelabel-mark.servicekmod-static-nodes.serviceloadmodules.serviceldconfig.servicecryptsetup.targetsystemd-sysctl.servicesystemd-ask-password-console.pathsystemd-journal-catalog-update.servicesystemd-udev-trigger.servicesystemd-tmpfiles-setup.servicesystemd-hwdb-update.servicesys-kernel-config.mountsystemd-binfmt.servicesystemd-tmpfiles-setup-dev.servicesystemd-machine-id-commit.servicesystemd-firstboot.service
检查 systemd
单元文件
现在进一步,让我们来查看一个服务单元文件,比如 sshd 的单元文件:
sudo vi /etc/systemd/system/multi-user.target.wants/sshd.service
它看起来像这样:
[Unit]Description=OpenSSH server daemonDocumentation=man:sshd(8) man:sshd_config(5)After=network.target sshd-keygen.targetWants=sshd-keygen.target[Service]Type=notifyEnvironmentFile=-/etc/crypto-policies/back-ends/opensshserver.configEnvironmentFile=-/etc/sysconfig/sshdExecStart=/usr/sbin/sshd -D $OPTIONS $CRYPTO_POLICYExecReload=/bin/kill -HUP $MAINPIDKillMode=processRestart=on-failureRestartSec=42s[Install]WantedBy=multi-user.target
您可以看到服务单元文件是干净且易于理解的。
第一个重要部分是 [Unit]
部分中的 After 子句。这表示 sshd 服务需要在 network.target 和 sshd-keygen.target 加载后加载。
[Install]
部分显示该服务被 multi-user.target 所需。这意味着 multi-user.target 将加载 sshd 守护程序,但如果在加载过程中 sshd 失败,它不会关闭或崩溃。
由于 multi-user.target
是默认目标,sshd 守护程序应该在启动时启动。在 [Service]
部分,Restart
参数的值为 on-failure
。此设置允许 sshd 守护程序在崩溃或非正常退出时重新启动。
结论
在本文中,您了解了 System V、Upstart 和 systemd 服务管理守护程序。您探索了启动脚本和配置文件、重要参数、启动顺序以及控制服务启动行为的命令。
在本文的第二部分中,我们将运用这些技能到一个实际示例,并使用 systemd 配置 MySQL。完成后,您的 MySQL 实例将在重新启动或崩溃后自动重新启动。虽然您将使用 MySQL 作为示例应用程序,但您可以替换任意数量的服务,比如 Nginx 或 Apache web 服务器。