Featured image of post 根分区用到 92% 那天,我把 LVM 扩容的流程走了一遍

根分区用到 92% 那天,我把 LVM 扩容的流程走了一遍

从分区扩容到 pvresize、lvextend,再到 ext4 和 xfs 各自的文件系统扩法,把每一步的命令和验证都记下来。

上个月收到监控告警,说根分区使用率 92%。

登录上去 df -h 一看,四十个 G 的盘只剩三个多 G。du -sh /* 挨个看,/var/lib/docker 占了大头,还有一堆构建缓存。

清了一轮,回到 60%。但我知道这只是暂时的,服务还在长,早晚得扩。

还好当初装系统的时候选了 LVM。要是普通分区,扩容就得停机、进救援模式、用 fdisk 调分区,风险大得多。

先理清楚 LVM 的三层

很多人被 LVM 绕晕,是因为它的名字太多。其实就三层。

物理层是 PV,Physical Volume,一块硬盘或者一个分区,初始化之后交给 LVM 管。

中间层是 VG,Volume Group,一个池子,把多个 PV 合成一个大的存储空间。

上层是 LV,Logical Volume,从池子里划出来的一块,格式化之后挂载使用。

扩容的时候,顺序是从下往上。先让 PV 变大,VG 才会跟着变大,然后才能扩 LV,最后扩文件系统。

搞反了就白做。

看现在的状态

1
lsblk

输出大概是这样:

1
2
3
4
5
6
7
8
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda             254:0    0   40G  0 disk
├─vda1          254:1    0    1M  0 part
├─vda2          254:2    0  512M  0 part /boot/efi
└─vda3          254:3    0 39.5G  0 part
  ├─vg0-root    253:0    0   20G  0 lvm  /
  ├─vg0-swap    253:1    0    2G  0 lvm  [SWAP]
  └─vg0-data    253:2    0 17.5G  0 lvm  /data

这块盘 40G,vda3 是 LVM 的物理分区,占了 39.5G。里面划了三个 LV。

问题是我这块盘本来就只有 40G,没有剩余空间。所以要扩,得先给虚拟机加盘。

加盘

在云控制台或者虚拟化平台里,把磁盘从 40G 调到 80G。这一步在云厂商那边点一下就行,不用停机。

加完之后,虚拟机里 lsblk 还是显示 40G,因为内核还没重新读分区表。

1
2
lsblk
# vda 显示 80G,但 vda3 还是 39.5G

这就是关键的一步。vda 本身变大了,但 vda3 这个分区没变。

扩分区

有两种情况。

如果你用的是 GPT 分区表,而且分区后面没有别的分区,用 growpart 最省事:

1
sudo growpart /dev/vda 3

growpart 是 cloud-utils-growpart 包里的工具,大部分云镜像都预装了。

如果没装,apt install cloud-utils-growpart 或者 yum install cloud-utils-growpart。

跑完再看:

1
lsblk

vda3 应该变成 79.5G 了。

如果是 MBR 分区表,growpart 也能处理,但有个限制,主分区最多四个,而且扩展分区后面的空间不好扩。这种情况下得用 fdisk 删了重建分区,fdisk 的 d 删分区只删分区表项,不动数据,只要起始扇区没变就是安全的。不过操作的时候手要稳。

让 PV 认识新的大小

分区变大了,但 LVM 还不知道。因为 PV 里记录了自己的大小。

1
sudo pvresize /dev/vda3

这个命令会让 PV 重新读取分区的大小,然后更新自己的元数据。

验证:

1
sudo pvs

输出里的 PSize 应该变成 79.5G,PFree 是新增的空间。

如果 PFree 没变,说明 pvresize 没生效,回头检查分区是不是真的扩了。

扩 LV

1
sudo lvs

看当前的 LV 大小。

扩的时候有两种写法。

按绝对值扩到某个大小:

1
sudo lvextend -L 40G /dev/vg0/root

注意 -L 是最终大小,不是增量。如果你写 -L 40G 而当前已经是 40G,它什么都不会做。

按增量扩:

1
sudo lvextend -L +20G /dev/vg0/root

这个更直观,加二十个 G。

或者干脆把池子里的空间全用掉:

1
sudo lvextend -l +100%FREE /dev/vg0/root

注意这里是小写的 -l,后面跟百分比。大写的 -L 后面跟具体数值。这个大小写很容易看错。

扩文件系统

这一步最容易被忘。LV 变大了,但上面的文件系统还是原来的大小,df -h 看不出变化。

ext4 用:

1
sudo resize2fs /dev/vg0/root

不用给大小,它会自动扩到 LV 的边界。

xfs 用:

1
sudo xfs_growfs /

注意 xfs 的 xfs_growfs 参数是挂载点,不是设备名。写 /dev/vg0/root 会报错。

这是两个文件系统最明显的区别。ext4 可以缩,xfs 只能扩。所以如果你的盘用 xfs,规划的时候要留点余量,缩不回去。

在线扩容

好消息是,上面这些操作除了 growpart 之外,全都可以在线做,不用卸载不用停机。

ext4 从内核 3.6 开始支持在线扩容,resize2fs 会自动检测并走在线模式。xfs 一直是在线的。

我用的是 ext4,整个流程在跑着服务的机器上做完,中间没有中断。nginx 和数据库都没感觉。

不过还是建议在业务低峰做。虽然理论上是安全的,但万一 growpart 把分区表写坏了,那就是大事故。

一次完整的操作

把上面的命令串起来,加上 df 前后的对比:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 扩容前
df -h /

# 分区扩容
sudo growpart /dev/vda 3

# PV 重新读取
sudo pvresize /dev/vda3

# LV 扩容
sudo lvextend -l +100%FREE /dev/vg0/root

# 文件系统扩容
sudo resize2fs /dev/vg0/root

# 验证
df -h /

我实际跑的时候,每一步之间都 lsblk 或 pvs 确认一下。多敲几条命令,比出问题再回头找强。

几个坑

growpart 之后忘了 pvresize,这是最常见的。pvs 里 PFree 是 0,你就会以为是分区没扩成功,其实只是 LVM 不知道。

lvextend 用了 -L 但写的是增量值,比如 -L 20G 而当前已经是 20G,它什么都不做,也不报错。

xfs 用 resize2fs 会直接报错说不支持,别慌,换 xfs_growfs。

还有一个,如果你的 /boot 在 LVM 上,扩它是可以的,但 grub 那边可能需要重新生成配置。我这个机器的 /boot 是独立分区,没在 LVM 里,所以没遇到。

缩容的注意事项

扩容易,缩难。

ext4 缩容要三步。先 umount,然后 e2fsck -f 检查,再 resize2fs <设备> <目标大小>,最后 lvreduce。

顺序不能反。先缩文件系统再缩 LV。如果先缩 LV,文件系统就损坏了,数据基本找不回来。

xfs 根本不支持缩容。要用 xfs 又想缩,只能备份、重建、恢复。

所以我的建议是,宁可一开始规划得保守一点,也不要给自己留缩容的活。

磁盘规划的一点经验

装系统的时候我习惯这么分:

/boot 给 512M,独立分区,不在 LVM 里。这是为了让 grub 和系统救援都能正常工作。

swap 给内存的一到两倍,但不超过 4G。现在很多云厂商建议不给 swap,我的看法是留一点,防止突发的内存尖峰直接把进程打死。

/ 给剩下的全部。不用单独分 /var 和 /home,因为 LVM 的好处就是可以动态调,一开始分太细反而失去了灵活性。

这样分下来,如果哪天 /var/lib/docker 涨得太快,我直接扩根分区就行,不用去动别的。

现在的状态

扩到 80G 之后,根分区用了 38%,/data 那边还有空间。跑了一个多月,稳定。

顺手做了两件事。一是给 docker 加了 data-root 的迁移,把镜像目录挪到了 /data 下面,这样系统盘的压力小很多。二是加了个监控,磁盘到 85% 就推消息,不用等到 92% 才被叫醒。

LVM 这个东西,平时感觉不到它的存在,真正需要的时候才发现它的好。当初装系统多勾了一个选项,省了今天一晚上的事。

使用 Hugo 构建
主题 Stack 由 Jimmy 设计
寻 Mi 处载入中...