1. 为什么要在RHEL 8上折腾Stratis和VDO
RHEL 8把存储管理拆成了两条腿走路:一条是Stratis,面向本地磁盘池的"傻瓜式"卷管理;另一条是VDO,面向块设备的"压缩+去重"层。很多人第一次听到这两个词的反应是"LVM不香吗",我当初也这么想,直到一台日志服务器磁盘三天两头爆满、另一台跑虚拟化镜像的机器空间利用率低得可怜,才回头认真把这两个东西啃了一遍。
先把定位说清楚,免得走弯路。Stratis解决的是"我有一堆盘,想快速建池、建文件系统、做快照、做精简配置,但不想背LVM那一长串pvcreate/vgcreate/lvcreate命令"的问题。它在底层其实还是LVM和XFS,只是把复杂度封装起来了,对外暴露的是stratis这一套命令。VDO(Virtual Data Optimizer)解决的是另一个维度的问题:同一块物理盘,怎么塞进更多的逻辑数据。它通过零块消除、重复数据删除、压缩三层机制,把实际写入的物理空间压下来,特别适合备份、日志、虚拟化镜像这类重复率高的场景。
这两个东西不是互斥的,实际生产里经常叠着用:底层用VDO做一层空间优化,上面再跑Stratis做池化管理。但叠之前你得先搞清楚每一层在干什么,否则出了问题根本不知道是哪一层背锅。这篇就把我踩过的坑、验证过的参数、以及一套能直接照着敲的演练流程完整写出来,适合刚接触RHEL 8存储、或者想从LVM迁移到新方案的朋友。
提示:本文所有操作基于RHEL 8.x,命令在CentOS Stream 8/9、Rocky Linux 8上基本通用,但VDO在RHEL 9之后被移出了主线,如果你用的是9,需要额外装
vdo和kmod-kvdo包,这点后面会单独说。
2. Stratis的底层逻辑与池化设计思路
2.1 Stratis到底封装了什么
很多人以为Stratis是个全新的文件系统,其实不是。Stratis是一个存储管理守护进程(stratisd)加一套CLI(stratis),它管理的是底层的块设备池,池上面再切出文件系统。底层用的技术栈是:
- devicemapper:负责把多个块设备聚合成一个池,做精简配置(thin provisioning)。
- XFS:每个Stratis文件系统实际就是一个XFS,格式化由Stratis自动完成。
所以你在Stratis里建一个文件系统,本质上是让stratisd在池里切一块thin LV,然后mkfs.xfs。理解这一点很重要,因为后面排查问题时,你既可以用stratis命令看上层,也可以下到lvs、dmsetup看底层。
Stratis的核心概念只有三个:
| 概念 | 说明 | 类比 |
|---|---|---|
| pool | 块设备池,由一个或多个块设备组成 | 相当于LVM的VG |
| filesystem | 池上切出的文件系统,精简配置 | 相当于LV,但自动格式化 |
| blockdev | 加入池的物理设备 | 相当于PV |
2.2 为什么Stratis适合"懒人"和快速迭代场景
我最初抗拒Stratis的原因是"多一层封装就多一层故障点"。但用了一段时间后发现,它在几个场景下确实比裸LVM省事:
第一,快照和精简配置是默认行为。LVM做快照要手动算COW空间、要lvcreate -s,Stratis直接stratis filesystem snapshot,而且文件系统天生精简,不用预先分配。对于需要频繁做测试环境快照的场景,这个体验差距很大。
第二,扩容是自动的。池里加盘,文件系统不用手动resize,Stratis会自动把新空间纳入。LVM里你得vgextend再lvextend再xfs_growfs三步,Stratis一步stratis pool add-data搞定。
第三,overprovisioning是内建的。你可以建一个比池实际容量大的文件系统,写满了再扩池。这在开发测试环境里很实用,不用一开始就规划死容量。
但代价也要说清楚:Stratis不支持缩小文件系统,不支持RAID(它把冗余交给底层硬件或mdraid),快照不能单独挂载到别的机器。所以它定位很明确——单机、本地盘、快速迭代,不是企业级共享存储的替代品。
2.3 池的创建与设备选择的一个关键细节
建池的时候有个坑我踩过:Stratis默认会把加入池的设备整个吃掉,包括分区表。如果你拿一块已经有分区的盘直接stratis pool create,它会报错或者把分区表清掉。正确做法是先用wipefs -a把设备清干净。
# 确认设备没有挂载、没有分区表 lsblk /dev/sdb wipefs -a /dev/sdb # 建池 stratis pool create mypool /dev/sdb # 查看池 stratis pool list另一个细节:池的设备大小最好一致。Stratis底层是thin pool,如果混用不同大小的盘,devicemapper的元数据分配会按最大设备算,小盘的空间利用率会打折。我一般建议同型号同容量一起加,扩容时也尽量成对加。
3. VDO的空间优化机制与参数计算
3.1 VDO的三层优化到底怎么工作
VDO不是简单的压缩,它是三层叠加:
- 零块消除:写入全零的块直接不落盘,只记元数据。这个对预分配的虚拟磁盘镜像特别有效,因为镜像里大量是零块。
- 重复数据删除:对每个4KB块算哈希(默认用SHA-256),哈希相同的块只存一份。这是VDO最核心的机制,也是它省空间的主力。
- 压缩:去重之后剩下的唯一块,再用LZ4压缩。LZ4的特点是快,压缩比一般,但对在线场景友好。
这三层的顺序不能乱:先去重再压缩。因为如果先压缩,相同内容的块压缩后可能因为元数据差异导致哈希不同,去重就失效了。
VDO的元数据本身也占空间,默认每1TB逻辑空间需要约1.4GB元数据(实际配置时按物理空间算,比例类似)。所以你不能把整块盘都给VDO用,得留出元数据空间。vdo create的时候它会自动算,但你要心里有数。
3.2 去重效果到底能到多少
这是所有人最关心的问题。我实测过几类场景:
| 场景 | 去重+压缩后空间占用 | 说明 |
|---|---|---|
| 虚拟机镜像(同模板多实例) | 原始10%-20% | 同模板重复率极高 |
| 日志文件(结构化重复) | 原始30%-50% | 重复行多,压缩也有效 |
| 备份归档(增量) | 原始20%-40% | 取决于增量重复度 |
| 随机二进制数据 | 原始90%-100% | 基本没效果 |
关键结论:VDO的收益完全取决于数据重复率。如果你的数据是加密后的、已经压缩过的、或者本身就是随机数据,VDO不但不省空间,还会因为元数据开销和CPU消耗拖慢性能。我见过有人给已经用LUKS加密的盘再套VDO,结果空间没省、IO延迟翻倍,这就是典型的用错场景。
3.3 创建VDO卷时的参数取舍
# 安装 dnf install vdo kmod-kvdo # 创建VDO卷,逻辑大小设为物理的1.5倍(overprovision) vdo create --name=vdovol \ --device=/dev/sdc \ --vdoLogicalSize=1.5T \ --vdoSlabSize=2G \ --compression=on \ --deduplication=on几个参数要解释:
--vdoLogicalSize:逻辑大小,可以大于物理大小,这就是overprovision。但不能无限大,一般建议不超过物理的1.5-2倍,否则写满了会触发只读保护。--vdoSlabSize:VDO把物理空间切成slab管理,默认2G。slab太小元数据多,太大分配不灵活。2G是经验值,除非有特殊需求不用改。--compression和--deduplication:默认都开。如果数据已经压缩过,建议关掉compression省CPU。
创建完用vdostats --human-readable看状态,重点看Used、Available、1K-blocks这几个字段。
注意:VDO卷创建后不能直接改逻辑大小,要改得先停掉、用
vdo growLogical,而且只能增不能减。所以规划时宁可保守一点。
4. 从裸盘到可用文件系统的完整演练
4.1 环境准备与设备清理
假设你有一台RHEL 8,挂了三块盘:/dev/sdb、/dev/sdc、/dev/sdd。演练目标:用sdb+sdc建Stratis池,sdd建VDO卷,最后在VDO上再建Stratis池,体验叠加。
第一步永远是清理设备:
# 确认没有挂载 mount | grep -E 'sdb|sdc|sdd' # 清分区表和文件系统签名 wipefs -a /dev/sdb wipefs -a /dev/sdc wipefs -a /dev/sdd # 确认干净 blkid /dev/sdb /dev/sdc /dev/sddblkid输出为空才算干净。如果还有残留,用dd if=/dev/zero of=/dev/sdX bs=1M count=100把头部清掉。
4.2 建Stratis池并切文件系统
# 装包 dnf install stratisd stratis-cli # 启动服务 systemctl enable --now stratisd # 建池 stratis pool create pool01 /dev/sdb /dev/sdc # 看池状态 stratis pool list # 建文件系统 stratis filesystem create pool01 fs01 # 看文件系统 stratis filesystem liststratis filesystem list会输出文件系统的设备路径,类似/dev/stratis/pool01/fs01。这个路径可以直接挂载:
mkdir -p /mnt/fs01 mount /dev/stratis/pool01/fs01 /mnt/fs01 # 写进fstab,注意用UUID echo "UUID=$(blkid -s UUID -o value /dev/stratis/pool01/fs01) /mnt/fs01 xfs defaults 0 0" >> /etc/fstab这里有个必须注意的点:Stratis文件系统在fstab里要用xfs类型,而且必须加x-systemd.requires=stratisd.service,否则开机时stratisd还没起来,挂载会失败。正确的fstab行是:
UUID=xxxx /mnt/fs01 xfs defaults,x-systemd.requires=stratisd.service 0 0这个坑我踩过,重启后文件系统没挂上,排查半天才发现是服务依赖顺序问题。
4.3 建VDO卷并观察去重效果
# 建VDO卷 vdo create --name=vdo01 --device=/dev/sdd --vdoLogicalSize=1.5T # 格式化并挂载 mkfs.xfs /dev/mapper/vdo01 mkdir -p /mnt/vdo01 mount /dev/mapper/vdo01 /mnt/vdo01 # 看初始状态 vdostats --human-readable现在做个去重实验。往/mnt/vdo01里写两份完全相同的大文件:
# 生成一个500M的随机文件 dd if=/dev/urandom of=/tmp/base.img bs=1M count=500 # 拷贝两份到VDO cp /tmp/base.img /mnt/vdo01/copy1.img cp /tmp/base.img /mnt/vdo01/copy2.img # 看VDO统计 vdostats --human-readable你会看到Used大概只增加了500M左右,而不是1G,因为第二份被去重了。vdostats里的Deduplication和Compression字段会显示节省的比例。这个实验能让你直观感受到VDO在干什么。
4.4 在VDO上叠加Stratis
这是进阶玩法。VDO卷本身是个块设备/dev/mapper/vdo01,可以当Stratis的底层设备用:
# 先卸载VDO上的XFS(如果之前格式化过) umount /mnt/vdo01 wipefs -a /dev/mapper/vdo01 # 把VDO卷加入Stratis池 stratis pool create pool02 /dev/mapper/vdo01 stratis filesystem create pool02 fs02 mount /dev/stratis/pool02/fs02 /mnt/fs02这样数据先经过Stratis的thin层,再经过VDO的去重压缩层。但要注意:Stratis的thin provisioning和VDO的overprovision会叠加,空间超卖的风险翻倍。我一般在这种叠加场景下把VDO的logicalSize设得保守些,比如物理的1.2倍,避免写爆。
5. 快照、扩容与日常维护的实操细节
5.1 Stratis快照的正确用法
Stratis快照是瞬间完成的,因为它底层是thin snapshot,不复制数据。命令很简单:
stratis filesystem snapshot pool01 fs01 fs01-snap但有几个细节文档里不会强调:
第一,快照和原文件系统共享池空间。快照本身不占空间,但原文件系统后续的写入会触发COW,占用池空间。如果池满了,快照和原文件系统都会出问题。所以要监控池的Used比例。
第二,快照可以单独挂载,但挂载后是只读还是可写取决于你怎么挂。默认挂载是可写的,但写快照会加速空间消耗。测试场景我一般挂成只读:
mount -o ro /dev/stratis/pool01/fs01-snap /mnt/snap第三,删除快照要确认没有挂载,否则会报设备忙。
5.2 池扩容与文件系统自动增长
池扩容分两种情况:加新盘、扩已有盘。
# 加新盘 stratis pool add-data pool01 /dev/sde # 扩已有盘(比如盘做了分区,扩分区后) stratis pool add-data pool01 /dev/sdb加完之后,池的Total Physical会变大,池上的所有文件系统自动获得新空间,不需要手动resize。这是Stratis比LVM省事的地方。但注意:文件系统的逻辑大小不会自动变,如果你之前建的文件系统设了固定大小,得手动stratis filesystem set-size。默认建的文件系统是精简的,会跟着池走。
5.3 VDO的监控与调优
VDO的日常监控主要看vdostats:
# 简洁模式 vdostats --human-readable # 详细模式,看每个卷 vdostats --verbose重点关注的字段:
Used:物理已用空间。接近物理大小时会触发只读。Available:物理剩余。Logical Size:逻辑大小。Write Amplification:写放大,正常应该接近1,如果远大于1说明去重效果差或者元数据开销大。
如果发现Used快满了,有两个选择:加物理盘(vdo growPhysical)或者扩逻辑(vdo growLogical,但物理不够时没用)。我一般设置监控告警在Used达到85%时触发,留出处理时间。
提示:VDO卷写满后会进入只读模式,此时写入全部失败,但读还能用。恢复方法是扩物理空间或者删数据。这个只读保护是VDO的安全机制,别以为是故障。
6. 踩过的坑与排查链路实录
6.1 Stratis池满了导致文件系统只读
现象:某台测试机的Stratis文件系统突然变成只读,dmesg里有XFS: filesystem has become read-only。
排查链路:
- 先看
stratis pool list,发现Used接近Total Physical,池满了。 - 池满的原因是thin provisioning超卖,文件系统逻辑大小加起来超过了池物理大小,实际写入把池撑爆了。
- 底层devicemapper的thin pool满了之后,XFS检测到写入失败,自动切只读。
解决:加盘扩容stratis pool add-data,然后mount -o remount,rw重新挂载。但已经写入失败的数据可能损坏,需要检查。
教训:Stratis的overprovision是双刃剑,生产环境要么监控池使用率,要么建文件系统时设--size限制逻辑大小,别让它无限超卖。
6.2 VDO去重率突然下降
现象:一个跑了几周的VDO卷,vdostats显示去重率从70%掉到20%。
排查链路:
- 先确认数据模式有没有变。查了写入来源,发现新上了一批已经压缩过的归档数据。
- 已压缩数据进去重层,哈希不同但内容其实高度相似,去重失效;压缩层也因为数据已压缩,压不动。
- 结论是数据特征变了,不是VDO故障。
解决:把这类已压缩数据挪到普通卷,VDO只放未压缩的原始数据。
教训:VDO不是万能的,数据特征决定收益。上线前一定要用真实数据做样本测试,别信理论值。
6.3 开机挂载顺序导致Stratis文件系统没挂上
现象:重启后/mnt/fs01是空的,mount命令手动执行能挂上。
排查链路:
- 看
systemctl status stratisd,服务是active的。 - 看
journalctl -b,发现挂载单元在stratisd之前执行了,找不到设备。 - 确认fstab里没加
x-systemd.requires=stratisd.service。
解决:补上依赖参数,systemctl daemon-reload后重启验证。
教训:Stratis文件系统的设备节点是stratisd动态创建的,fstab必须声明服务依赖,这是和普通块设备最大的区别。
6.4 VDO卷创建时报"设备太小"
现象:vdo create报错,说设备空间不足以容纳元数据。
排查链路:
- 查设备大小,
lsblk显示500G。 - 查VDO元数据需求,默认每TB逻辑需要约1.4GB元数据,加上VDO自身的固定开销。
- 500G设备如果logicalSize设得太大,元数据空间不够。
解决:要么减小--vdoLogicalSize,要么换更大的设备。我一般建议VDO底层设备至少100G起步,太小没意义。
教训:VDO有最小空间要求,规划时把元数据开销算进去,别把设备空间用满。
7. 两套方案怎么选与组合使用建议
7.1 选型对照表
| 维度 | Stratis | VDO |
|---|---|---|
| 核心目标 | 池化管理、快照、精简配置 | 空间优化(去重+压缩) |
| 底层技术 | LVM thin + XFS | 块设备层 + UDS + LZ4 |
| 适用场景 | 本地多盘管理、测试环境快照 | 备份、日志、虚拟化镜像 |
| 不适用 | 需要RAID、需要缩容 | 已压缩/加密/随机数据 |
| 性能影响 | 小,thin层开销低 | 中,去重消耗CPU和内存 |
| 扩容方式 | add-data,自动增长 | growPhysical/growLogical |
| 缩容 | 不支持 | 不支持 |
7.2 组合使用的正确姿势
如果你的场景既需要池化管理又需要空间优化,可以叠加,但顺序有讲究:
推荐顺序:VDO在下,Stratis在上。因为VDO是块设备层的优化,应该尽量贴近物理设备;Stratis的thin层在上面做逻辑管理。这样VDO能对Stratis写入的所有数据做去重,包括快照产生的COW数据。
不推荐:Stratis在下,VDO在上。因为Stratis的thin设备本身是精简的,VDO再去重意义不大,而且两层精简叠加容易出问题。
叠加时的容量规划要保守:假设物理1T,VDO logicalSize设1.2T,Stratis池就按1.2T算,文件系统逻辑大小总和别超过1.2T。这样即使写满,VDO还有物理空间兜底。
7.3 监控与告警的最小配置
不管用哪套,监控是必须的。我一般配两个检查:
# Stratis池使用率 stratis pool list --output=name,total-physical,used-physical # VDO使用率 vdostats --human-readable | awk '{print $1, $5, $6}'把这两个输出接到监控系统,阈值设85%。Stratis池满了会导致文件系统只读,VDO满了也会只读,都是影响业务的。
注意:RHEL 9之后VDO不再默认安装,需要从AppStream单独装。如果你在规划长期方案,要评估这个变化。Stratis在RHEL 9/10里还是主线,可以放心用。
8. 一些不写在文档里的经验
最后分享几个我实际用下来觉得值得记的点。
Stratis的stratis filesystem list输出里有个Size字段,那是逻辑大小,不是实际占用。实际占用要看池的Used。很多人看文件系统Size以为空间还多,结果池早满了。这个认知差是新手最容易踩的。
VDO的--vdoSlabSize改起来很麻烦,创建后基本不能改,要改得重建。所以创建时想清楚,2G对绝大多数场景够用,除非你的数据块特别大或者特别小。
Stratis快照不要留太多。每个快照都会让原文件系统的COW开销累积,快照越多,原文件系统写入越慢、空间消耗越快。我一般只保留最近2-3个快照,旧的及时删。
VDO的CPU开销在去重开启时比较明显,尤其是写入密集场景。如果机器CPU核数少,建议先测一下,或者关掉去重只留压缩。压缩用LZ4,开销比去重小得多。
两套方案都不支持缩容,这是设计上的取舍,不是bug。规划时宁可从小开始慢慢加,也别一次给太大后面想缩。我见过有人给Stratis文件系统设了10T逻辑大小,结果池只有2T,写了一点就满了,想缩还缩不了,只能重建。
演练环境里随便折腾,生产环境先做容量规划。Stratis和VDO的很多行为(thin超卖、VDO只读保护)在测试环境不容易触发,因为测试数据量小。上线前一定要用接近真实的数据量和写入模式压一遍,把边界摸清楚。
这套东西我前后在几台机器上跑了小半年,从最初的"这什么鬼"到现在的"真香",中间踩的坑基本都写在这了。如果你刚开始接触,建议先按第4节的演练走一遍,把命令敲熟,再回头看第6节的排查链路,会有更深的体会。