我发现自己需要重新排列系统分区,以将之前根文件系统下的数据移动到专用安装点。这些卷都位于 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 身份运行。
确保系统处于稳定状态
确保没有其他人在使用它,并且没有其他重要的事情发生。停止像 httpd 或 ftpd 这样的服务提供单元可能是个好主意,只是为了确保外部连接不会破坏中间的事情。
systemctl stop httpd systemctl stop nfs-server # and so on....
确保您已
lsof
安装 (lsof -v
)。并且fuser
(fuser -V
) 也已安装(Debian/Ubuntu 软件包:)psmisc
。卸载所有未使用的文件系统
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...
创建临时根 注意:如果 /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
)等等。这是有意为之的,因为它鼓励人们不要在这种临时操纵的根系统中停留太久。此时,您还应该确保安装了所有必要的软件,因为它肯定会破坏包管理器。浏览所有步骤,并确保您拥有必要的可执行文件。
转入根部
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
,因此无法让正在运行的进程关闭它。确保远程访问在转换中幸存下来
systemctl restart sshd systemctl status sshd
重新启动 sshd 后,通过打开另一个终端并通过 ssh 再次连接到计算机,确保您可以进入。如果不能,请先解决问题,然后再继续。
确认可以再次连接后,退出当前使用的 shell 并重新连接。这允许剩余的分叉
sshd
退出并确保新的分叉不会持有/oldroot
。关闭仍使用旧根的所有内容
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
卸载旧根
umount /oldroot
此时,您可以执行所需的任何操作。最初的问题需要一个简单的
resize2fs
调用,但你可以在这里做任何你想做的事情;另一种用例是将根文件系统从简单分区传输到 LVM/RAID/其他分区。将根向后旋转
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 的直接反转。
处理临时根
重复步骤 5 和 6,但使用
/tmp/tmproot
代替/oldroot
。然后:umount /tmp/tmproot rmdir /tmp/tmproot
由于它是 tmpfs,此时临时根会溶解到以太中,再也不会出现。
把东西放回原位
再次挂载文件系统:
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
答案3
我在 DigitalOcean VPS 上做了什么(以 root 身份安装了 25G /dev/vda1):
- 使用fdisk,删除第一个(25G)分区
- 创建一个20G的分区(第一个扇区必须与原来的扇区相同)
- 创建一个新分区(显然大小为 5G),它将是 /dev/vda2
- 写入分区表并退出 fdisk。
- 在 /dev/vda2 上创建 ext4 文件系统
- 记下两个分区的 UUID (ls -l /dev/disk/by-uuid)
- 将 /boot/grub/grub.cfg 和 /etc/fstab 中的 /dev/vda1 UUID 替换为 /dev/vda2 UUID
- 将 /dev/vda2 挂载到 /mnt
- 将所有内容(除了 dev、proc、run、sys、mnt)从 / 复制到 /mnt
- 跑步
update-grub
reboot
并祈祷
当我被告知不支持 resize2fs 时,上述方法效果很好。