☰
Ceph RBD删除后空间不释放?从原理到排查全面解析
2026/10/21 9:18:10 网站建设 项目流程

前几天值班时收到一条告警,某个rbd池使用率逼近85%。排查后发现前一天刚删了一个几百GB的临时镜像,按理说空间应该哗啦啦地降下来,但ceph df里的数字纹丝不动。群里的消息又弹出来一句:"删除rbd的数据后ceph空间没有释放"——这句话基本是每个存储运维都见过的求助标题。

这个问题的迷惑性在于,大多数时候不是Ceph"卡住了",而是我们删的东西和Ceph统计的空间根本不是同一层。RBD是块存储,删除它有两种完全不同的语义:删整个镜像,和删镜像里文件系统上的文件。两种场景的释放路径、依赖条件、验证方法全都不一样。这篇就把我实际排查时走通的路线完整写出来,按场景分类,从命令到原理都讲清楚。

1. 先分清删的是什么:RBD镜像还是RBD里的文件系统数据

排查这个问题之前,必须先做一道"语文题"。RBD(Rados Block Device)对外呈现的是一块裸设备,就像一块没有格式化的硬盘。它本身不知道自己里面装了ext4、xfs还是NTFS。正因如此,我们说的"删数据"至少分成三个层面:

1.1 场景A:删除整个RBD镜像

执行rbd rm或者通过管理平台删除一个块设备卷。这时候Ceph要处理的是整个镜像对应的所有rados对象。正常情况下,删除完成后,对应pool的USED应该下降。如果它不下降,通常是镜像存在依赖关系、删除被拒绝,或者镜像根本没有进入真正的删除流程。

1.2 场景B:在RBD块设备内部删除文件

把RBD挂载到Linux服务器或者作为虚拟机的系统盘,然后rm掉一批文件。这是最容易产生误判的场景。客户机上的df -h可能已经看到目录空间释放了,但Ceph集群侧的ceph df没有任何反应。因为块设备内部文件系统标记"空闲"的块,Ceph是完全感知不到的,除非额外开启discard/trim链路把"这些块没用了"的通知传递下来。

1.3 快速判断表

你做的操作客户机df变化Ceph池USED变化常用的释放动作
rbd rm删除镜像不适用,设备没了应下降,若未下降则查引用/trashrbd children/rbd trash purge
在RBD内删除文件会下降不会下降(除非开discard)fstrim、rbd sparsify
删除rbd pool整个池不适用应大幅下降检查是否残留对象

先把场景归类,后续排查才不会南辕北辙。很多人一上来就翻OSD日志、检查PG状态,忙活半天,最后发现他只是忘了在客户机执行fstrim。

2. rbd rm背后的"隐形锁":克隆、快照与引用计数

如果确定是场景A,执行了rbd rm但空间没释放,第一件事不是看OSD,而是确认这个镜像有没有被"钉住"。

2.1 为什么父镜像删不掉:子镜像引用关系

Ceph RBD有一个很常用的功能:rbd clone。它允许从一个快照创建子镜像,子镜像在初始状态下并不复制父镜像的全部数据,而是引用父镜像的对象。这个机制让部署虚拟机模板变得非常快,但也带来一个典型问题:父镜像被多个子镜像引用时,Ceph会拒绝删除它。

另一个类似的情况是快照。如果镜像存在快照,而快照又被保护或者被克隆依赖,rbd rm可能会直接报错,而不是"删除后空间慢慢释放"。换句话说,空间没有释放,很可能是删除根本没成功。

我遇到过最典型的报错长这样:

$ rbd rm vm-images/base-ubuntu-22.04 2025-02-10 10:23:14.234 7f1234abcd8 -1 rbd: error: image still has snapshots

或者:

rbd: error: cannot remove an image that has children

这两种情况分别对应快照未清理、克隆子镜像仍依赖父镜像。如果命令压根没走完,那ceph df当然不会变。

2.2 解除引用的完整操作链

按照下面的顺序操作,基本能解开大部分"隐形锁":

  1. 查子镜像:
rbd children vm-images/base-ubuntu-22.04

输出可能是:

pool=vm-images name=vm-child-001 pool=vm-images name=vm-child-002
  1. 查快照列表:
rbd snap ls vm-images/base-ubuntu-22.04
  1. 对子镜像执行扁平化,把引用对象复制成子镜像自己的数据:
rbd flatten vm-images/vm-child-001 rbd flatten vm-images/vm-child-002

注意:flatten会把父对象完整复制到子镜像,大镜像执行时会瞬间增加底层空间占用,并且在线高峰期可能拖慢集群。建议先在低峰期确认容量余量,尽量分批操作。

  1. 如果快照被保护,先解除保护再删除:
rbd snap unprotect vm-images/base-ubuntu-22.04@snapshot-before-clone rbd snap rm vm-images/base-ubuntu-22.04@snapshot-before-clone
  1. 再执行删除:
rbd rm vm-images/base-ubuntu-22.04

2.3 批量环境下的排查命令

如果pool里有很多镜像,手动一个个查太慢。可以写个小脚本先扫一遍,把可能被依赖的镜像找出来:

for img in $(rbd ls vm-images); do children=$(rbd children vm-images/$img 2>/dev/null) if [ -n "$children" ]; then echo "$img -> $children" fi done

这种巡检建议纳入删除前的例行操作。尤其是用RBD做虚拟化模板、云盘克隆的环境里,父镜像被引用是导致"删除不释放"的头号原因。

3. 客户机删文件后,Ceph真的不知道:discard链路与稀疏化

第二个高频场景是:在RBD块设备内部删了文件,客户机空间释放了,Ceph集群空间稳稳不动。这个问题的根因不在Ceph,而在块设备与文件系统之间的信息断层。

3.1 块存储与文件系统的"感知差"

文件系统如ext4、xfs,会在自己的元数据里维护块分配状态。当用户执行rm时,文件系统只是把对应数据块标记为"未分配",不会主动告诉底层设备"这批块不用了"。除非挂载时带了discard选项,或者手动执行fstrim。

对Ceph来说,RBD镜像里的每个rados对象都还好好地存在,所有的数据块在Ceph看来依然是"已占用"状态。Ceph不是不知道删除操作,是压根没收到通知。让我用一个生活化的类比:这就好比你把衣柜里不穿的衣服扔了,但你家的房子面积不会因此变大——扔衣服改变的是柜子里面的布局,房本上写的建筑面积还是那么多。

3.2 在客户机打开TRIM

要打通这条链路,得让文件系统把"空闲块"转换成块设备协议里的discard命令,再通过RBD驱动发给librbd/krbd,最终删除对应rados对象。

Linux环境下的操作很简单:

# 挂载时启用实时discard mount -o discard /dev/rbd0 /mnt/data # 或者对已挂载分区执行一次性TRIM fstrim -v /mnt/data

执行之前可以先确认块设备是否支持discard:

lsblk -D /dev/rbd0

看输出里的DISC-GRAN和DISC-MAX。如果都是0,说明这条链路没打通。

fstrim执行完成后,输出类似:

/mnt/data: 153.6 GiB (164926744166 bytes) trimmed

这说明有约150GB的块被通知为"空洞"。但要注意,这只是告诉Ceph哪些对象可以被稀疏化,实际空间回收还需要在Ceph侧看版本和镜像快照情况。

3.3 在Ceph侧做稀疏化

新版RBD提供了一个专门命令:rbd sparsify。它会把镜像中已discard的hole对象真正删除,把稀疏空间还给pool。

rbd sparsify vm-images/ubuntu-server

执行前建议先看这个命令的进度和影响,特别是镜像存在快照时。因为快照可能引用旧对象,sparsify只能回收没有被快照引用的"空洞"对象。如果镜像有多个活跃快照,回收效果可能不如预期。

对于通过QEMU/KVM运行的虚拟机,如果虚拟磁盘控制器不支持传递trim,客户机内的fstrim只会白跑。RAID控制器、virtio-blk等都要确认配置。在libvirt的domain配置里,一般可以加discard='unmap'之类的属性,具体要看虚拟化平台的版本和驱动能力。这一步和Ceph无关,却是整条链路最容易断的一环。

3.4 判断discard是否真的生效

客户机执行fstrim后,在Ceph节点上可以对比镜像大小与已分配对象大小:

rbd du vm-images/ubuntu-server

输出中的SIZE和PROVISIONED会有差异。如果discard生效,实际使用的对象数会明显小于名义大小。PROVISIONED代表真正占用的空间,如果这个数值下降,说明客户机的trim已经传到了Ceph这一层。

4. trash、watcher与延迟回收:藏着镜像的"垃圾桶"

除了引用关系和discard,还有一个容易忽略的机制:RBD trash。它本质上是一个延迟删除垃圾桶,专门为了避免误删设计的。如果团队启用了trash功能,或者管理平台调用的是rbd trash move而不是直接rbd rm,那删除操作只是把镜像放进了垃圾桶,并不会立刻清理数据。

4.1 删除命令其实进了trash

检查命令很简单:

rbd trash ls --all vm-images

输出里会列出已经"删除"但仍躺在trash中的镜像。如果发现目标镜像还在,执行立即清理:

rbd trash purge vm-images

或者只删指定镜像:

rbd trash remove vm-images/ubuntu-server --force

--force参数有风险,它会绕过一些保护直接删除。生产环境里最好确认该镜像确无依赖、无客户端连接后再用。

4.2 watcher卡住删除流程

另一个常见问题:客户端还在连接着这个RBD镜像。Ceph使用watcher机制来感知客户端是否在线。如果虚拟机没有正常关机、物理机没有卸载设备,watcher就会一直存在,导致删除流程无法完成。这时rbd rm可能表现为执行成功,但实际底层对象还在被引用,空间不会释放。

排查命令:

rbd status vm-images/ubuntu-server

如果输出显示Watchers里有活动客户端,需要先回到客户端侧处理:

umount /mnt/data

或者在虚拟化管理平台里关闭对应虚拟机。然后再回来执行删除。

也可以直接通过rados查看对象上的watcher:

rados -p vm-images listwatchers rbd_data.xxx.xxx

这种问题在"删除正在使用的云盘"时特别常见。有时候管理平台点击删除后没有强制切断客户端,Ceph侧就一直在等待watch超时。短则几十秒,长则数小时。

4.3 Bluestore底层回收的延迟

还有一种情况不能算Bug,只能算"统计延迟":Ceph底层使用Bluestore时,删除的rados对象不会立即在物理磁盘上变成可用空间。Bluestore有自己的分配器,删除小对象时会产生内部碎片,需要经过后台compact或合并才能释放连续空余区块。

所以有时我们会看到:

ceph df

里pool的USED已经下降了,但ceph osd df显示OSD的可用空间没怎么涨。这个阶段不需要干预,等Bluestore的compact周期跑完,数值会慢慢回正。如果等了很久仍没有变化,才需要考虑是不是对象残留。

更麻烦的是大量小对象删除后,OSD的碎片率升高。可以通过以下命令观察:

ceph daemon osd.0 bluestore allocator score

这个分数如果长期偏高,说明分配器内部碎片多,空间利用率不好。必要时需要人工干预或等待重构任务,但这不是"空间没释放",而是"空间暂时散落"。

5. 如何确认空间是否真的释放:三个命令看穿真相

排错过程中最怕的不是问题没解决,而是问题已经解决了却没看对指标。很多伙伴盯着ceph df最上面的RAW USED看半天,发现下降很慢,就以为没释放,实际上应该看pool对应的USED列。

5.1 ceph df的正确打开方式

执行:

ceph df

输出分两块。上面是集群总览,下面是POOLS列表。需要关注的是POOLS列表里对应pool的USED数值,比如:

POOLS: NAME ID USED %USED MAX AVAIL OBJECTS vm-images 12 1024G 78.5 280G 220000

删除100GB镜像后,如果这个USED从1024G降到924G,那说明数据释放是正常的。RAW USED的变化会带着副本和Bluestore开销一起体现,下降幅度可以大也可以小,不能用它做唯一判断依据。

5.2 rados df和osd df辅助定位

如果pool的USED已经下降,但心里还是不踏实,可以用rados df看每个pool的对象数和对象总大小:

rados df

配合ceph osd df tree看单个OSD的数据分布:

ceph osd df tree

正常释放时,对应pool所在OSD的USED也会逐步下降。如果pool USED下降但OSD USED纹丝不动,下一站去check Bluestore的碎片和compact状态,而不是继续在rbd层面绕圈。

5.3 回收过程中的时间因素

RBD删除对象的数量级通常是几万到几十万。即使rados操作本身是异步并行的,删除请求发送到OSD后,还要经历本地事务提交、内部元数据更新、可能的日志合并。所以实际生产中,rbd rm命令返回delete,但ceph df的数值在几十秒到几分钟内才完全稳定,都属于正常范围。

如果指挥了半小时依然没有变化,建议再查一次:

rados -p vm-images ls | grep rbd_data | wc -l

这个数量如果远大于预期,说明有对象残留,回到第2、4章提到的原因继续排查。如果数量已经很少,那问题大概率在Bluestore底层,耐心等compact即可。

6. 最后一次复盘与习惯养成

这类问题我前后处理过很多次,复盘下来发现,90%的"空间没释放"不是Ceph坏了,而是删除对象的生命周期没走完整。与其每次紧急扩容,不如把下面几个动作变成周常,能省掉大量夜间值班时间。

6.1 删除前先做三查

任何一次RBD镜像删除,提交前至少跑三条命令:

rbd status pool/image rbd children pool/image rbd trash ls --all pool

三条命令分别解决"客户端还在不在"、"有没有子镜像依赖"、"是不是进了垃圾桶"。花不到十秒,能把大多数误操作挡在门外。

6.2 定期TRIM与监控

对频繁删除数据的业务,建议在客户机配置定时任务:

0 2 * * * /sbin/fstrim -av

同时在Ceph侧监控rbd du的PROVISIONED字段,一旦发现裸镜像的provisioned远大于实际业务数据量,就安排一次rbd sparsify,把空洞及时回收。注意错开业务高峰,sparsify和flatten一样,都是会消耗集群IO的操作。

6.3 我的个人建议

如果管理平台是自己封装的,删除云盘时尽量显式调用rbd trash并设置合理的延迟时间,比如1到3天。这样既能防误删,又不会让空间无限积压。同时要确保删除流程里包含"强制断开客户端"的步骤,避免watcher残留。

再有就是遇到"没释放"先别心慌,按本文场景对号入座:先看命令有没有报错,再看trash,再看discard,最后才怀疑底层回收延迟。Ceph这种系统,空间不会凭空消失,也不会凭空出现,每一块对象都有它的去处。把删除动作拆开看明白,问题自然就解开了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询