如何使用 systemd-nspawn 进行 Linux 系统恢复

利用 systemd 启动容器的能力来修复损坏的系统根文件系统。
184 位读者喜欢这篇文章。
magnifying glass on computer screen, finding a bug in the code

Opensource.com

自从 GNU/Linux 系统出现以来,系统管理员就需要从根文件系统损坏、意外配置更改或阻止系统启动到“正常”状态的其他情况中恢复。

Linux 发行版通常在启动时提供一个或多个菜单选项(例如,在 GRUB 菜单中),可用于抢救损坏的系统;通常,它们会将系统启动到单用户模式,并禁用大多数系统服务。在最坏的情况下,用户可以修改引导加载程序中的内核命令行,以使用标准 shell 作为 init (PID 1) 进程。 这种方法最复杂,并且充满并发症,当系统需要抢救时,会导致沮丧和浪费时间。

最重要的是,这些方法都假定损坏的系统具有某种物理控制台,但在云计算时代,这已不再是理所当然的。 如果没有物理控制台,那么以这种方式影响引导过程的选项很少(如果有的话)。 即使是物理机器也可能是小型嵌入式设备,无法提供易于使用的控制台,并且找到正确的串行端口电缆和适配器并设置串行终端仿真器,所有这些都是为了在处理紧急情况时使用串行控制台端口,这通常很复杂。

当另一个系统(具有相同的体系结构和大致相似的配置)可用时,简化修复过程的常用技术是从损坏的系统中取出存储设备,并将它们作为辅助设备连接到工作系统。 对于物理系统,这通常很简单,但是大多数云计算平台也可以支持此操作,因为它们允许将损坏实例的根存储卷挂载到另一个实例上。

一旦根文件系统附加到另一个系统,使用 **fsck** 和其他工具解决文件系统损坏问题就很简单。 解决配置错误、损坏的软件包或其他问题可能更复杂,因为它们需要挂载文件系统并找到并更改正确的配置文件或数据库。

使用 systemd

在 **systemd** 之前,使用文本编辑器编辑配置文件是更正配置的实用方法。 找到必要的文件并理解其内容可能是一个单独的挑战,这超出了本文的范围。

但是,当 GNU/Linux 系统使用 **systemd** 时,许多配置更改最好使用它提供的工具进行——例如,启用和禁用服务需要在各个位置创建或删除符号链接。 **systemctl** 工具用于进行这些更改,但是使用它需要一个 **systemd** 实例正在运行并监听(在 D-Bus 上)请求。 当根文件系统作为其他文件系统挂载在另一台计算机上时,无法使用正在运行的 **systemd** 实例进行这些更改。

手动启动目标系统的 **systemd** 也不可行,因为它被设计为系统上的 PID 1 进程并管理所有其他进程,这将与用于修复的系统上已在运行的实例冲突。

值得庆幸的是,**systemd** 具有启动容器的能力,容器是完全封装的 GNU/Linux 系统,具有自己的 PID 1 和环境,并利用 Linux 内核提供的各种命名空间功能。 与 Docker 和 Rocket 之类的工具不同,**systemd** 不需要容器镜像即可启动容器;它可以启动一个以现有文件系统中的任何点为根的容器。 这是使用 **systemd-nspawn** 工具完成的,该工具将创建必要的系统命名空间并启动容器中的初始进程,然后在容器中提供一个控制台。 与仅更改文件系统的表面根目录的 **chroot** 相比,这种类型的容器将具有单独的文件系统命名空间,适合的文件系统挂载在 **/dev**、**/run** 和 **/proc** 上,以及单独的进程命名空间和 IPC 命名空间。 请查阅 **systemd-nspawn** 手册页 以了解有关其功能的更多信息。

一个例子展示它是如何工作的

在本例中,包含损坏系统根文件系统的存储设备已连接到正在运行的系统,显示为 **/dev/vdc**。 设备名称将根据现有存储设备的数量、设备类型以及用于将其连接到系统的方法而有所不同。 根文件系统可以使用整个存储设备,也可以位于设备内的分区中;由于最常见的(简单)配置将根文件系统放置在设备的第一个分区中,因此本示例将使用 **/dev/vdc1**。 _请务必将下面的命令中的设备名称替换为系统正确的设备名称。_

损坏的根文件系统也可能比设备上的单个文件系统更复杂;它可能是 LVM 卷集中的一个卷,或者位于组合到软件 RAID 设备的一组设备上。 在这些情况下,必须执行构成和激活保存文件系统的逻辑设备的必要步骤,然后才能将其用于挂载。 同样,这些步骤超出了本文的范围。

先决条件

首先,确保已安装 **systemd-nspawn** 工具 - 大多数 GNU/Linux 发行版默认情况下不安装它。 它由大多数发行版上的 **systemd-container** 软件包提供,因此请使用发行版的软件包管理器安装该软件包。 本例中的说明已使用 Debian 9 进行了测试,但在任何现代 GNU/Linux 发行版上都应类似地工作。

使用下面的命令几乎肯定需要 root 权限,因此您需要以 root 身份登录,使用 **sudo** 获取具有 root 权限的 shell,或者在每个命令前面加上 **sudo**。

验证和挂载文件系统

首先,使用 **fsck** 验证目标文件系统的结构和内容

$ fsck /dev/vdc1

如果发现文件系统存在任何问题,请适当地回答问题以更正它们。 如果文件系统损坏严重,则可能无法修复,在这种情况下,您必须找到其他方法来提取其内容。

现在,创建一个临时目录并将目标文件系统挂载到该目录上

$ mkdir /tmp/target-rescue
$ mount /dev/vdc1 /tmp/target-rescue

挂载文件系统后,启动一个容器,并将该文件系统作为其根文件系统

$ systemd-nspawn --directory /tmp/target-rescue --boot -- --unit rescue.target

用于启动容器的命令行参数是

  • **--directory /tmp/target-rescue** 提供容器根文件系统的路径。
  • **--boot** 在容器的根文件系统中搜索合适的 init 程序并启动它,并将命令行中的参数传递给它。 在本例中,目标系统也使用 **systemd** 作为其 PID 1 进程,因此其余参数是为它准备的。 如果您要修复的目标系统使用任何其他工具作为其 PID 1 进程,则需要相应地调整参数。
  • **--** 将 **systemd-nspawn** 的参数与为容器的 PID 1 进程准备的参数分开。
  • **--unit rescue.target** 告诉容器中的 **systemd** 它应该尝试在引导过程中达到的目标的名称。 为了简化目标系统中的抢救操作,请将其引导到“rescue”模式,而不是引导到其正常的多用户模式。

如果一切顺利,您应该看到类似于以下的输出

Spawning container target-rescue on /tmp/target-rescue.
Press ^] three times within 1s to kill container.
systemd 232 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN)
Detected virtualization systemd-nspawn.
Detected architecture arm.

Welcome to Debian GNU/Linux 9 (Stretch)!

Set hostname to <test>.
Failed to install release agent, ignoring: No such file or directory
[  OK  ] Reached target Swap.
[  OK  ] Listening on Journal Socket (/dev/log).
[  OK  ] Started Dispatch Password Requests to Console Directory Watch.
[  OK  ] Reached target Encrypted Volumes.
[  OK  ] Created slice System Slice.
         Mounting POSIX Message Queue File System...
[  OK  ] Listening on Journal Socket.
         Starting Set the console keyboard layout...
         Starting Restore / save the current clock...
         Starting Journal Service...
         Starting Remount Root and Kernel File Systems...
[  OK  ] Mounted POSIX Message Queue File System.
[  OK  ] Started Journal Service.
[  OK  ] Started Remount Root and Kernel File Systems.
         Starting Flush Journal to Persistent Storage...
[  OK  ] Started Restore / save the current clock.
[  OK  ] Started Flush Journal to Persistent Storage.
[  OK  ] Started Set the console keyboard layout.
[  OK  ] Reached target Local File Systems (Pre).
[  OK  ] Reached target Local File Systems.
         Starting Create Volatile Files and Directories...
[  OK  ] Started Create Volatile Files and Directories.
[  OK  ] Reached target System Time Synchronized.
         Starting Update UTMP about System Boot/Shutdown...
[  OK  ] Started Update UTMP about System Boot/Shutdown.
[  OK  ] Reached target System Initialization.
[  OK  ] Started Rescue Shell.
[  OK  ] Reached target Rescue Mode.
         Starting Update UTMP about System Runlevel Changes...
[  OK  ] Started Update UTMP about System Runlevel Changes.
You are in rescue mode. After logging in, type "journalctl -xb" to view
system logs, "systemctl reboot" to reboot, "systemctl default" or ^D to
boot into default mode.
Give root password for maintenance
(or press Control-D to continue):

在此输出中,您可以看到 **systemd** 作为容器中的 init 进程启动,并检测到它正在容器内运行,因此它可以适当地调整其行为。 启动各种单元文件以使容器达到可用状态,然后请求目标系统的 root 密码。 如果您想要一个具有 root 权限的 shell 提示符,可以在此处输入 root 密码,或者您可以按 **Ctrl+D** 以允许启动过程继续,这将显示一个正常的控制台登录提示符。

完成对目标系统的必要更改后,快速连续按三次 **Ctrl+]**; 这将终止容器并使您返回到原始 shell。 从那里,您可以通过卸载目标系统的文件系统并删除临时目录来清理。

$ umount /tmp/target-rescue
$ rmdir /tmp/target-rescue

就是这样! 您现在可以删除目标系统的存储设备并将它们返回到目标系统。

以这种方式使用 **systemd-nspawn** 的想法,尤其是 **--boot 参数**,来自 StackExchange 上发布的 一个问题。 感谢 Shibumi 和 kirbyfan64sos 为这个问题提供了有用的答案!

标签
User profile image.
Kevin P. Fleming 拥有 25 年以上的编程经验,精通每种主要的编程语言。 行业经验包括传统的客户端/服务器数据库应用程序、开源消息传递和网络以及大型机操作系统。 Kevin 的主要技能是通过问题分析和解决方案设计来有效地利用资源来生成解决方案。

1 条评论

非常有趣!! 谢谢!

Creative Commons License本作品采用 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.