跳转至

磁盘打满与 LVM 扩容

01 前置理论与核心机制

1. 为什么生产环境推荐使用 LVM?

  • 传统分区直挂的局限性:若物理硬盘采用传统分区格式化后直接挂载(mount),一旦磁盘空间达到 100%,通常只能停机维护、挂载新盘、全量迁移数据并修改挂载配置或业务路径,会导致较长时间的业务中断。
  • LVM 的动态存储池机制:LVM (Logical Volume Manager) 在底层物理硬盘(PV)与上层文件系统之间,抽象出了一个动态的“卷组存储池” (Volume Group, VG)。新增的物理硬盘可随时在线加入该卷组中,再从卷组中动态划分或追加空间给“逻辑卷” (Logical Volume, LV)。底层物理存储的扩展对上层应用完全透明,从而实现在线不停机扩容。

2. 在线扩容的两层刷新机制

  • 逻辑卷层扩展(块设备层):首先在 LVM 层面,将卷组(VG)中的空闲物理块(PE)划拨给目标逻辑卷(LV),使块设备容量变大。
  • 文件系统层扩展(元数据层):逻辑卷容量扩大后,上层已格式化的文件系统(如 EXT4 或 XFS)并不会自动感知。必须执行对应的文件系统扩展命令,刷新超级块(Superblock)与块组元数据,操作系统与业务进程才能真正使用新增的磁盘空间。

3. LVM 的能力边界

  • 单纯的线性 LVM 仅负责容量抽象与灵活调度,本身不具备磁盘故障冗余能力。若需防范底层物理硬盘损坏导致的数据丢失,底层仍需结合硬件 RAID(磁盘阵列)或分布式多副本存储机制。

02 故障排查与扩容闭环 SOP

当线上服务因 /data 目录空间占满(100%)导致写入阻塞时,按以下四步执行在线扩容:

步骤一:确认容量状态与新磁盘设备

核查当前挂载点容量使用率,并确认操作系统已识别到新接入的物理磁盘或云盘:

# 确认 /data 挂载点使用率已达 100% 及对应的逻辑卷名称
df -hT /data

# 检查块设备列表,确认新增磁盘的设备名 (例如 /dev/sdc)
lsblk

步骤二:初始化物理卷并并入卷组

将新磁盘初始化为 LVM 物理卷(PV),并将其合入现有的卷组(VG)中:

# 1. 初始化物理卷 (PV)
pvcreate /dev/sdc

# 2. 将新物理卷加入现有的卷组 (假设目标卷组名为 data_vg)
vgextend data_vg /dev/sdc

# 查看卷组状态,确认 VFree (可用空闲空间) 已增加
vgs

步骤三:从卷组中划拨容量给逻辑卷

从已扩充的卷组(VG)中,划拨指定容量追加给空间耗尽的逻辑卷(LV):

# -L +5G 表示在原有容量基础上增加 5G (/dev/data_vg/data_lv 为目标逻辑卷路径)
# 若需将卷组内剩余空间全部分配,可使用: lvextend -l +100%FREE /dev/data_vg/data_lv
lvextend -L +5G /dev/data_vg/data_lv

# 确认逻辑卷底层容量已增加
lvs

  • 注意:此过程无需卸载目录,原挂载点与 /etc/fstab 配置均保持不变。

步骤四:在线扩展文件系统容量 (关键步骤)

块设备空间分配完成后,必须根据文件系统的实际类型执行在线扩展命令,否则 df -h 显示的可用容量不会改变:

# 场景 A:若为 EXT3/EXT4 文件系统,参数需指定逻辑卷的设备路径
resize2fs /dev/data_vg/data_lv

# 场景 B:若为 XFS 文件系统,参数必须指定挂载点目录(新版 xfsprogs 强制要求)
xfs_growfs /data

  • 进阶技巧:在执行步骤三的 lvextend 时,也可直接携带 -r(--resizefs)参数(即 lvextend -r -L +5G /dev/data_vg/data_lv),LVM 会自动识别底层文件系统类型并同步完成文件系统刷新。
  • 验证闭环:执行完毕后,再次运行 df -h /data,确认可用空间已在线扩展,因磁盘满而阻塞的业务写入随即恢复正常。

03 真实生产故障现象与案例库

案例 1:执行 lvextend 后 df -h 显示容量未变化

  • 故障现象:运维人员收到磁盘满告警后,执行 lvextend 扩容了 100G 空间,lvs 与 lsblk 均显示逻辑卷已变大,但业务依然报错 No space left on device,df -h 显示使用率仍为 100%。
  • 排查与处置:操作人员仅完成了底层逻辑卷(块设备层)的扩充,遗漏了上层文件系统的扩展刷新步骤。通过 df -T 确认文件系统类型后,补充执行 resize2fs(或 xfs_growfs),空间立即释放生效。

案例 2:对 XFS 文件系统误用 resize2fs 导致报错

  • 故障现象:在 CentOS 7 服务器上执行 resize2fs /dev/mapper/centos-root 时,终端报错 resize2fs: Bad magic number in super-block while trying to open...。
  • 排查与处置:CentOS 7 及更高版本默认采用 XFS 文件系统,而 resize2fs 仅适用于 EXT2/EXT3/EXT4 系列文件系统。针对 XFS 必须使用专用的 xfs_growfs 命令,且传入的参数应为挂载点路径(如 xfs_growfs /)。改用正确指令后成功完成扩容。

案例 3:混合不同性能介质的物理盘引发 I/O 降级

  • 故障现象:某数据库节点的逻辑卷原先构建在 NVMe SSD 物理盘上。后续因空间不足,运维人员将一块机械硬盘(HDD)加入到同一个 VG 卷组中并对 LV 进行了扩容。
  • 结果与教训:容量虽然扩展成功,但当新增数据跨过 SSD 边界开始写入 HDD 对应的物理区域(PE)时,数据库的 I/O 延迟显著升高,吞吐量大幅下降。LVM 默认按线性顺序分配物理块,不自动区分介质性能差异,生产环境中严禁将不同性能级别(如 SSD 与 HDD)的磁盘混入同一个 VG 卷组。

04 生产排障速查表(LVM 在线扩容)

操作阶段执行命令 / 观测点动作定性操作注意事项01 状态核查lsblk / df -hT确认新增磁盘设备名、逻辑卷路径及文件系统格式仔细核对盘符,严禁误初始化存量数据盘02 PV 层pvcreate /dev/sdc将物理裸盘或分区初始化为物理卷写入 LVM 元数据头,纳管底层块设备03 VG 层vgextend /dev/sdc将新增 PV 并入目标卷组存储池执行 vgs 确认 VFree 空闲容量已增加04 LV 层lvextend -L +5G 从卷组中划拨容量给目标逻辑卷推荐使用 -l +100%FREE 分配全部剩余空间,或加 -r 自动刷新05 FS 层 (EXT)resize2fs 在线扩展 EXT3/EXT4 文件系统适用于 Ubuntu/Debian 等默认 EXT4 环境06 FS 层 (XFS)xfs_growfs <挂载点目录>在线扩展 XFS 文件系统适用于 RHEL/CentOS/Rocky 默认环境,参数为挂载目录

05 面试应答参考话术

面试官问:“为什么生产环境的 Linux 服务器通常不建议直接分区挂载物理硬盘,而是要使用 LVM?如果一块数据盘 100% 打满了,你怎么在线扩容?”

可以这样回答:如果直接对物理硬盘分区格式化并挂载,一旦磁盘空间 100% 耗尽,往往只能停机挂载新盘、全量拷贝迁移数据,甚至修改业务配置路径,停机窗口过长。引入 LVM 的核心目的是为了实现在线平滑扩容。LVM 在物理磁盘和文件系统之间增加了一层逻辑卷管理,将多块物理盘抽象为一个统一的“卷组存储池(VG)”。 当遇到线上磁盘打满需要扩容时,标准的在线处理流程分为四步:第一步:识别新盘。

在云平台或物理机挂载新硬盘后,执行 lsblk 确认系统已识别到新块设备(如 /dev/sdc),并用 df -hT 确认被打满目录对应的逻辑卷名称和文件系统类型。第二步:初始化并扩展卷组。

使用 pvcreate /dev/sdc 将新盘初始化为物理卷(PV),随后执行 vgextend 将其加入到现有的卷组(VG)中,此时存储池的总可用容量就变大了。第三步:扩展逻辑卷。

执行 lvextend 命令(例如 lvextend -L +50G 或 -l +100%FREE),将卷组里的新增空间划拨给被打满的那个逻辑卷(LV)。整个过程不需要卸载目录,业务完全无感知。第四步:刷新文件系统(关键细节)。

逻辑卷底层变大后,必须通知上层文件系统扩展元数据。如果是 EXT4 格式,就执行 resize2fs <逻辑卷路径>;如果是 XFS 格式,就执行 xfs_growfs <挂载点目录>(或者在第三步直接用 lvextend -r 自动完成刷新)。刷新完成后,df -h 的可用空间就会立即增加,业务恢复正常写入。

06 面试高频追问与应答预案

追问 1:既然 LVM 支持在线无缝扩容,那它支持在线缩容(缩小容量)吗?

  • 回答要点:
  • XFS 文件系统不支持缩容:XFS 在底层架构设计上仅支持扩展,不支持缩小(无官方缩容工具),若强行使用 lvreduce 缩小底层逻辑卷,会直接破坏 XFS 文件系统结构导致数据损坏。
  • EXT4 支持缩容,但必须离线操作:EXT4 文件系统支持缩小,但不支持在线缩容。必须先停止业务并执行 umount 卸载分区,接着依次执行 e2fsck -f 强制文件系统检查、resize2fs 先缩小文件系统边界,最后才能执行 lvreduce 缩小逻辑卷。
  • 生产规范:由于缩容操作一旦计算错边界极易截断底层数据块引发数据丢失,在生产运维规范中,通常禁止对生产逻辑卷执行缩容操作。

追问 2:lvextend -L +5G 和 lvextend -l +100%FREE 两个参数有什么区别?

  • 回答要点:
  • -L(大写)后面指定的是具体的绝对容量大小(单位支持 M、G、T)。例如 -L +5G 表示在现有容量基础上精确增加 5GB(若不加 + 号直接写 -L 5G,则代表将逻辑卷总大小设定为 5GB)。
  • -l(小写)后面指定的是 PE(Physical Extent,物理扩展块,默认单个 PE 为 4MB)的数量或百分比。生产中最常用的是 -l +100%FREE,表示将当前卷组(VG)中所有剩余的空闲空间一次性全部分配给该逻辑卷,无需人工计算剩余字节数。

07 LVM 文件系统自动识别与扩展脚本 (auto_lvm_resize.sh)

该脚本用于在执行完 lvextend 扩充逻辑卷后,自动识别目标卷的挂载点与文件系统格式(EXT4/XFS),并调用对应的扩展命令刷新容量。

#!/usr/bin/env bash
# ==============================================================================
# 脚本名称: auto_lvm_resize.sh
# 功能描述: 自动识别逻辑卷的文件系统类型 (EXT/XFS) 并执行对应的在线扩容刷新命令
# 使用方式: 在宿主机以 root 权限执行: ./auto_lvm_resize.sh
# 示例: ./auto_lvm_resize.sh /data  或  ./auto_lvm_resize.sh /dev/mapper/data_vg-data_lv
# ==============================================================================

set -o pipefail

TARGET_INPUT=$1

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
PLAIN='\033[0m'

if [ -z "$TARGET_INPUT" ]; then
    echo -e "${RED}用法: $0 ${PLAIN}"
    exit 1
fi

echo -e "${BLUE}================================================================${PLAIN}"
echo -e "${BLUE}           LVM 逻辑卷文件系统自动识别与在线扩展脚本             ${PLAIN}"
echo -e "${BLUE}================================================================${PLAIN}"

# 1. 解析目标设备路径、文件系统格式与挂载点
MOUNT_INFO=$(df -PT "$TARGET_INPUT" 2>/dev/null | awk 'NR==2 {print $1, $2, $7}')

if [ -z "$MOUNT_INFO" ]; then
    echo -e "${RED}[ERROR] 无法解析目标 ${TARGET_INPUT},请确认路径存在且处于已挂载状态。${PLAIN}"
    exit 1
fi

DEV_PATH=$(echo "$MOUNT_INFO" | awk '{print $1}')
FS_TYPE=$(echo "$MOUNT_INFO" | awk '{print $2}')
MOUNT_POINT=$(echo "$MOUNT_INFO" | awk '{print $3}')

echo -e "\n${YELLOW}[1/2] 解析挂载拓扑与文件系统类型...${PLAIN}"
echo -e "  -> 设备路径 : ${GREEN}${DEV_PATH}${PLAIN}"
echo -e "  -> 挂载目录 : ${GREEN}${MOUNT_POINT}${PLAIN}"
echo -e "  -> 文件系统 : ${YELLOW}${FS_TYPE}${PLAIN}"

# 2. 根据文件系统类型执行对应的扩展指令
echo -e "\n${YELLOW}[2/2] 正在执行文件系统容量在线刷新...${PLAIN}"

# 修复了这里的逻辑或乱码
if [[ "$FS_TYPE" == "ext2" || "$FS_TYPE" == "ext3" || "$FS_TYPE" == "ext4" ]]; then
    echo -e "  -> 识别为 EXT 系列文件系统,调用 resize2fs 工具。"
    echo -e "  -> 执行命令: ${BLUE}resize2fs ${DEV_PATH}${PLAIN}\n"
    resize2fs "$DEV_PATH"
    EXEC_STATUS=$?
elif [[ "$FS_TYPE" == "xfs" ]]; then
    echo -e "  -> 识别为 XFS 文件系统,调用 xfs_growfs 工具 (传入挂载点目录)。"
    echo -e "  -> 执行命令: ${BLUE}xfs_growfs ${MOUNT_POINT}${PLAIN}\n"
    xfs_growfs "$MOUNT_POINT"
    EXEC_STATUS=$?
else
    echo -e "  -> ${RED}[FATAL] 当前脚本不支持该文件系统类型的在线扩展: ${FS_TYPE} (仅支持 ext2/3/4 与 xfs)。${PLAIN}"
    exit 1
fi

echo -e "\n${BLUE}----------------------------------------------------------------${PLAIN}"
if [ $EXEC_STATUS -eq 0 ]; then
    echo -e "${GREEN}[SUCCESS] 文件系统容量刷新成功,当前最新空间状态如下:${PLAIN}"
    df -hT "$MOUNT_POINT" | awk 'NR==1 {print "  " $0} NR==2 {print "  \033[0;32m" $0 "\033[0m"}'
else
    echo -e "${RED}[ERROR] 文件系统容量刷新失败,请检查逻辑卷底层是否已通过 lvextend 分配新增空间。${PLAIN}"
fi
echo -e "${BLUE}================================================================${PLAIN}"