一次对象存储元数据osd空间放大问题调查

写在前面

欢迎关注 奋斗的cepher 微信公众号阅读更多好文
默认使用ceph的对象存储功能,在rados层使用的是数据与元数据划分不同存储池进行分别存放的方案,具体来说就是对象的数据部分存放在data pool,而元数据部分,如对象索引、用户信息、gc及lc信息等,是存放在其他存储池,这样做的好处是可以根据不同的数据特点,将存储池划分到不同的磁盘介质上,充分发挥硬件的优势,同时能兼顾比较好的性价比,具体的分析和集群建设问题松鼠哥已经在课程中进行了详细的介绍。

数据部分的特点是数据体积较大,放在hdd上比较划算,而元数据的特点是数据体积小但数量往往很大,因此放在ssd上可以加速对象存储的性能,然而本次遇到的问题是元数据存储池的osd用量莫名地大,所以有了本篇的调查。

本篇的重点在解决思路,授人与鱼不如授人以渔,思路才是最重要的。

开始

首先是现象,没什么特别,就是告警提示有osd挂掉了,且明显不是坏盘引发。

接下来是确定挂掉的原因,直接去看日志,发现是这样的:

-10001> 2024-10-09 05:36:22.267 7f110346d700  1 bluefs _allocate failed to allocate 0x41e9aee on bdev 1, free 0x1120000; fallback to bdev 2
-10001> 2024-10-09 05:36:22.267 7f110346d700 -1 bluefs _allocate failed to allocate 0x on bdev 2, dne
-10001> 2024-10-09 05:36:22.267 7f110346d700 -1 bluefs _flush_range allocated: 0x0 offset: 0x0 length: 0x41e9aee
-10001> 2024-10-09 05:36:22.298 7f110346d700 -1 /home/jenkins-build/build/workspace/ceph-build/ARCH/x86_64/AVAILABLE_ARCH/x86_64/AVAILABLE_DIST/centos7/DIST/centos7/MACHINE_SIZE/gigantic/release/13.2.10/rpm/el7/BUILD/ceph-13.2.10/src/os/bluestore/BlueFS.cc: In function 'int BlueFS::_flush_range(BlueFS::FileWriter*, uint64_t, uint64_t)' thread 7f110346d700 time 2024-10-09 05:36:22.268694
/home/jenkins-build/build/workspace/ceph-build/ARCH/x86_64/AVAILABLE_ARCH/x86_64/AVAILABLE_DIST/centos7/DIST/centos7/MACHINE_SIZE/gigantic/release/13.2.10/rpm/el7/BUILD/ceph-13.2.10/src/os/bluestore/BlueFS.cc: 1704: FAILED assert(0 == "bluefs enospc")
......

看ceph的日志排查问题,最重要的是看-1的日志,很多同学截图日志往往get不到重点,其实重点就是-1的日志,别的多数情况下都不太重要,这里很清楚了,-1的日志显示osd尝试从2个dev上分配空间都失败了,所以assert了。

定位到直接原因后,就要展开排查思路了,既然是osd空间分配问题,那么看下它的空间情况

[root@test-mon1 ~]# ceph osd df|grep 1628
1628 ssd 1.00000  1.00000 447 GiB 385 GiB  118 GiB  252 GiB 484 MiB 237 GiB 83.06 0.89 177

不管是集群层面还是监控,都没有显示osd达到full也就是理论上还有可用空间的,显然目前osd只用了83%,那么为什么rocksdb无法申请到空间了呢?

一个bluestore做的osd,其磁盘空间一般来说我们可以分成2部分:

  • 一部分是osd的data区域,这在osd中用来存放数据部分,具体是osd目录中的block指向的块设备。
  • 另外一部分是db区域,具体是osd目录中的block.wal和block.db,其中block.wal实质为rocksdb的一部分。

当创建osd时我们可以单独指定某些块设备或者卷作为block.db和block.wal,也可以不单独指定,当我们仅指定data时,osd创建以后,db和wal就自动在data上分配空间,默认为block.db分配data空间的4%作为自己的可用空间,当rocksdb自己的可用空间用完后,就会往data上分,如果data也没有空间可以分出,就会报上述的错误。

这个osd.1628就是没有单独分出rocksdb和wal的单个ssd磁盘建成的osd,用来存放几个对象存储元数据池的数据:

[root@test-mon1 ~]# ceph df
......
POOLS:
  NAME                            ID     USED        %USED     MAX AVAIL     OBJECTS    
  songshuge.rgw.buckets.data       1      565 TiB     23.33       1.8 PiB     3080098811 
  .rgw.root                        2       32 KiB         0       499 GiB             39 
  songshuge.rgw.buckets.index      3          0 B         0       499 GiB         347596 
  songshuge.rgw.log                4      3.0 KiB         0       499 GiB           1229 
  songshuge.rgw.meta               5      237 KiB         0       499 GiB           1179 
  songshuge.rgw.control            6          0 B         0       499 GiB              8 
  songshuge.data1                  7      1.8 PiB     74.83       606 TiB     1555068064 
  songshuge.data2                  8      1.5 PiB     63.55       887 TiB     1174599566 
  songshuge.rgw.buckets.non-ec     9      1.1 GiB         0       499 TiB       43370446 

从元数据存储池的用量统计可以看到,只有non-ec池的用量较大,其他池几乎没有占用。

存储池存储数据时,实际上会有两种形式,一种是数据直接写入的形式,另外一种是omap的形式。数据直接写入的形式,就是将op过来的数据直接写到osd的data区,而omap的形式,则是将op过来的数据以kv的方式写入到rocksdb中,由于集群存放的数据量庞大,index存储池的占用应该是很大的,但是在ceph df中没有体现,是因为index pool的数据都是以omap的形式存放,这部分空间无法在ceph df中看到,只能通过ceph osd df观察具体的osd用量才能看到:

[root@test-mon1 ~]# ceph osd df|grep -E "1628|OMAP"
ID   CLASS WEIGHT  REWEIGHT SIZE    USE     DATA    OMAP     META    AVAIL   %USE  VAR  PGS 
1628 ssd 1.00000  1.00000 447 GiB 385 GiB  118 GiB  232 GiB 484 MiB 237 GiB 83.06 0.89  177

接下来,排查的思路是调查data和db的空间使用情况。

首先是rocksdb的空间情况,定位到离assert最近的一个lsm_state,它是db空间使用的直接统计,看下它的实际空间情况:

-10001> 2024-10-09 05:36:15.495 7f1103c6e700  4 rocksdb: (Original Log Time 2024/10/09-05:36:15.496359) EVENT_LOG_v1 {"time_micros": 1728423375496349, "job": 167, "event": "compaction_finished", "compaction_time_micros": 39492399, "output_level": 3, "num_output_files": 25, "total_output_size": 1680590352, "num_input_records": 4104963, "num_output_records": 3783129, "num_subcompactions": 1, "output_compression": "NoCompression", "num_single_delete_mismatches": 0, "num_single_delete_fallthrough": 0, "lsm_state": [3, 49, 421, 3075, 0, 0, 0]}

大致算了一下,rocksdb使用的总空间大小为(3+49+421+3075)*64MiB,跟ceph osd df中显示的osd的omap用量差不多,那么data部分呢?

前面说过,data部分是用来存放数据写入的,但是这些osd承载的都是对象存储的元数据池,理论上应该不会产生data写入,而只有omap写入才对,唯一有可能产生data写入的是non-ec的存储池,而且显然它的对象数量很大。

前述命令显示,non-ec的用量只有1.1GiB,怎么会造成这么大的data写入呢?

non-ec这个存储池的作用,是在对象存储写入中,当使用multipart方式上传时,rgw需要往这个pool中写入一条记录,当多个块都上传完成时,rgw需要根据记录来完成这些块的合并,合并完成后就会删除这个记录,另外,分块上传失败时,用户手动进行abort,通知rgw上传终止,也会删除这个记录,如果合并失败且没有abort,这个记录就不会被删除,这就存在残留的风险。

哪怕是记录残留,4千多万的对象只有1.1GiB,会造成osd的data写满吗?

别说不会,看一下这些对象什么情况,随机挑选一个看看:

[root@test-mon1 ~]# rados -p songshuge.rgw.buckets.non-ec stat b041a41c-3253-4f05-aef0-bc753b1c061b.b7e53454.766__multipart_12420000441435122B/test1.mp4.tmp.2~ABUw1IQCFRBtnPiAIEgPOjJr62SPqWR.meta
songshuge.rgw.buckets.non-ec/b041a41c-3253-4f05-aef0-bc753b1c061b.b7e53454.766__multipart_12420000441435122B/test1.mp4.tmp.2~ABUw1IQCFRBtnPiAIEgPOjJr62SPqWR.meta mtime 2023-10-11 12:06:51.000000, size 27

最后一个字段显示,每个记录是27B,好家伙,这么小!

一个记录27B,4千多万个记录确实才1GiB,这有什么问题?

当然有问题,这里就比较深入了,需要知道,当一个data写提交到osd时,由于每个写入都是单独的对象(可以把osd看作对象存储),osd需要为这个对象申请空间来存放,出于效率考虑,这个申请的空间大小有个最小值,ssd默认是16k(bluestore_min_alloc_size_ssd),那么,为了存下这个27B的数据,osd需要在data区域申请一个最小的16k的块进行写入,这里就产生了606倍的空间放大

这就离谱!为什么这么小体积的数据要写data而不用omap方式写?why??是因为使用omap存的话难度很大吗?

解决

明确原因和基本逻辑后,解决思路就基本有几个:

  • 写个外挂工具巡检这个non-ec池,删掉过期的non-ec残留的meta记录
  • 压力给到用户,让用户去abort这些失败的操作,rgw会去清理这些数据
  • 为bucket设置lc,定期清理残留的碎片和记录(S3支持)
  • 格局,不就是空间放大嘛,让隔壁老外看到以为咱们加不起呢,咱加盘,加最贵的,狠狠地加,每台加满

这些思路都是阔以的,同学们可以按需执行~

总结

由于集群没有警告osd的full,这种空间放大问题比较隐蔽,不容易排查,总体思路是比较明确的:

  • 1、确定直接原因,去看日志呗
  • 2、捋顺逻辑,查询各方数据,确定大体情况
  • 3、osd空间无非就是data和db,分类调查下去,总会发现蛛丝马迹
  • 4、大胆假设,小心论证,最终得到确切结论和解决方案

都看到最后了,觉得有收获的老铁给松鼠哥赏一个吧,谢谢~
欢迎关注 奋斗的cepher 微信公众号阅读更多好文

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的松鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值