1. chroot 命令实战:改变根目录环境

chroot (change root) 命令能够改变当前进程及其子进程的根目录。这意味着被 chroot 过的程序将认为新的目录是其文件系统的根目录 (/),无法访问该新根目录之外的文件和目录。这在系统恢复、构建隔离环境或测试软件时非常有用。

1.1 chroot 的典型应用场景

  • 系统恢复与修复: 当您的 Linux 系统无法正常启动,例如 GRUB 引导器损坏、核心配置文件错误、或者需要重置 root 密码时,chroot 是进入受损系统环境进行修复的关键工具。
  • 构建隔离的构建/测试环境: 在一个干净、隔离的环境中编译软件,避免与宿主系统的库冲突,或为特定目的(如交叉编译)设置独立的开发环境。
  • 创建“chroot jail” (监狱): 限制特定服务的访问权限,增强安全性。例如,可以将 FTP 服务器或 SSH 用户限制在文件系统的某个特定子目录中,防止他们访问其他敏感区域。

1.2 chroot 系统恢复实战(以 GRUB 修复为例)

假设您的 Linux 系统(例如 CentOS/Ubuntu)因 GRUB 配置错误或磁盘问题无法启动。您需要使用 live CD/USB 或进入系统的救援模式。

操作步骤:

  1. 启动到救援环境:

    • 方法一:使用 Linux Live CD/USB: 插入 Live CD/USB,从 BIOS/UEFI 设置中选择从其启动。通常会进入一个图形界面或命令行 shell。
    • 方法二:使用发行版的安装介质进入救援模式:
      • 插入对应发行版的安装 DVD/USB。
      • 从引导菜单选择 “Troubleshooting” -> “Rescue a Red Hat Enterprise Linux system” (RHEL/CentOS) 或类似选项 (Ubuntu 通常是 “Try Ubuntu” 进入 Live 环境)。
      • 救援模式启动后,系统会尝试检测并挂载现有 Linux 安装。如果成功,它会提示您选择将其挂载到 /mnt/sysimage/mnt/sysroot 等路径。选择继续。
  2. 确定并挂载根分区:

    • 进入救援 shell 后(通常是 root 用户),您需要确定您的系统根分区(/)在哪里。

      1
      2
      # 列出所有块设备,查找你的根分区(例如 /dev/sda2 或 /dev/mapper/centos-root)
      lsblk -f
    • 创建挂载点并挂载根分区。假设根分区是 /dev/sda2

      1
      2
      mkdir /mnt/rootfs
      mount /dev/sda2 /mnt/rootfs
    • 如果您的系统有单独的 /boot 分区,也需要挂载它。假设 /boot/dev/sda1

      1
      2
      mkdir /mnt/rootfs/boot
      mount /dev/sda1 /mnt/rootfs/boot
    • 如果使用了 **LVM (Logical Volume Manager)**,您需要先激活 LVM 卷组:

      1
      2
      3
      4
      5
      6
      7
      # 扫描并激活所有LVM卷组
      vgscan --mknodes
      vgchange -ay
      # 再次lsblk -f确认LVM卷已显示,例如 /dev/mapper/vgname-root
      lsblk -f
      # 挂载LVM根卷
      mount /dev/mapper/vgname-root /mnt/rootfs
  3. 绑定必要的虚拟文件系统:

    • 为了在 chroot 环境中能正常工作,您需要将 /proc/sys/dev 这些虚拟文件系统绑定到您挂载的根分区内,以便 chroot 后的环境能够访问内核信息、设备文件和进程数据。

      1
      2
      3
      4
      5
      mount --bind /proc /mnt/rootfs/proc
      mount --bind /sys /mnt/rootfs/sys
      mount --bind /dev /mnt/rootfs/dev
      # 如果需要网络,或者一些服务,可能还需要绑定 /run
      mount --bind /run /mnt/rootfs/run
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      # for循环方式
      # 假设你的 chroot 目标目录是 /mnt/mychroot

      # 定义需要绑定挂载的虚拟文件系统列表
      VIRTUAL_FSS="/proc /sys /dev /run"

      # 使用 for 循环逐一执行绑定挂载
      for fs in $VIRTUAL_FSS; do
      echo "Binding $fs to /mnt/mychroot$fs"
      # 确保目标挂载点存在(如果是首次创建 chroot 环境)
      sudo mkdir -p /mnt/mychroot"$fs"
      # 执行绑定挂载
      sudo mount --bind "$fs" /mnt/mychroot"$fs"
      done

      # 完成绑定挂载后,才能安全地执行 chroot 命令
      sudo chroot /mnt/mychroot /bin/bash
  4. 执行 chroot 命令:

    • 现在,您可以进入您的实际系统环境了:

      1
      2
      chroot /mnt/rootfs /bin/bash
      # 或者直接 chroot /mnt/rootfs (大多数情况下会默认启动 /bin/bash)
    • 您的 shell 提示符可能会改变(例如,从 bash-4.2# 变为 root@yourhost:/#),这表明您已经成功进入了 chroot 环境。在这个环境中,/mnt/rootfs 就是 /

  5. 进行系统修复(GRUB 修复示例):

    • chroot 环境中,您可以执行各种修复操作。以 GRUB 修复为例:

      • 对于 BIOS/MBR 系统:

        1
        2
        3
        4
        grub-install /dev/sda  # 将GRUB安装到硬盘的MBR
        update-grub # (Debian/Ubuntu) 重新生成grub.cfg
        # 或者
        grub2-mkconfig -o /boot/grub2/grub.cfg # (RHEL/CentOS) 重新生成grub.cfg
      • 对于 UEFI/GPT 系统: 确保 ESP (EFI System Partition) 已挂载到 /boot/efi。如果在第 2 步中没挂载,现在需要挂载它:

        1
        2
        3
        4
        # 假设 /dev/sda1 是ESP
        mount /dev/sda1 /boot/efi
        grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=your_distro_name --recheck
        update-grub # 或 grub2-mkconfig
    • 其他常见的修复:

      • 重置 root 密码: passwd root
      • 编辑 /etc/fstab 修复错误的挂载点或 UUID。
      • 安装/修复软件包: apt update && apt install some-package (Debian/Ubuntu) 或 yum install some-package (RHEL/CentOS)。
  6. 退出 chroot 并重启:

    • 修复完成后,输入 exit 退出 chroot 环境。

    • 卸载之前挂载的 /proc, /sys, /dev 和根分区:

      1
      2
      3
      4
      5
      umount /mnt/rootfs/proc
      umount /mnt/rootfs/sys
      umount /mnt/rootfs/dev
      umount /mnt/rootfs/boot # 如果挂载了 /boot
      umount /mnt/rootfs
    • 移除 Live CD/USB 或重启虚拟机,尝试从硬盘正常启动系统。

1.3 chroot 的限制

chroot 并非一个完美的沙箱,它存在一些安全限制:

  • 不能完全隔离 Root 用户: 如果 chroot 环境内的进程拥有 root 权限,它可以通过某些技术(如重新挂载文件系统、访问 /dev 下的特殊设备文件)来“逃逸”出 chroot jail
  • 不提供资源限制: chroot 不限制 CPU、内存或网络使用。
  • 依赖于主机内核: 内部进程使用的仍然是宿主系统的内核,不能模拟不同的内核版本或补丁。
  • 因此,对于更严格的隔离,通常会使用 容器技术 (如 Docker, LXC)虚拟机

2. initrd (initramfs) 实战:定制早期启动环境

我们知道 initramfs (或旧称 initrd) 是一个临时的根文件系统,在内核完全启动前提供必要的驱动和工具。实战中,我们有时需要自定义 initramfs,例如添加特定的驱动、调试工具或在启动早期执行自定义脚本。

2.1 initramfs 的常见修改场景

  • 添加特定硬件驱动: 当您的系统使用不常见或最新的硬件(如某些 NVMe 硬盘、RAID 控制器)而标准 initramfs 不包含其驱动时。
  • 加密根分区: 需要在启动早期输入密码解密根分区时,相关工具(如 cryptsetup)必须包含在 initramfs 中。
  • 网络启动(PXE): 如果根文件系统通过网络(如 NFS)挂载,initramfs 必须包含网络驱动和相关工具。
  • 调试和救援:initramfs 中嵌入调试 shell 或工具,以便在系统启动失败时进行早期故障诊断。

2.2 定制 initramfs 实战(以添加调试 Shell 为例)

我们将以 Ubuntu/Debian 为例,演示如何解包、修改并重新打包 initramfs,使其在启动失败时自动提供一个简单的 shell。

预备知识:

  • initramfs 文件通常位于 /boot/initrd.img-$(uname -r)
  • 它实际上是一个 CPIO 归档文件,通常使用 gzipxz 压缩。

操作步骤:

  1. 准备工作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 进入 /boot 目录
    cd /boot
    # 确定当前使用的 initramfs 文件名
    ls -lh initrd.img-*
    # 假设是 initrd.img-6.5.0-27-generic
    INITRAMFS_FILE="initrd.img-6.5.0-27-generic"

    # 创建一个临时目录来解压 initramfs
    mkdir /tmp/initramfs_tmp
    cd /tmp/initramfs_tmp

    # 将 initramfs 文件复制到临时目录并解压
    # 注意:initramfs 可能是 gzip 或 xz 压缩的
    # 对于 gzip 压缩的 initramfs:
    sudo cp /boot/$INITRAMFS_FILE .
    gunzip -c $INITRAMFS_FILE | cpio -idmv
    • 如果解压失败,可能是 xz 压缩。尝试:

      1
      2
      3
      # 对于 xz 压缩的 initramfs (常见于较新的系统)
      sudo cp /boot/$INITRAMFS_FILE .
      xzcat $INITRAMFS_FILE | cpio -idmv
  2. 修改 initramfs 内容:

    • 解压后,您会看到 initramfs 的内部结构,其中包含了 bin, sbin, etc, lib 等目录,以及最重要的 /init 脚本。

    • 为了在启动失败时进入调试 shell,我们可以修改 /init 脚本。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      # 备份原始的 init 脚本
      mv init init.orig

      # 创建一个新的 init 脚本(一个简单的调试 shell)
      # 这里只是一个示例,更复杂的调试可以添加更多逻辑
      sudo tee init << EOF
      #!/bin/sh
      # Original init script will be called after basic setup

      echo "Entering custom initramfs shell..."
      echo "Trying to find and mount real rootfs..."

      # 这里可以添加你自己的调试逻辑,例如:
      # - 打印环境变量
      # - 检查特定文件是否存在
      # - 运行 fsck
      # - 尝试手动挂载分区

      # 如果需要,提供一个临时shell以便手动调试
      # 在实际情况中,你可能需要确保 /bin/sh 或 /bin/bash 存在于 initramfs 中
      # 并且所有依赖的库都在 /lib 或 /lib64 中
      /bin/sh

      # 如果 shell 退出,尝试调用原始的 init 脚本
      # 确保 init.orig 存在
      if [ -x init.orig ]; then
      exec init.orig "$@"
      else
      echo "Original init script not found!"
      echo "Failed to continue boot. Dropping to a rescue shell."
      exec /bin/sh
      fi
      EOF
      # 赋予执行权限
      chmod +x init
    • 注意: 确保 /bin/sh 以及其所需的库(可以通过 ldd /bin/sh 查看)都存在于 initramfs 的相应目录中。通常,发行版的默认 initramfs 已经包含了这些。

  3. 重新打包 initramfs

    • 回到 /tmp/initramfs_tmp 目录,并重新打包。

      1
      2
      3
      4
      5
      6
      # 使用 find 命令列出所有文件,然后通过 cpio 管道打包
      # 对于 gzip 压缩:
      find . -print0 | cpio --null -ov --format=newc | gzip -9 > /boot/initrd.img-custom-$(uname -r)

      # 对于 xz 压缩:
      # find . -print0 | cpio --null -ov --format=newc | xz -9 --check=crc32 > /boot/initrd.img-custom-$(uname -r)
    • 重要: 将新的 initramfs 文件命名为不同的名称,以免覆盖原始文件。例如 initrd.img-custom-$(uname -r)

  4. 更新 GRUB 配置:

    • 现在需要告诉 GRUB 使用新的 initramfs 文件。

    • 对于 Debian/Ubuntu:

      1
      sudo update-grub

      update-grub 会扫描 /boot 目录并自动添加到 GRUB 菜单中。通常会创建一个新的启动项。

    • 对于 RHEL/CentOS:

      1
      sudo grub2-mkconfig -o /boot/grub2/grub.cfg

      您可能需要手动编辑 /etc/grub.d/40_custom/boot/grub2/grub.cfg(不推荐直接编辑 grub.cfg)来添加一个新的启动项,指向您的自定义 initramfs

  5. 测试:

    • 重启系统,在 GRUB 菜单中选择您的新自定义 initramfs 启动项。
    • 如果一切顺利,您应该会看到您添加的“Entering custom initramfs shell…”消息,然后进入一个 shell。
    • 退出 shell 后,系统应该会尝试继续引导。

注意: 修改 initramfs 具有一定的风险,如果操作不当可能导致系统无法启动。在生产环境操作前务必进行充分测试和备份。


3. 救援模式实战:系统故障排除利器

救援模式(Rescue Mode) 是 Linux 系统在启动出现严重问题时,提供一个最小化的运行环境,以便管理员进行故障诊断和修复。进入救援模式的方法因发行版而异,但核心思想是提供一个可操作的 shell 环境,通常会尝试自动挂载系统的根文件系统。

3.1 救援模式的进入方法(以 CentOS/RHEL 8/9 为例)

  1. 从安装介质启动:

    • 将 CentOS/RHEL 8/9 的安装 ISO 挂载到虚拟机或插入物理机。
    • 重启系统,从 BIOS/UEFI 设置中选择从该安装介质启动。
    • 在 GRUB 菜单中,选择 “Troubleshooting” (疑难解答)。
    • 在下一级菜单中,选择 “Rescue a Red Hat Enterprise Linux system” (救援 Red Hat Enterprise Linux 系统)。
    • 系统会开始加载救援环境。它会提示您选择语言、键盘布局,并询问是否要网络配置。
    • 最关键的一步是,它会尝试找到您硬盘上的 Linux 安装,并询问是否将它挂载到 /mnt/sysroot。选择 1 (Continue) 让系统尝试自动挂载。
    • 如果挂载成功,系统会提示您已将您的系统挂载到 /mnt/sysroot,并且您可以键入 chroot /mnt/sysroot 来进入您的系统。
  2. 通过修改 GRUB 启动参数进入紧急模式(Emergency Mode):

    • 这适用于系统可以显示 GRUB 菜单但无法完全启动到图形界面或多用户模式的情况。

    • 在 GRUB 启动菜单中,选择您要启动的 Linux 内核版本,然后按下 e 键进入编辑模式。

    • 找到以 linuxlinuxefi 开头的行。

    • 在这行的末尾添加 systemd.unit=emergency.targetrd.break

      • systemd.unit=emergency.target:会尝试启动到 emergency.target,这是一个更高级的救援模式,通常会挂载根文件系统并提供一个只读的 shell。
      • rd.break:会中断 initramfs 的启动过程,在真正的根文件系统挂载之前提供一个 initramfs shell。这对于调试 initramfs 自身问题非常有用。
    • 按下 Ctrl+XF10 启动。

    • 如果使用 rd.break 您将获得一个 switch_root:/# 提示符的 shell。此时根文件系统尚未挂载。您需要手动挂载:

      1
      2
      3
      4
      # Remount rootfs as read-write
      mount -o remount,rw /sysroot
      # Chroot into the actual system
      chroot /sysroot

      之后您可以执行修复操作。

3.2 救援模式下的常见修复操作

一旦进入救援模式并 chroot 到您的实际系统后,您可以执行之前在 chroot 部分提到的所有修复操作。以下是一些常见场景:

  • 修复 GRUB 引导器: (如前所述)

    • grub-install /dev/sda
    • update-grub / grub2-mkconfig
  • 重置 Root 用户密码:

    • passwd root

    • 如果您的系统使用了 SELinux,在修改密码后,为了确保下次启动时 SELinux 上下文正确,可能还需要创建 /.autorelabel 文件:

      Bash

      1
      touch /.autorelabel

      系统重启时会进行 SELinux 重新标记,这可能需要一些时间。

  • 修复 /etc/fstab 错误:

    • 如果 /etc/fstab 文件中有错误的条目导致系统无法启动,您可以编辑它并纠正错误。
    • vim /etc/fstab
    • 或者直接注释掉可疑的行。
  • 文件系统检查与修复:

    • 如果磁盘分区损坏,可能需要在挂载前对分区进行文件系统检查。
    • 首先退出 chroot 环境(或在救援模式 shell 中不 chroot),确保分区未被挂载。
    • fsck -y /dev/sdaX (将 sdaX 替换为实际分区,-y 选项自动回答 “yes” 以修复问题)。
  • 修复损坏的软件包:

    • 进入 chroot 后,您可以尝试重新安装或修复损坏的软件包。
    • Debian/Ubuntu: apt update && apt install --reinstall package_name
    • RHEL/CentOS: yum reinstall package_namednf reinstall package_name
  • 内核相关问题:

    • 如果系统无法启动是由于内核问题,可以在 chroot 环境中尝试安装一个新的内核版本或回滚到旧的可用内核。
    • apt install linux-image-generic (Ubuntu)
    • yum install kernel (RHEL/CentOS)
    • 安装后别忘了更新 GRUB:update-grubgrub2-mkconfig

3.3 退出救援模式

  • 完成修复后,输入 exit 退出 chroot 环境(如果进入了)。
  • 然后再次输入 exitreboot 命令来重启系统。
  • 确保移除 Live CD/USB 或取消挂载安装 ISO。

总结:

chrootinitrd (initramfs) 和救援模式是 Linux 系统管理员解决启动故障、进行系统维护和构建隔离环境的强大工具。chroot 允许您在一个损坏的系统上工作,就像它正常运行一样;initramfs 提供了早期启动的灵活性;而救援模式则为进入这些修复环境提供了一个安全可靠的途径。熟练掌握这些技术,将使您在面对复杂的 Linux 系统问题时游刃有余。