如何在不启动 livecd 的情况下收缩根文件系统

如何在不启动 livecd 的情况下收缩根文件系统

我发现自己需要重新排列系统分区,以将之前根文件系统下的数据移动到专用安装点。这些卷都位于 LVM 中,因此这相对容易:创建新卷,将数据移入其中,收缩根文件系统,然后在适当的点挂载新卷。

问题在于步骤 3,缩小根文件系统。涉及的文件系统是ext4,所以支持在线调整大小;但是,安装后,文件系统只能增长。缩小分区需要卸载它,这对于正常操作的根分区来说当然是不可能的。

网络上的答案似乎围绕着启动 LiveCD 或其他救援媒体,执行收缩操作,然后启动回已安装的系统。但是,相关系统是远程的,我只能通过 SSH 进行访问。我可以重新启动,但无法启动救援光盘并从控制台执行操作。

如何在保持远程 shell 访问的同时卸载根文件系统?

答案1

在解决此问题时,提供的信息位于https://www.ivarch.com/blogs/oss/2007/01/resize-a-live-root-fs-a-howto.shtml是关键。然而,该指南适用于非常旧的 RHEL 版本,并且各种信息已过时。

下面的说明是为 CentOS 7 设计的,但它们应该很容易转移到任何运行 systemd 的发行版。所有命令均以 root 身份运行。

  1. 确保系统处于稳定状态

    确保没有其他人在使用它,并且没有其他重要的事情发生。停止像 httpd 或 ftpd 这样的服务提供单元可能是个好主意,只是为了确保外部连接不会破坏中间的事情。

     systemctl stop httpd
     systemctl stop nfs-server
     # and so on....
    

    确保您已lsof安装 ( lsof -v)。并且fuser( fuser -V) 也已安装(Debian/Ubuntu 软件包:)psmisc

  2. 卸载所有未使用的文件系统

     umount -a
    

    这将为根卷本身和各种临时/系统 FS 打印许多“目标正忙”警告。这些暂时可以忽略。重要的是,除了根文件系统本身之外,磁盘上的文件系统不会保持挂载状态。验证这一点:

     # mount alone provides the info, but column makes it possible to read
     mount | column -t
    

    如果您看到任何磁盘上的文件系统仍然挂载,那么某些不应该运行的东西仍在运行。检查它正在使用什么fuser

     # if necessary:
     yum install psmisc
     # then:
     fuser -vm <mountpoint>
     systemctl stop <whatever>
     umount -a
     # repeat as required...
    
  3. 创建临时根 注意:如果 /tmp 是 / 上的目录,则在此过程中如果我们使用 /tmp/tmproot,我们将无法卸载 /。因此,可能需要使用替代安装点,例如 /tmproot。

     mkdir /tmp/tmproot
     mount -t tmpfs none /tmp/tmproot
     mkdir /tmp/tmproot/{proc,sys,dev,run,usr,var,tmp,oldroot}
     cp -ax /{bin,etc,mnt,sbin,lib,lib64} /tmp/tmproot/
     cp -ax /usr/{bin,sbin,lib,lib64} /tmp/tmproot/usr/
     cp -ax /var/{account,empty,lib,local,lock,nis,opt,preserve,run,spool,tmp,yp} /tmp/tmproot/var/
    

    这会创建一个非常小的根系统,它会破坏(除其他外)联机帮助页查看(否/usr/share)、用户级自定义(否/root/home)等等。这是有意为之的,因为它鼓励人们不要在这种临时操纵的根系统中停留太久。

    此时,您还应该确保安装了所有必要的软件,因为它肯定会破坏包管理器。浏览所有步骤,并确保您拥有必要的可执行文件。

  4. 转入根部

     mount --make-rprivate / # necessary for pivot_root to work
     pivot_root /tmp/tmproot /tmp/tmproot/oldroot
     for i in dev proc sys run; do mount --move /oldroot/$i /$i; done
    

    systemd 导致挂载默认允许子树共享(与 一样mount --make-shared),这会导致pivot_root失败。因此,我们使用 全局关闭此功能mount --make-rprivate /。系统和临时文件系统被批量移动到新的根目录中。这是使其正常工作所必需的;用于与 systemd 通信的套接字等位于 中/run,因此无法让正在运行的进程关闭它。

  5. 确保远程访问在转换中幸存下来

     systemctl restart sshd
     systemctl status sshd
    

    重新启动 sshd 后,通过打开另一个终端并通过 ssh 再次连接到计算机,确保您可以进入。如果不能,请先解决问题,然后再继续。

    确认可以再次连接后,退出当前使用的 shell 并重新连接。这允许剩余的分叉sshd退出并确保新的分叉不会持有/oldroot

  6. 关闭仍使用旧根的所有内容

     fuser -vm /oldroot
    

    这将打印仍保留旧根目录的进程列表。在我的系统上,它看起来像这样:

                  USER        PID ACCESS COMMAND
     /oldroot:    root     kernel mount /oldroot
                  root          1 ...e. systemd
                  root        549 ...e. systemd-journal
                  root        563 ...e. lvmetad
                  root        581 f..e. systemd-udevd
                  root        700 F..e. auditd
                  root        723 ...e. NetworkManager
                  root        727 ...e. irqbalance
                  root        730 F..e. tuned
                  root        736 ...e. smartd
                  root        737 F..e. rsyslogd
                  root        741 ...e. abrtd
                  chrony      742 ...e. chronyd
                  root        743 ...e. abrt-watch-log
                  libstoragemgmt    745 ...e. lsmd
                  root        746 ...e. systemd-logind
                  dbus        747 ...e. dbus-daemon
                  root        753 ..ce. atd
                  root        754 ...e. crond
                  root        770 ...e. agetty
                  polkitd     782 ...e. polkitd
                  root       1682 F.ce. master
                  postfix    1714 ..ce. qmgr
                  postfix   12658 ..ce. pickup
    

    在卸载之前,您需要处理每一个进程/oldroot。蛮力方法kill $PID对每个人来说都很简单,但这可能会破坏事情。更柔和地做到这一点:

     systemctl | grep running
    

    这将创建一个正在运行的服务的列表。您应该能够将其与持有 的进程列表关联起来/oldroot,然后systemctl restart为每个进程发出问题。有些服务将拒绝在临时根中出现并进入失败状态;这些目前并不重要。

    如果您要调整大小的根驱动器是 LVM 驱动器,您可能还需要重新启动一些其他正在运行的服务,即使它们没有显示在 . 创建的列表中fuser -vm /oldroot。由于以下错误,您可能无法在步骤 7 下调整 LVM 驱动器的大小:

     fsadm: Cannot proceed with mounted filesystem "/oldroot"
    

    您可以尝试systemctl restart systemd-udevd,如果失败,您可以使用以下命令找到剩余的安装座grep system /proc/*/mounts | column -t

    查找显示以下内容的进程mounts:none并尝试重新启动这些进程:

     PATH                      BIN                        FSTYPE
     /proc/16395/mounts:tmpfs  /run/systemd/timesync      tmpfs
     /proc/16395/mounts:none   /var/lib/systemd/timesync  tmpfs
     /proc/18485/mounts:tmpfs  /run/systemd/inhibit       tmpfs
     /proc/18485/mounts:tmpfs  /run/systemd/seats         tmpfs
     /proc/18485/mounts:tmpfs  /run/systemd/sessions      tmpfs
     /proc/18485/mounts:tmpfs  /run/systemd/shutdown      tmpfs
     /proc/18485/mounts:tmpfs  /run/systemd/users         tmpfs
     /proc/18485/mounts:none   /var/lib/systemd/linger    tmpfs
    

    有些进程无法通过简单的 来处理systemctl restart。对我来说,这些包括auditd(它不喜欢通过 被杀死systemctl,所以只需要kill -15)。这些可以单独处理。

    通常,您会发现的最后一个进程就是systemd它本身。为此,请运行systemctl daemon-reexec.

    完成后,表格应如下所示:

                  USER        PID ACCESS COMMAND
     /oldroot:    root     kernel mount /oldroot
    
  7. 卸载旧根

     umount /oldroot
    

    此时,您可以执行所需的任何操作。最初的问题需要一个简单的resize2fs调用,但你可以在这里做任何你想做的事情;另一种用例是将根文件系统从简单分区传输到 LVM/RAID/其他分区。

  8. 将根向后旋转

     mount <blockdev> /oldroot
     mount --make-rprivate / # again
     pivot_root /oldroot /oldroot/tmp/tmproot
     for i in dev proc sys run; do mount --move /tmp/tmproot/$i /$i; done
    

    这是步骤 4 的直接反转。

  9. 处理临时根

    重复步骤 5 和 6,但使用/tmp/tmproot代替/oldroot。然后:

     umount /tmp/tmproot
     rmdir /tmp/tmproot
    

    由于它是 tmpfs,此时临时根会溶解到以太中,再也不会出现。

  10. 把东西放回原位

    再次挂载文件系统:

    mount -a
    

    此时,您还应该/etc/fstab根据grub.cfg您在步骤 7 中所做的任何调整进行更新。

    重新启动所有失败的服务:

    systemctl | grep failed
    systemctl restart <whatever>
    

    再次允许共享子树:

    mount --make-rshared /
    

    启动已停止的服务单元 - 您可以使用以下单个命令:

    systemctl isolate default.target
    

你就完成了。

非常感谢 Andrew Wood 在 RHEL4 上完成了这一演变,以及 steve,他为我提供了前者的链接。

答案2

如果您确定自己在做什么 - 因此不进行实验,您可以连接到 initrd,这是一种非交互式且快速的方法。

在基于 Debian 的系统上,操作方法如下。

看代码:https://github.com/szepeviktor/debian-server-tools/blob/master/debian-setup/debian-resizefs.sh

还有一个例子:https://github.com/szepeviktor/debian-server-tools/blob/master/debian-setup/debian-convert-ext3-ext4.sh

答案3

我在 DigitalOcean VPS 上做了什么(以 root 身份安装了 25G /dev/vda1):

  1. 使用fdisk,删除第一个(25G)分区
  2. 创建一个20G的分区(第一个扇区必须与原来的扇区相同)
  3. 创建一个新分区(显然大小为 5G),它将是 /dev/vda2
  4. 写入分区表并退出 fdisk。
  5. 在 /dev/vda2 上创建 ext4 文件系统
  6. 记下两个分区的 UUID (ls -l /dev/disk/by-uuid)
  7. 将 /boot/grub/grub.cfg 和 /etc/fstab 中的 /dev/vda1 UUID 替换为 /dev/vda2 UUID
  8. 将 /dev/vda2 挂载到 /mnt
  9. 将所有内容(除了 dev、proc、run、sys、mnt)从 / 复制到 /mnt
  10. 跑步update-grub
  11. reboot并祈祷

当我被告知不支持 resize2fs 时,上述方法效果很好。

相关内容