如果你手头有一台 CentOS Stream 9 服务器,某天突然收到根分区使用率 97% 的告警,日志写不进去、服务开始报错、连 SSH 都卡顿,而生产环境又不可能说重启就重启——这时候你需要的正是 LVM 在线扩容。
基于 LVM 的逻辑卷管理,CentOS Stream 9 的根分区可以在系统不重启、服务不停机的情况下直接扩大,全程只需要几条命令。这篇教程就是我从实际运维里整理出来的完整流程,从原理到命令、从判断到避坑全都写清楚了。操作步骤不复杂,不需要多深的 Linux 功底,第一次做扩容的运维新手可以照着敲,抢修现场的技术同学也可以直接拿来当手册用。
先说明一下:教程里的命令我都按真实场景验证过,示例环境里 VG 名是 cs、根 LV 路径是 /dev/cs/root,但这只是我机器的命名。你机器上的 VG 名可能是 centos、rhel 或者其他什么,一切以你自己的 vgs、lvs 输出为准,不要照抄名字。
1. 扩容前先理清三件事:根分区为什么会满、LVM 凭什么能在线扩
1.1 根分区写满的典型场景与后果
根分区是 Linux 系统里最容易被塞满的地方,而且往往是在你毫无防备的时候出问题。我接手过的故障里,最常见的几种情况是:
- 业务日志和系统日志持续增长,比如 /var/log 下的 messages、journal 文件没有配置轮转,几天就能吃掉十几 GB。
- 容器镜像和容器数据堆积,Docker/containerd 的默认数据目录通常就在 /var/lib/docker 或 /var/lib/containers,镜像拉多了、日志没清理,空间很快就没了。
- 内核更新和软件包缓存,dnf 升级残留的旧内核、缓存包都占用 /boot 和 /usr 所在分区的空间。
- 数据库、缓存文件直接写在根分区上,比如 MySQL 的 datadir 没有单独规划盘,随着业务增长把根分区填满。
- 临时文件,/tmp 下有程序异常生成的大文件,文件被进程占用删不掉,肉眼很难发现。
根分区写满的后果不只是"不能写文件"这么简单。系统很多核心功能依赖 / 目录下的可写空间,比如 /run 下的 socket 文件、/tmp 下的临时文件、/var 下的锁文件。一旦满了,可能会出现进程崩溃、数据库拒绝写入、SSH 登录后无法创建历史记录文件、cron 任务静默失败等一堆看起来毫无关联的诡异问题。
所以遇到根分区快满的时候,最优解不是急着删文件(你根本不知道哪些能删),而是先把容量扩上去,让系统恢复健康,之后再从容地排查是什么吃掉了空间。
1.2 LVM 的三层抽象:PV、VG、LV 一次讲透
想理解 LVM 在线扩容,必须先把它的三层结构搞清楚。LVM(Logical Volume Manager,逻辑卷管理)把磁盘管理分成了三层:
- PV(Physical Volume,物理卷):可以是一块整盘,也可以是一个分区。它就代表"物理上真实存在的存储空间"。
- VG(Volume Group,卷组):由一块或多块 PV 组成的一个"容量池"。PV 可以随时加入 VG,所以 VG 的容量可以动态变大。
- LV(Logical Volume,逻辑卷):从 VG 这个容量池里划分出来的"虚拟磁盘"。系统真正格式化、挂载、写入数据的是 LV。根分区在 LVM 场景下就是一个 LV。
用日常的东西类比:PV 是你买回来的一桶桶水,VG 是你家的蓄水池,LV 是从水池接出来的一根水管。水池里的水可以随时从外面提桶加进来,水管也可以随时换更粗的。对使用水的家电来说,它只看到水管出口的出水量,完全不知道水池扩容这件事——这就是在线扩容的神奇之处。
CentOS Stream 9 默认安装的时候,如果选的是自动分区方案,根分区通常就在 LVM 上:/boot 是独立分区,根目录挂载在一个 LV 上。这也就意味着,只要你当初没有手动改过分区方式,大概率可以直接用 LVM 在线扩容。
1.3 在线扩容的前提、边界与安全底线
LVM 在线扩容不是所有场景都能用的,动手之前要先确认三个前提:
- 根分区必须是由 LVM 管理的。怎么确认?执行 lsblk,如果根目录对应的设备路径是 /dev/mapper/xxx 或者 lvm 类型,就说明是 LVM。如果根分区是普通分区(比如 /dev/sda2 直接挂载到 /),那这套流程用不了,只能通过其他方式处理,比如用 parted 调整分区(风险更高)或者迁移数据。
- 要扩容的 LV 所在 VG 有足够的可用空间,或者你有办法补充新磁盘、扩展原磁盘的容量。这是扩容的"粮草"。
- 当前文件系统支持在线扩容。CentOS Stream 9 默认根分区是 XFS,XFS 完全支持在线扩展;如果是 ext4 也支持在线扩展。要注意的是,XFS 只能扩大不能缩小,所以扩容操作本身安全,但千万不要想着用 LVM 缩小 XFS 根分区。
安全底线方面,我的习惯是:任何磁盘操作之前,先做一份云平台快照,或者至少确认这台机器有最近的备份。lvextend 本身是相当安全、成熟的元数据操作,但它毕竟是动底层存储的东西,花几分钟打个快照能让你在操作失误时全身而退。另外,操作尽量放在业务低峰期,虽然在线扩容对服务影响很小,但逻辑卷元数据和文件系统增长会有瞬时 IO 负载。
2. 体检环节:四组命令看清根分区的真实家底
2.1 第一步先看清楚:当前根分区的用量与挂载情况
不管多急着扩容,动手前都要先做一次"体检"。我通常按固定顺序跑以下几组命令:
首先看根分区的使用率和文件系统类型:
df -hT /这个命令的 T 参数很关键,它会显示文件系统类型。输出里能看到挂载点是 / 的设备,比如 /dev/mapper/cs-root,Type 是 xfs。记住这个类型,后面决定用哪个扩容命令。
然后再看整体磁盘结构和挂载关系:
lsblklsblk 会以树形结构显示所有块设备。一个典型的输出长这样:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 60G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 59G 0 part └─cs-root 253:0 0 40G 0 lvm /看到 cs-root 这一行挂载在 /,说明根分区确实是 LV。同时注意 sda2 分区大小是 59G,而 LV root 只有 40G——也就是说卷组里很可能还有空闲空间,这就是我们在线扩容的底气。
2.2 LVM 家底盘点:vgs、lvs、pvs 三个命令怎么读
接下来用 LVM 自带的三个命令看家族明细:
vgs lvs pvs三个命令分别对应查看卷组、逻辑卷、物理卷的概况。输出示例:
# vgs VG #PV #LV #SN Attr VSize VFree cs 1 1 0 wz--n- 58.99g <19.00g # lvs LV VG Attr LSize Pool Origin Data% Meta% root cs -wi-ao---- 40.00g这里的 VFree 列是 19.00g,表示卷组 cs 里还有 19GB 没分配的空间。你现在要做的判断很简单:
- VFree 有空间,直接给根 LV 扩容。
- VFree 是 0 或者不够用,就需要先给 VG 增加容量(见后面第 4 章)。
如果觉得 vgs 输出不够详细,可以用 vgdisplay 看更完整的信息,比如 PE 大小和空闲 PE 数量。vgdisplay cs里会看到 Free PE / Total PE,PE 是 LVM 分配空间的最小单位,默认一般是 4MiB。知道 PE 大小有助于理解 lvextend 用 -l 指定数量时的换算关系。
2.3 判断扩容路径与文件系统类型,避免选错命令
体检做完,其实已经把"走哪条路"想清楚了:
- 如果 lsblk 显示根分区是 LV 类型,且 vgs 显示 VFree 有空间,走"直接扩 LV"这条路,对应第 3 章。
- 如果 vgs 显示 VFree 已经耗尽,但磁盘本身还有未分配空间(比如 sda 是 100G,但 sda2 分区只有 60G),可以走"扩展分区 + pvresize"这条路,对应 4.2 节。
- 如果机器上还有一块新加的空白磁盘,走"新盘建 PV 并入 VG"这条路,对应 4.1 节。
- 文件系统类型决定了最后的 grow 命令:CentOS Stream 9 默认是 XFS,用 xfs_growfs;如果当初手选成了 ext4,用 resize2fs。用 lvextend -r 的话可以自动识别,但手动操作时必须分清。
一句话总结:扩容的本质是"先在 VG 层把水加够,再在 LV 层把水管加粗,最后让文件系统感知到新容量"。每一步都有对应命令,缺一不可。
3. 主流程第一步:VG 有富余时直接在线扩 LV
3.1 推荐做法:lvextend -r 一步完成 LV 与文件系统扩容
体检确认 VG 还有 19G 空闲之后,扩容命令其实就一行。先看根 LV 的名字,比如 /dev/cs/root,然后执行:
lvextend -r -l +100%FREE /dev/cs/root这条命令里:
- -r 是 --resizefs 的简写,含义是"扩展逻辑卷之后,自动扩展文件系统",一步到位。
- -l +100%FREE 表示把卷组里所有剩余空闲空间全部分配给这个 LV。注意这个百分比是相对于 VG 的空闲空间,不是 LV 当前大小。
执行后你会看到类似这样的输出:
Size of logical volume cs/root changed from 40.00 GiB (10240 extents) to 59.00 GiB (15104 extents). Logical volume cs/root successfully resized. File system xfs found on cs/root, mounted at / File system size changed from 40.00 GiB to 59.00 GiB看到 "File system size changed" 这一行,说明文件系统也自动扩展成功了。整个过程不需要重启,也不需要卸载根分区,服务全程在线。这就是 LVM 在线扩容最舒服的地方。
如果你不想把 VG 里的空间全给根分区,想留一点余量给后续建其他 LV 用,可以改成指定增量大小,比如:
lvextend -r -L +15G /dev/cs/root-L 后面跟的是具体大小,+15G 表示在原有基础上增加 15GB,VG 里还会剩 4G 空间。习惯上我更推荐有明确容量需求时用 -L,想快速救急时用 -l +100%FREE。
3.2 手动路线:lvextend 之后 xfs_growfs 与 resize2fs 该怎么选
虽然 -r 参数很方便,我还是要单独讲一下手动路线,因为实际运维中你总会遇到 -r 不好使的时候(比如宿主机 lvm2 版本较老、或者文件系统工具缺失)。
手动路线分两步。第一步同样扩 LV:
lvextend -L +19G /dev/cs/root注意这里不加 -r,LV 扩容后文件系统大小不变。此时你执行 df -hT 会看到容量没有任何变化,这是正常的,因为文件系统还没有感知新空间。
第二步根据文件系统类型选命令。前面 df -hT 已经确认了根分区是 XFS,执行:
xfs_growfs /xfs_growfs 的参数是挂载点,不是设备路径。对于根分区,直接写 / 就够了。它会扫描挂载在 / 上的 XFS 文件系统并把容量扩展到设备实际大小,输出里会出现data blocks changed from ... to ...,看到这行就说明扩展生效。
如果你当初把 / 做成了 ext4,则用:
resize2fs /dev/cs/rootresize2fs 的参数是设备路径,和 xfs_growfs 恰好相反。它同样支持在线扩展 ext4,输出类似Resizing the filesystem on /dev/cs/root to 12345678 (4k) blocks.。
新手最容易犯的错就是把命令和文件系统搞混:XFS 用 resize2fs 会直接报错,ext4 用 xfs_growfs 也会告诉你找不到 XFS 文件系统。所以每次扩容前,用 df -hT / 看一眼类型,几秒钟的事。
3.3 扩容完成的验证与输出解读
扩容不是执行完命令就收工,验证环节必须做。我一般按下面的顺序确认:
df -hT / vgs lvsdf 看使用率降没降,vgs 看 VFree 是否归零(取决于你用了哪种扩容方式),lvs 看 LV 新大小。还是用前面的例子,扩容前后对比大概是这样:
| 检查项 | 扩容前 | 扩容后 |
|---|---|---|
| / 文件系统大小 | 40G | 59G |
| / 使用率 | 98% | 66% 左右 |
| VG 空闲 | 19G | 0(如果用了 +100%FREE) |
| LV 大小 | 40G | 59G |
如果 df 显示容量没变,请回到 3.2 节手动执行 xfs_growfs / 或者 resize2fs。我见过很多同事扩完 LV 忘了扩文件系统,隔天一脸懵地来问我"为什么 lvextend 没生效",其实命令本身没有任何问题。
另外一个小细节:执行 lvextend 时输出的 extents 数字,是和 VG 的 PE 大小挂钩的。默认 PE 是 4MiB,所以 1GB 大约是 256 个 PE。看到 10240 extents 对应 40GB、15104 extents 对应 59GB,换算一下能帮你快速判断 LVM 元数据层面是否真的按预期分配了。
4. VG 不够用了怎么办:新增磁盘与云盘扩容两条路径
4.1 新增数据盘并入 VG:从识别磁盘到 vgextend 的完整命令链
VFree 是 0 的时候,第一步先让 VG 变大。最常见的做法是给机器加一块新磁盘,把整块盘或盘上分区变成 PV,再并入 VG。
以云环境为例,给实例新挂载一块 100G 数据盘后,先确认系统识别到了:
lsblk如果看到类似 /dev/sdb 这样的新设备,大小 100G,且没有挂载点,就可以直接拿它做 PV。我个人的偏好是云盘直接用整块盘做 PV,不建分区表,省事且性能无损。执行:
pvcreate /dev/sdb vgextend cs /dev/sdbpvcreate 是把这块盘变成物理卷。vgextend 是把它并入名为 cs 的卷组。成功后能看到Volume group "cs" successfully extended。再跑一遍 vgs,VFree 应该多了 100G。
如果你更习惯分区方式,或者要加进 VG 的盘将来可能还有其他用途,那就先进 fdisk 建分区:
fdisk /dev/sdb交互式操作里依次按 n(新建分区)、p(主分区)、回车(分区号默认 1)、回车两次(扇区默认)、t(修改类型)、输入 8e(Linux LVM)、w(写入)。之后刷新分区表并创建 PV:
partprobe /dev/sdb pvcreate /dev/sdb1 vgextend cs /dev/sdb1整块盘和分区两种路径殊途同归,最终效果都是给 VG 增加容量。之后回到第 3 章的 lvextend 流程,把空间分给根分区即可。
4.2 云控制台原地扩容系统盘:growpart 配合 pvresize 的经典流程
除了加新盘,云环境里还有一种很常见的场景:直接扩容已有系统盘。比如最初系统盘 60G,你在控制台把它扩到了 200G。这种场景下磁盘设备不会变成新的 /dev/sdb,而是 /dev/sda 本身变大,但分区和 PV 还停留在原来的 60G 尺寸,需要分三步把"增量空间"一层层认进来。
第一步,确认内核已经识别到新容量:
lsblk正常情况下 sda 会显示 200G,但 sda1、sda2 分区还是原大小。如果 sda 都还是 60G,说明内核还没刷新磁盘容量,执行 4.3 里的 SCSI 扫描或重启。
第二步,扩展分区。假设根 PV 在 /dev/sda2 上,先用 growpart 把分区扩到磁盘末尾:
dnf install -y cloud-utils-growpart growpart /dev/sda 2 partprobe /dev/sdagrowpart 后面第一个参数是磁盘设备,第二个是分区号。运行结束后 sda2 分区会占满整个 sda 的剩余空间。
第三步,扩展 PV:
pvresize /dev/sda2pvresize 的作用是让 PV 使用扩容后的整个分区空间。执行完可以用 pvs 对比前后 Size。如果 PV 当初是直接建在整块盘上的(比如 PV 就是 /dev/sda),跳过 growpart,直接执行 pvresize /dev/sda 即可。
到这里 VG 的容量已经变大,vgs 应该能看到 VSize 和 VFree 都增加了。剩下的老套路,lvextend -r 或手动 xfs_growfs /。
这套流程在阿里云这类云平台上是通用的。只要底层是 virtio 等虚拟化设备且系统引导没问题,CentOS Stream 9 都能支持热扩容后在线完成上述操作。不同点只在于控制台触发扩容后,内核刷新磁盘容量可能在几秒到几分钟内完成,少数情况需要重启一次。
4.3 新盘识别不出来时的排查顺序
加新盘后 lsblk 看不到设备,这是大家在云环境里问得最多的问题。我的排查顺序固定如下:
- 先看 /proc/partitions:
cat /proc/partitions如果这里能看到 sdb,说明内核已经识别,只是 lsblk 输出可能没刷新,执行 udevadm settle 再看一次。
- 如果是传统 SCSI 虚拟磁盘,可以触发一次总线重新扫描:
for host in /sys/class/scsi_host/host*; do echo "- - -" > $host/scan; done执行后立即 lsblk 检查。
- 看 dmesg 日志:
dmesg | grep -i "sd\|virtio\|sdb"能看到磁盘设备相关日志,比如 sdb 是否被发现、是否有 IO 错误。
- 如果以上都不行,在业务窗口允许的情况下重启一次。云平台控制台挂载的磁盘在大多数虚拟化环境里支持热插拔识别,但极少数老型号虚拟化平台对热添加支持不彻底,重启是最干脆的兜底方案。
排查的时候不要急,一块磁盘没有出现,无非是链路问题、驱动问题、平台问题三类,一层层确认就好。最忌讳的是 lsblk 没看到新盘就反复执行 pvcreate,那样只会得到Device /dev/sdb not found的报错。
5. 扩容实操中的翻车现场与保险措施
5.1 最容易踩的雷:设备名看错、类型选错、XFS 不可缩
扩容本身不复杂,但我接手过的"扩容事故"几乎都集中在三个点上。
第一个是设备名看错。pvextend、pvcreate 都是不可逆或者很难逆转的操作,如果你机器上有 /dev/sda(系统盘)和 /dev/sdb(新数据盘),手一抖把 pvcreate /dev/sda 敲下去,系统盘直接变成 PV,引导和数据都可能出问题。我的习惯是操作前先用 lsblk -o NAME,SIZE,MODEL,SERIAL 对一遍设备和盘的大小、型号,确认哪个是目标盘。
第二个是文件系统类型选错。XFS 的根分区用 resize2fs 是无效的,会提示 superblock 不对;ext4 的根分区用 xfs_growfs 也会直接报错。扩容前用一次 df -hT / 确认类型,比事后排查省太多时间。
第三个是把 XFS 当成 ext4 一样"能缩能扩"。XFS 文件系统只支持扩容,不支持缩小。如果你哪天看到"根分区太大想缩一缩"这种需求,正确的做法是备份、重建 LV、恢复数据,而不是用 lvreduce 去缩。任何对 XFS 的 lvreduce 尝试都可能让文件系统元数据错乱,这是我在实际工作中非常忌讳的操作。
还有一个隐藏的坑:lvextend -l +100%FREE 会把卷组里所有空闲空间都给当前 LV。如果你的 VG 里还有别的 LV 需要保留扩容余地,这个命令就不合适,建议用 -L 精确指定增长量。做之前用 vgs 看一眼 VFree,明确"这些空闲我要分多少出去"。
5.2 动手前的三道保险:快照、fstab 备份、LV 路径核对
我在生产环境做任何 LVM 操作之前,都会花几分钟做三道保险,这套流程帮我避免了至少三次事故:
保险一:云平台快照。在控制台给系统盘打一份快照或镜像。快照是异步的,确保快照状态变成"已完成"再继续,否则快照可能不含最新数据。如果机器没有云平台快照能力,可以用 LVM 本身的快照功能,但对新手来说,云平台快照更直观可靠。
保险二:备份关键配置和现有布局。执行:
cp /etc/fstab /etc/fstab.bak blkid > /tmp/blkid_before_resize.txt vgs > /tmp/vgs_before_resize.txt lvs > /tmp/lvs_before_resize.txt这几行命令把你的文件系统表、设备 UUID、卷组和逻辑卷状态都留了个底。万一后续操作影响了启动,还能参照这些文件恢复。
保险三:核对 LV 路径。在 lvextend 之前,花五秒钟跑一遍 lvs 确认要扩容的 LV 路径准确。我见过有人把 /dev/cs/root 写成了 /dev/cs/boot,扩容了不该扩的卷,虽然不至于数据丢失,但违背了操作意图。
另外强烈建议把长命令放进 tmux 或 screen 会话里执行:
tmux new -s resize原因很现实:扩容期间如果 SSH 断掉,命令可能只执行一半,虽然 LVM 有恢复机制,但 tmux 能保证你的会话和命令在断连后继续跑完。这个习惯在抢修时尤其值钱。
5.3 什么时候仍然需要重启,什么时候完全不用
写到这里,把"在线扩容"和"重启"的边界说清楚,省得大家误解。
完全不用重启的操作:
- 已有 VG 空闲空间,直接 lvextend -r 扩展根 LV,全程不用重启。
- 新磁盘热插拔成功、并入 VG 后再扩根 LV,全程不用重启。
- 云盘控制台扩容后,内核已经识别到新容量,且通过分区扩展 + pvresize 完成空间认领,全程不用重启。
可能需要重启的情况:
- 云盘控制台扩容后,lsblk 迟迟不显示新容量,所有 SCSI 扫描手段都无效,此时需要重启一次让内核重新读取磁盘容量。
- 你操作的是非 LVM 分区(比如 /boot 所在分区),那不在本文讨论范围,且通常需要更复杂的处理。
- 误操作改了 /etc/fstab、/boot 下文件等启动相关内容,这时机器会异常,需要尽快修复并重启验证。
一句话总结:LVM 在线扩容的绝大多数场景都不需要重启,而且这正是它作为生产环境根分区方案的核心价值。
最后分享一点个人习惯:扩容完成后,我会顺手把根分区使用率降到安全范围以内,但真正的收尾是排查"空间为什么会被占满"。常见做法是 du -sh /var/log /var/lib /home 等目录,找出空间大户,配置日志轮转、设置容器日志大小限制。毕竟扩容只是治标,把容量规划和日志治理做好才是治本。这套组合下来,CentOS Stream 9 的根分区基本上就很难再给你惹麻烦了。