大话Ceph--RBD那点事儿

引言

这篇文章主要介绍了RBD在Ceph底层的存储方式,解释了RBD的实际占用容量和RBD大小的关系,用几个文件的例子演示了文件在RBD(更恰当的是xfs)中的存储位置,最后组装了一个RBD,给出了一些FAQ。

RBD是什么

RBD : Ceph’s RADOS Block Devices , Ceph block devices are thin-provisioned, resizable and store data striped over multiple OSDs in a Ceph cluster.

上面是官方的阐述,简单的说就是:

  • RBD就是Ceph里的块设备,一个4T的块设备的功能和一个4T的SATA类似,挂载的RBD就可以当磁盘用。

  • resizable:这个块可大可小。

  • data striped:这个块在ceph里面是被切割成若干小块来保存,不然1PB的块怎么存的下。

  • thin-provisioned:精简置备,我认为的很容易被人误解的RBD的一个属性,1TB的集群是能创建无数1PB的块的。说白了就是,块的大小和在ceph中实际占用的大小是没有关系的,甚至,刚创建出来的块是不占空间的,今后用多大空间,才会在ceph中占用多大空间。打个比方就是,你有一个32G的U盘,存了一个2G的电影,那么RBD大小就类似于32G,而2G就相当于在ceph中占用的空间。

RBD,和下面说的,是一回事。

实验环境很简单,可以参考一分钟部署单节点Ceph这篇文章,因为本文主要介绍RBD,对ceph环境不讲究,一个单OSD的集群即可。

创建一个1G的块foo,因为是Hammer默认是format 1的块,具体和format 2的区别会在下文讲到:


 
 
  1. [ root@ceph cluster] # ceph -s
  2.    cluster fc44cf62 -53e3 -4982 -9b87 -9d3b27119508
  3.     health HEALTH_OK
  4.     monmap e1: 1 mons at {ceph= 233.233 .233 .233: 6789/ 0}
  5.            election epoch 2, quorum 0 ceph
  6.     osdmap e5: 1 osds: 1 up, 1 in
  7.      pgmap v7: 64 pgs, 1 pools, 0 bytes data, 0 objects
  8.             7135 MB used, 30988 MB / 40188 MB avail
  9.                   64 active+clean
  10. [ root@ceph cluster] # rbd create foo --size 1024
  11. [ root@ceph cluster] # rbd info foo
  12. rbd image 'foo':
  13.    size 1024 MB in 256 objects
  14.    order 22 ( 4096 KB objects)
  15.    block_name_prefix: rb .0 .1014 .74b0dc51
  16.    format: 1
  17. [ root@ceph cluster] # ceph -s
  18. ...
  19.    pgmap v10: 64 pgs, 1 pools, 131 bytes data, 2 objects
  20.             7136 MB used, 30988 MB / 40188 MB avail
  21.                   64 active+clean

Tips : rbd的指令如果省略-p / --pool参数,则会默认-p rbd,而这个rbd pool是默认生成的。

对刚刚的rbd info的几行输出简单介绍下:


 
 
  1.     size 1024 MB in 256 objects
  2.     order 22 (4096 KB objects)
  3.     block_name_prefix: rb .0 .1014 .74b0dc51
  4.     format: 1
  • size: 就是这个块的大小,即1024MB=1G,1024MB/256 = 4M,共分成了256个对象(object),每个对象4M,这个会在下面详细介绍。

  • order 22, 22是个编号,4M是22, 8M是23,也就是2^22 bytes = 4MB, 2^23 bytes = 8MB。

  • block_name_prefix: 这个是块的最重要的属性了,这是每个块在ceph中的唯一前缀编号,有了这个前缀,把服务器上的OSD都拔下来带回家,就能复活所有的VM了。

  • format : 格式有两种,1和2,下文会讲。

观察建foo前后的ceph -s输出,会发现多了两个文件,查看下:


 
 
  1.     pgmap v10: 64 pgs, 1 pools, 131 bytes data, 2 objects
  2. [root@ceph cluster]# rados -p rbd ls
  3. foo .rbd
  4. rbd_directory

再查看这两个文件里面的内容:


 
 
  1. [root@ceph ~] # rados -p rbd get foo.rbd foo.rbd
  2. [root@ceph ~] # rados -p rbd get rbd_directory rbd_directory
  3. [root@ceph ~] # hexdump  -vC foo.rbd
  4. 00000000   3c 3c 3c 20 52 61 64 6f   73 20 42 6c 6f 63 6b 20   |<<< Rados Block |
  5. 00000010   44 65 76 69 63 65 20 49   6d 61 67 65 20 3e 3e 3e   |Device Image >>>|
  6. 00000020  0a 00 00 00 00 00 00 00   72 62 2e 30 2e 31 30 31   |........rb.0.101|
  7. 00000030   34 2e 37 34 62 30 64 63   35 31 00 00 00 00 00 00   |4.74b0dc51......|
  8. 00000040   52 42 44 00 30 30 31 2e   30 30 35 00 16 00 00 00   |RBD.001.005.....|
  9. root@ceph ~] # hexdump  -vC rbd_directory
  10. 00000000   00 00 00 00 01 00 00 00   03 00 00 00 66 6f 6f 00   |............foo.|

这时候我们再创建一个RBD叫bar,再次对比查看:


 
 
  1. [root@ceph ~] # rbd create bar --size 1024
  2. [root@ceph ~] # rados -p rbd ls
  3. bar.rbd
  4. foo.rbd
  5. rbd_directory
  6. [root@ceph ~] # rados -p rbd get rbd_directory rbd_directory
  7. [root@ceph ~] # hexdump  -vC rbd_directory
  8. 00000000   00 00 00 00 02 00 00 00   03 00 00 00 62 61 72 00   |............bar.|
  9. 00000010   00 00 00 03 00 00 00 66   6f 6f 00 00 00 00         |.......foo....|

多出了个bar.rbd文件,很容易联想到这个文件的内容是和foo.rbd内容类似的,唯一不同的是保存了各自的block_name_prefix。然后,rbd_directory里面多出了bar这个块名字。可以得出以下的推论:

每个块(RBD)刚创建(format 1)时都会生成一个rbdName.rbd这样的文件(ceph里的对象),里面保存了这个块的prefix
同时,rbd_directory会增加刚刚的创建的rbdName,顾名思义这个文件就是这个pool里面的所有RBD的索引。

为了简单试验,删掉刚刚的bar只留下foo:


 
 
  1. [root@ceph ~]# rbd rm bar
  2. Removing image: 100% complete.. .done.

RBD使用

建好了块,我们就开始使用这个块了:


 
 
  1. [root@ceph ~] # rbd map foo
  2. /dev/rbd 0
  3. [root@ceph ~] # mkfs.xfs /dev/rbd0
  4. meta-data= /dev/rbd 0              isize= 256    agcount= 9, agsize= 31744 blks
  5.         =                       sectsz= 512   attr= 2, projid32bit= 1
  6.         =                       crc= 0        finobt= 0
  7. data     =                       bsize= 4096   blocks= 262144, imaxpct= 25
  8.         =                       sunit= 1024   swidth= 1024 blks
  9. naming   =version 2              bsize= 4096   ascii-ci= 0 ftype= 0
  10. log      =internal log           bsize= 4096   blocks= 2560, version= 2
  11.         =                       sectsz= 512   sunit= 8 blks, lazy-count= 1
  12. realtime =none                   extsz= 4096   blocks= 0, rtextents= 0
  13. [root@ceph ~] # mkdir  /foo
  14. [root@ceph ~] # mount /dev/rbd0 /foo/
  15. [root@ceph ~] # df -h
  16. 文件系统        容量  已用  可用 已用% 挂载点
  17. /dev/vda1         40G   7.0G   31G   19% /
  18. ...
  19. /dev /rbd0      1014M   33M  982M    4% /foo

我喜欢记点东西,比如上面的33M就是刚格式化完的xfs系统的大小,算是一个特点吧。
先别急着用,集群发生了点变化,观察下:


 
 
  1. [root@ceph ~]# ceph -s
  2.      pgmap v44: 64 pgs, 1 pools, 14624 KB data, 15 objects
  3. [root@ceph ~]# rados -p rbd ls |sort
  4. foo.rbd
  5. rb .0 .1014 .74b0dc51 .000000000000
  6. rb .0 .1014 .74b0dc51 .000000000001
  7. rb .0 .1014 .74b0dc51 .00000000001f
  8. rb .0 .1014 .74b0dc51 .00000000003e
  9. rb .0 .1014 .74b0dc51 .00000000005d
  10. rb .0 .1014 .74b0dc51 .00000000007c
  11. rb .0 .1014 .74b0dc51 .00000000007d
  12. rb .0 .1014 .74b0dc51 .00000000007e
  13. rb .0 .1014 .74b0dc51 .00000000009b
  14. rb .0 .1014 .74b0dc51 .0000000000ba
  15. rb .0 .1014 .74b0dc51 .0000000000d9
  16. rb .0 .1014 .74b0dc51 .0000000000f8
  17. rb .0 .1014 .74b0dc51 .0000000000ff
  18. rbd_directory

比刚刚多了13个文件,而且特别整齐还!观察这些文件的后缀,可以发现,后缀是以16进制进行编码的,那么从0x00 -> 0xff是多大呢,就是十进制的256,这个数字是不是很眼熟:

    size 1024 MB in 256 objects
 
 

可是这里只有13个文件,并没有256个啊,这就是RBD的精简置备的一个验证,刚刚创建foo的时候,一个都没有呢,而这里多出的13个,是因为刚刚格式化成xfs时生成的。我们着重关注索引值为0x00 & 0x01这两个碎片文件(Ceph Object):


 
 
  1. [root@ceph ~] # rados -p rbd get rb.0.1014.74b0dc51.000000000000 rb.0.1014.74b0dc51.000000000000
  2. [root@ceph ~] # rados -p rbd get rb.0.1014.74b0dc51.000000000001 rb.0.1014.74b0dc51.000000000001
  3. [root@ceph ~] # hexdump -vC rb.0.1014.74b0dc51.000000000000|more
  4. 00000000   58 46 53 42 00 00 10 00   00 00 00 00 00 04 00 00   |XFSB............|
  5. 00000010   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  6. 00000020  c5 79 ca f7 fc 5d 48 2d   81 5e 4c 75 29 3c 90 d3   |.y...]H-.^Lu)<..|
  7. ...
  8. [root@ceph ~] # ll rb.* -h
  9. -rw-r--r-- 1 root root 128K 1012 19 : 10 rb. 0. 1014.74b0dc51. 000000000000
  10. -rw-r--r-- 1 root root   16K 1012 19 : 10 rb. 0. 1014.74b0dc51. 000000000001
  11. [root@ceph ~] # file rb.0.1014.74b0dc51.000000000000
  12. rb. 0. 1014.74b0dc51. 000000000000: SGI XFS filesystem data (blksz 4096, inosz 256, v2 dirs)
  13. [root@ceph ~] # rbd export foo hahahaha
  14. Exporting image: 100% complete...done.
  15. [root@ceph ~] # file hahahaha
  16. hahahaha: SGI XFS filesystem data (blksz 4096, inosz 256, v2 dirs)
  17. [root@ceph ~] # hexdump -vC rb.0.1014.74b0dc51.000000000001
  18. 00000000   49 4e 41 ed 02 01 00 00   00 00 00 00 00 00 00 00   |INA.............|
  19. 00000010   00 00 00 02 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  20. 00000020   00 00 00 00 00 00 00 00   57 fe 15 be 0b cb a3 58   |........W......X|
  21. 00000030   57 fe 15 be 0b cb a3 58   00 00 00 00 00 00 00 06   |W......X........|
  22. 00000040   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  23. ...............太长了自己看~
  24. [root@ceph ~] # hexdump -vC rb.0.1014.74b0dc51.000000000001|grep IN |wc -l
  25. 64

这里的每一行输出都是很值得思考的,首先我们导出刚刚提到的两个对象,查看第一个对象,开头就是XFSB,可以验证这是刚刚mkfs.xfs留下来的,这时候查看文件大小,发现并没有4M那么大,别担心一会会变大的,值得关注的是file第0x00个对象,输出居然是XFS filesystem data,进一步验证了刚刚mkfs.xfs的足迹,这和整个块的file信息是一样的,我猜测xfs把文件系统核心信息就保存在块设备的最最前面的128KB。而后面的第0x01个对象里面的IN输出是64,我不负责任得猜想这个可能是传说中的inode。抛去猜想这里给到的结论是:

在使用块设备的容量后,会按需生成对应的对象,这些对象的共同点是:命名遵循 block_name_prefix+index, index range from [0x00, 0xff],而这个区间的大小正好是所有对象数的总和。

现在让我们把foo塞满:


 
 
  1. [ root@ceph ~] # dd if=/dev/zero of=/foo/full-me
  2. dd: 正在写入 "/foo/full-me": 设备上没有空间
  3. 记录了 2010449+ 0 的读入
  4. 记录了 2010448+ 0 的写出
  5. 1029349376字节( 1.0 GB)已复制, 37.9477 秒, 27.1 MB/秒
  6. [ root@ceph ~] # ceph -s
  7.      pgmap v81: 64 pgs, 1 pools, 994 MB data, 258 objects
  8.             10500 MB used, 27623 MB / 40188 MB avail

这里写了将近1G的数据,重点在后面的258 objects,如果理解了前面说的内容,这258个对象自然是由rbd_directoyfoo.rbd还有256个prefix+index对象构成的,因为我们用完了这个块的所有容量,所以自然就生成了所有的256的小4M对象。

写入文件

我们把环境恢复到foo被填满的上一步,也就是刚刚mkfs.xfsmount /dev/rbd0 /foo这里。向这个块里面写入文件:


 
 
  1. [root@ceph ~] # echo '111111111111111111111111111111111111111111' > /foo/file1.txt
  2. [root@ceph ~] # echo '222222222222222222222222222222222222222222' > /foo/file2.txt
  3. [root@ceph ~] # echo '333333333333333333333333333333333333333333' > /foo/file3.txt
  4. [root@ceph ~] # rados -p rbd get rb.0.106a.74b0dc51.000000000001 rb.0.106a.74b0dc51.000000000001

这里我之所以只导出了0x01这个对象,是因为我之前已经导出过所有的对象,经过对比后发现,在写入文件之后,只有这个文件的大小增大了diff之后,很快找到了写入的内容。


 
 
  1. [root@ceph ~] # hexdump -vC rb.0.106a.74b0dc51.000000000001
  2. 00000000   49 4e 41 ed 02 01 00 00   00 00 00 00 00 00 00 00   |INA.............|
  3. 00000010   00 00 00 02 00 00 00 00   00 00 00 00 00 00 00 01   |................|
  4. 00000020   00 00 00 00 00 00 00 00   57 fe 25 2f 32 6c 64 83   |........W.%/2ld.|
  5. 00000030   57 fe 25 2f 32 6c 64 83   00 00 00 00 00 00 00 36   |W.%/2ld........6|
  6. 00000040   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  7. 00000050   00 00 00 02 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  8. 00000060  ff ff ff ff 03 00 00 00   40 00 09 00 30 66 69 6c   |........@...0fil|
  9. 00000070   65 31 2e 74 78 74 00 00   40 03 09 00 48 66 69 6c   |e1.txt..@...Hfil|
  10. 0000008 0   65 32 2e 74 78 74 00 00   40 04 09 00 60 66 69 6c   |e2.txt..@...`fil|
  11. 0000009 0   65 33 2e 74 78 74 00 00   40 05 00 00 00 00 00 00   |e3.txt..@.......|
  12. 000000a 0   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  13. 000000b 0   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  14. 000000c 0   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  15. ...
  16. 00004000   31 31 31 31 31 31 31 31   31 31 31 31 31 31 31 31   |1111111111111111|
  17. 00004010   31 31 31 31 31 31 31 31   31 31 31 31 31 31 31 31   |1111111111111111|
  18. 00004020   31 31 31 31 31 31 31 31   31 31 0a 00 00 00 00 00   |1111111111......|
  19. 00004030   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  20. ...
  21. 00005000   32 32 32 32 32 32 32 32   32 32 32 32 32 32 32 32   |2222222222222222|
  22. 00005010   32 32 32 32 32 32 32 32   32 32 32 32 32 32 32 32   |2222222222222222|
  23. 00005020   32 32 32 32 32 32 32 32   32 32 0a 00 00 00 00 00   |2222222222......|
  24. 00005030   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  25. ...
  26. 00006000   33 33 33 33 33 33 33 33   33 33 33 33 33 33 33 33   |3333333333333333|
  27. 00006010   33 33 33 33 33 33 33 33   33 33 33 33 33 33 33 33   |3333333333333333|
  28. 00006020   33 33 33 33 33 33 33 33   33 33 0a 00 00 00 00 00   |3333333333......|
  29. 00006030   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|

这里我们找到了文件名和文件的内容,这很xfs!因为是xfs将这些文件和他们的内容组织成这样的格式的,再关注下每一行前面的编号,这里同样是16进制编码,如果之前我对IN == inode的猜测是对的话,所有的inodeIN都出现在索引范围为[0x00000000, 0x00004000)的区间,这个0x00004000的单位是byte转换过来就是16KB,这个小4M内的所有的inode都保存在前16KB区间。而文件出现的第一个索引为0x00004000,第二个在0x00005000,第三个在0x00006000,之间相差了0x1000 bytes也就是4096 bytes  == 4KB,还记得mkfs.xfs时的输出吗:


 
 
  1. [root@ceph ~]# mkfs.xfs /dev/rbd1
  2. meta-data=/dev/rbd1              isize= 256    agcount= 9, agsize= 31744 blks
  3.         =                       sectsz= 512   attr= 2, projid32bit= 1
  4.         =                       crc= 0        finobt= 0
  5. data     =                       bsize= 4096   blocks= 262144, imaxpct= 25
  6.         =                       sunit= 1024   swidth= 1024 blks
  7. naming   =version 2              bsize= 4096   ascii-ci= 0 ftype= 0
  8. log      = internal log           bsize= 4096   blocks= 2560, version= 2
  9.         =                       sectsz= 512   sunit= 8 blks, lazy- count= 1
  10. realtime = none                   extsz= 4096   blocks= 0, rtextents= 0

也就是这里的bsize = 4096使得文件之间间隔了4KB,很抱歉我的xfs知识还是一片空白,所以这里很多东西都靠猜,等我补完这一课会回来再做更正的。所以这里我们的结论就是:

RBD其实是一个完完整整的块设备,如果把1G的块想成一个1024层楼的高楼的话,xfs可以想象成住在这个大楼里的楼管,它只能在大楼里面,也就只能看到这1024层的房子,楼管自然可以安排所有的住户(文件or文件名),住在哪一层哪一间,睡在地板还是天花板(文件偏移量),隔壁的楼管叫做ext4,虽然住在一模一样的大楼里(foo or bar),但是它们有着自己的安排策略,这就是文件系统如果组织文件的一个比喻了,我们就不做深究,明白到这里就好了
然并卵,拆迁大队长跑来说,我不管你们(xfsorext4)是怎么安排的,盖这么高的楼是想上天了?,然后大队长把这1024层房子,每4层(4MB)砍了一刀,一共砍成了256个四层,然后一起打包带走了,运到了一个叫做Ceph的小区里面,放眼望去,这个小区里面的房子最高也就四层(填满的),有的才打了地基(还没写内容)。。。

这一节最主要的目的就是说明,在Ceph眼里,它并不关心这个RBD是做什么用处的,统统一刀斩成4M大小的对象,而使用这个RBD的用户(比如xfs),它只能从RBD里面操作,它可能将一个大文件从三楼写到了五楼,但是Ceph不管,直接从四楼砍一刀,文件分了就分了,反正每个小四层都有自己的编号(index),不会真的把文件给丢了。

最后再来个小操作(4210688=0x405000):


 
 
  1. [root@ceph ~] # cat /foo/file2.txt
  2. 222222222222222222222222222222222222222222
  3. [root@ceph ~] # echo Ceph>Ceph
  4. [root@ceph ~] # dd if=Ceph of=/dev/rbd0  seek=4210688  oflag=seek_bytes
  5. 记录了 0+ 1 的读入
  6. 记录了 0+ 1 的写出
  7. 5字节( 5 B)已复制, 0. 0136203 秒, 0. 4 kB/秒
  8. [root@ceph ~] # hexdump -Cv /dev/rbd0 -n 100 -s 0x405000
  9. 00405000   43 65 70 68 0a 32 32 32   32 32 32 32 32 32 32 32   |Ceph.22222222222|
  10. 00405010   32 32 32 32 32 32 32 32   32 32 32 32 32 32 32 32   |2222222222222222|
  11. 00405020   32 32 32 32 32 32 32 32   32 32 0a 00 00 00 00 00   |2222222222......|
  12. 00405030   00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   |................|
  13. [root@ceph ~] # cat /foo/file2.txt
  14. Ceph
  15. 2222222222222222222222222222222222222

AMAZING!,至少我是这么觉得的,我们通过查看index0x01的小4M文件,得知了file2.txt这个文件内容在这个小4M内保存的位置为0x5000,因为0x01前面还有一个小4M文件即0x00,那么这个file2.txt在整个RBD内的偏移量为4MB+0x5000B=0x400,000B+0x5000B=0x405000B=4210688B,也就是说保存在/dev/rbd0的偏移量为0x405000的位置,这时候用dd工具,直接向这个位置写入一个Ceph,再查看file2.txt的内容,果然,被修改了!

RBD组装

受到这篇文章启发,我们开始组装一个RBD,所谓组装,就是把刚刚的13个小4M碎片从集群中取出来,然后利用这13个文件,重建出一个1G的RBD块,这里的实验环境没有那篇文章里面那么苛刻,因为集群还是能访问的,不像文章里提到的集群已死,不能执行ceph指令,需要从OSD里面把碎片文件一个个捞出来。

一点思考: 使用find .这种方法太慢了,有一个事实是,可以从任何一个dead OSD 导出这个集群的CRUSHMAP,我有一个未证实的猜想,能否将这个CRUSHMAP注入到一个活的集群,然后这两个集群的ceph osd map <pool> <object>的输出如果一样的话:

  • live集群: ceph osd map <pool> foo.rbd,得到块名所存储的位置,前往dead集群找到之。

  • dead集群: 读取foo.rbd文件读取prefix。希望你还能记得这个块的大小。

  • live集群: 做个循环,读取ceph osd map <pool> prefix+index[0x00,Max_index]的输出,可以获取在dead集群的某个OSD的某个PG下面,这样就可以直接定位而不需要从find .结果来过滤了。

  • 只是猜想,未证实,这两天证实过再回来订正。

我们很快就可以把那13个文件拿出来了,现在开始组装:


 
 
  1. [root@ceph rbd]# ll
  2. 总用量 14636
  3. -rw-r--r-- 1 root root  131072 10月 13 11 :35 rb .0 .1078 .74b0dc51 .000000000000
  4. -rw-r--r-- 1 root root   28672 10月 13 11 :35 rb .0 .1078 .74b0dc51 .000000000001
  5. -rw-r--r-- 1 root root   16384 10月 13 11 :35 rb .0 .1078 .74b0dc51 .00000000001f
  6. -rw-r--r-- 1 root root   16384 10月 13 11 :35 rb .0 .1078 .74b0dc51 .00000000003e
  7. -rw-r--r-- 1 root root   16384 10月 13 11 :35 rb .0 .1078 .74b0dc51 .00000000005d
  8. -rw-r--r-- 1 root root 4194304 10月 13 11 :36 rb .0 .1078 .74b0dc51 .00000000007c
  9. -rw-r--r-- 1 root root 4194304 10月 13 11 :36 rb .0 .1078 .74b0dc51 .00000000007d
  10. -rw-r--r-- 1 root root 2129920 10月 13 11 :36 rb .0 .1078 .74b0dc51 .00000000007e
  11. -rw-r--r-- 1 root root   16384 10月 13 11 :36 rb .0 .1078 .74b0dc51 .00000000009b
  12. -rw-r--r-- 1 root root   16384 10月 13 11 :36 rb .0 .1078 .74b0dc51 .0000000000ba
  13. -rw-r--r-- 1 root root   16384 10月 13 11 :36 rb .0 .1078 .74b0dc51 .0000000000d9
  14. -rw-r--r-- 1 root root   16384 10月 13 11 :36 rb .0 .1078 .74b0dc51 .0000000000f8
  15. -rw-r--r-- 1 root root 4194304 10月 13 11 :36 rb .0 .1078 .74b0dc51 .0000000000ff

组装的基本思想就是,先搭建一个1024层的大楼,然后,把刚刚的13个四层根据它的index,安插到对应的楼层,缺少的楼层就空在那就好了,脚本来自刚刚的文章,我对其进行了一小部分的修改,使之适合我们这个实验,脚本将输出一个名为foo的块:


 
 
  1. #!/bin/sh
  2. # Rados object size 这是刚刚的4M的大小
  3. obj_size=4194304
  4. # DD bs value
  5. rebuild_block_size=512
  6. #rbd="${1}"
  7. rbd= "foo"   #生成的块名
  8. #base="${2}" #prefix
  9. base= "rb.0.1078.74b0dc51"
  10. #rbd_size="${3}" #1G
  11. rbd_size= "1073741824"
  12. base_files=$(ls -1 ${base}.* 2>/dev/null | wc -l | awk '{print $1}')
  13. if [ ${base_files} -lt 1 ]; then
  14.   echo "COULD NOT FIND FILES FOR ${base} IN $(pwd)"
  15.   exit
  16. fi
  17. # Create full size sparse image.  Could use truncate, but wanted
  18. # as few required files and dd what a must.
  19. dd if=/dev/zero of= ${rbd} bs=1 count=0 seek= ${rbd_size} 2>/dev/null
  20. for file_name in $(ls -1 ${base}.* 2>/dev/null); do
  21.  seek_loc=$( echo ${file_name} | awk -F_ '{print $1}' | awk -v os= ${obj_size} -v rs= ${rebuild_block_size} -F. '{print os*strtonum("0x" $NF)/rs}')
  22.  dd conv=notrunc if= ${file_name} of= ${rbd} seek= ${seek_loc} bs= ${rebuild_block_size} 2>/dev/null
  23. done

从上面的脚本就可以看出,这是一个填楼工程,将其填完之后,我们来看得到的foo文件:


 
 
  1. [root@ceph rbd] # file foo
  2. foo: SGI XFS filesystem data (blksz 4096, inosz 256, v2 dirs)
  3. [root@ceph rbd] # du -sh foo
  4. 15M    foo
  5. [root@ceph rbd] # ll  -h foo
  6. -rw-r--r-- 1 root root 1.0G 1013 12: 26 foo

这时候,我们挂载会出现一个小问题,uuid重复:


 
 
  1. [ root@ceph rbd] # mount foo /mnt
  2. mount: 文件系统类型错误、选项错误、/dev/loop0 上有坏超级块、
  3.       缺少代码页或助手程序,或其他错误
  4.       有些情况下在 syslog 中可以找到一些有用信息- 请尝试
  5.       dmesg | tail  这样的命令看看。
  6. [ root@ceph rbd] # dmesg |tail
  7. [ 3270374.155472] XFS (loop0): Filesystem has duplicate UUID 1c4b010c-a2d8 -4615 -8307-be5419d94add - can 't mount

原因很简单,还记得我们刚刚操作时的mount /dev/rbd0 /foo吗? foo块是/dev/rbd0的克隆,所以fooUUID是和/dev/rbd0的是一样的,这时候我们umount /foo即可:


 
 
  1. [root@ceph rbd] # umount  /foo/
  2. [root@ceph rbd] # mount foo /mnt
  3. [root@ceph rbd] # ls /mnt/
  4. file1.txt  file2.txt  file3.txt
  5. [root@ceph rbd] # cat /mnt/file2.txt
  6. Ceph
  7. 2222222222222222222222222222222222222

神奇吧,我们用碎片文件组装了一个完完整整的RBD块,能和Ceph里map出来的RBD一样使用,并且数据也是一样的,相信如果理解了前几节的内容,对这个实验的结果就不会很意外了。

Format 1 VS Format 2

众所周知,RBD有两种格式:

  • Format 1: Hammer以及Hammer之前默认都是这种格式,并且rbd_default_features = 3.
  • Format 2: Jewel默认是这种格式,并且rbd_default_features = 61.

关于features的问题将在下节解释,这里我们只讨论这两种格式的RBD在ceph底层的存储有什么区别,首先安装一个Jewel版本的环境(或者改配置项),方法很简单:

 
      
      
		<p>1</p>

		<p>2</p>

		<p>3</p>

		<p>4</p>

		<p>5</p>

		<p>6</p>

		<p>7</p>

		<p>8</p>

		<p>9</p>

		<p>10</p>

		<p>11</p>

		<p>12</p>

		<p>13</p>

		<p>14</p>

		<p>15</p>

		<p>16</p>

		<p>17</p>

		<p>18</p>

		<p>19</p>

		<p>20</p>

		<p>21</p>

		<p>22</p>
		</td>
		<td>
		<pre>

 

		<p>[root@ceph install-ceph-in-one-minute]# cd /root/install-ceph-in-one-minute/</p>

		<p>[root@ceph install-ceph-in-one-minute]# ./cleanup.sh</p>

		<p>kill: 向 945 发送信号失败: 没有那个进程</p>

		<p>root 955 943 0 13:18 pts/4 00:00:00 grep ceph</p>

		<p>root 20960 2 0 10月11 ? 00:00:00 [ceph-msgr]</p>

		<p>root 22052 2 0 10月12 ? 00:00:00 [ceph-watch-noti]</p>

		<p>umount: /var/lib/ceph/osd/ceph-0:未挂载</p>

		<p>[root@ceph install-ceph-in-one-minute]# sed -i 's/hammer/jewel/g' install.sh</p>

		<p>[root@ceph install-ceph-in-one-minute]# ./install.sh</p>

		<p>...</p>

		<p>[root@ceph ~]# ceph -s</p>

		<p>cluster 04949396-488c-4244-a141-b2ae6de3ed38</p>

		<p>health HEALTH_OK</p>

		<p>monmap e1: 1 mons at {ceph=233.233.233.233:6789/0}</p>

		<p>election epoch 3, quorum 0 ceph</p>

		<p>osdmap e5: 1 osds: 1 up, 1 in</p>

		<p>flags sortbitwise</p>

		<p>pgmap v15: 64 pgs, 1 pools, 0 bytes data, 0 objects</p>

		<p>7234 MB used, 30889 MB / 40188 MB avail</p>

		<p>64 active+clean</p>

		<p>[root@ceph ~]# ceph -v</p>

		<p>ceph version 10.2.3 (ecc23778eb545d8dd55e2e4735b53cc93f92e65b)</p>
		</td>
	</tr></tbody></table></div><p>创建一个<code>foo</code>块,并观察集群多出了哪些文件:</p>
 
      
      
		<p>1</p>

		<p>2</p>

		<p>3</p>

		<p>4</p>

		<p>5</p>

		<p>6</p>

		<p>7</p>

		<p>8</p>

		<p>9</p>

		<p>10</p>

		<p>11</p>

		<p>12</p>

		<p>13</p>

		<p>14</p>

		<p>15</p>

		<p>16</p>

		<p>17</p>

		<p>18</p>

		<p>19</p>

		<p>20</p>

		<p>21</p>

		<p>22</p>

		<p>23</p>

		<p>24</p>

		<p>25</p>

		<p>26</p>

		<p>27</p>

		<p>28</p>

		<p>29</p>

		<p>30</p>
		</td>
		<td>
		<pre>

 

		<p>[root@ceph ~]# rbd create foo --size 1024</p>

		<p>[root@ceph ~]# rbd info foo</p>

		<p>rbd image 'foo':</p>

		<p>size 1024 MB in 256 objects</p>

		<p>order 22 (4096 kB objects)</p>

		<p>block_name_prefix: rbd_data.101474b0dc51</p>

		<p>format: 2</p>

		<p>features: layering, exclusive-lock, object-map, fast-diff, deep-flatten</p>

		<p>flags:</p>

		<p>[root@ceph ~]# rados -p rbd ls</p>

		<p>rbd_object_map.101474b0dc51</p>

		<p>rbd_header.101474b0dc51</p>

		<p>rbd_id.foo</p>

		<p>rbd_directory</p>

		<p>[root@ceph ~]# rbd map foo</p>

		<p>rbd: sysfs write failed</p>

		<p>RBD image feature set mismatch. You can disable features unsupported by the kernel with "rbd feature disable".</p>

		<p>In some cases useful info is found in syslog - try "dmesg | tail" or so.</p>

		<p>rbd: map failed: (6) No such device or address</p>

		<p>[root@ceph ~]# rbd feature disable foo deep-flatten</p>

		<p>[root@ceph ~]# rbd feature disable foo fast-diff</p>

		<p>[root@ceph ~]# rbd feature disable foo object-map</p>

		<p>[root@ceph ~]# rbd feature disable foo exclusice-lock</p>

		<p>[root@ceph ~]# rbd map foo</p>

		<p>/dev/rbd1</p>

		<p>[root@ceph ~]# rados -p rbd ls</p>

		<p>rbd_header.101474b0dc51</p>

		<p>rbd_id.foo</p>

		<p>rbd_directory</p>
		</td>
	</tr></tbody></table></div><p>多出了四个文件,在关闭<code>object-map</code>属性后,少了一个<code>rbd_object_map.101474b0dc51</code>文件,我们查看剩下的三个文件内容:</p>
 
      
      
		<p>1</p>

		<p>2</p>

		<p>3</p>

		<p>4</p>

		<p>5</p>

		<p>6</p>

		<p>7</p>

		<p>8</p>
		</td>
		<td>
		<pre>

 

		<p>[root@ceph ~]# rados -p rbd get rbd_header.101474b0dc51 rbd_header.101474b0dc51</p>

		<p>[root@ceph ~]# rados -p rbd get rbd_id.foo rbd_id.foo</p>

		<p>[root@ceph ~]# rados -p rbd get rbd_directory rbd_directory</p>

		<p>[root@ceph ~]# hexdump -Cv rbd_directory</p>

		<p>[root@ceph ~]# hexdump -Cv rbd_header.101474b0dc51</p>

		<p>[root@ceph ~]# hexdump -Cv rbd_id.foo</p>

		<p>00000000 0c 00 00 00 31 30 31 34 37 34 62 30 64 63 35 31 |....101474b0dc51|</p>

		<p>00000010</p>
		</td>
	</tr></tbody></table></div><p>可以发现,<code>rbd_directory</code>不再保存所有RBD的名称,相比于<code>format1的 foo.rbd</code>,<code>format2</code>采用<code>rbd_id.rbdName</code>的形式保存了这个块的<code>prefix</code>,而另一个文件<code>rbd_header,xxxxxxxxprefix</code>显示的保存了这个<code>prefix</code>,我们再向这个块写入点文件:</p>
 
      
      
		<p>1</p>

		<p>2</p>

		<p>3</p>

		<p>4</p>

		<p>5</p>

		<p>6</p>

		<p>7</p>

		<p>8</p>

		<p>9</p>

		<p>10</p>

		<p>11</p>

		<p>12</p>

		<p>13</p>

		<p>14</p>

		<p>15</p>

		<p>16</p>

		<p>17</p>

		<p>18</p>

		<p>19</p>

		<p>20</p>
		</td>
		<td>
		<pre>

 

		<p>[root@ceph ~]# mkfs.xfs /dev/rbd1</p>

		<p>[root@ceph ~]# ceph -s</p>

		<p>pgmap v187: 64 pgs, 1 pools, 14624 kB data, 16 objects</p>

		<p>[root@ceph ~]# rados -p rbd ls|sort</p>

		<p>rbd_data.101474b0dc51.0000000000000000</p>

		<p>rbd_data.101474b0dc51.0000000000000001</p>

		<p>rbd_data.101474b0dc51.000000000000001f</p>

		<p>rbd_data.101474b0dc51.000000000000003e</p>

		<p>rbd_data.101474b0dc51.000000000000005d</p>

		<p>rbd_data.101474b0dc51.000000000000007c</p>

		<p>rbd_data.101474b0dc51.000000000000007d</p>

		<p>rbd_data.101474b0dc51.000000000000007e</p>

		<p>rbd_data.101474b0dc51.000000000000009b</p>

		<p>rbd_data.101474b0dc51.00000000000000ba</p>

		<p>rbd_data.101474b0dc51.00000000000000d9</p>

		<p>rbd_data.101474b0dc51.00000000000000f8</p>

		<p>rbd_data.101474b0dc51.00000000000000ff</p>

		<p>rbd_directory</p>

		<p>rbd_header.101474b0dc51</p>

		<p>rbd_id.foo</p>
		</td>
	</tr></tbody></table></div><p>可以发现,生成的13个小4M文件的后缀和<code>format 1</code>的是一模一样的,只是命名规则变成了<code>rbd_data.[prefix].+[index]</code>,之所以后缀是一样的是因为<code>xfs</code>对于1G的块总是会这样构建文件系统,所以对于<code>F1 &amp; F2</code>,这些小4M除了命名规范不一样外,实际保存的内容都是一样的,下面对这两种格式进行简要的对比总结:</p>
格式rbd_diretoryIDprefixData
Format 1保存了所有RBD的名称foo.rbd在ID中形如 rb.0.1014.74b0dc51.000000000001
Format 2rbd_id.foo在ID中
并rbd_header.prefix显示输出
形如 rbd_data.101474b0dc51.0000000000000001

FAQ

rbd feature disable

最常见的RBD使用问题,一般出现于Format 2rbd map操作,需要关闭如下属性即可,注意顺序

 
      
      
		<p>1</p>

		<p>2</p>

		<p>3</p>

		<p>4</p>
		</td>
		<td>
		<pre>

 

		<p>[root@ceph ~]# rbd feature disable foo deep-flatten</p>

		<p>[root@ceph ~]# rbd feature disable foo fast-diff</p>

		<p>[root@ceph ~]# rbd feature disable foo object-map</p>

		<p>[root@ceph ~]# rbd feature disable foo exclusice-lock</p>
		</td>
	</tr></tbody></table></div><p>更详细的说明可以<a href="http://t.cn/RtlWp4X" rel="nofollow">参考这篇文章</a>。</p>

100TB的RBD要删几个月?

是的,之前看到磨渣调试的log发现,rbd rm foo的时候,是从index的0开始一个个得往后面删,所以100TB 的块有两千六百万个4M碎片组成,虽然实际上并没有用到这么多,但是一个个删的话,还是相当慢的,如果是Format 1我们可以rados ls|grep block_name_prefix |xargs rados rm这种思路删除,再删掉foo.rbd文件,再执行rbd rm foo就可以很快删掉了。这里有一篇很详尽的文章介绍了删除方法。

为什么总是4M的块

rbd_default_order = 22这个配置项决定了切块的大小,默认值为22,参考了这篇文章我得到了ordersize的几个关系:

ordersize
238M
224M
212M
201M

大概关系就是这样,暂时没找更详细的介绍,man rbd能看到更详细的信息。

订正: size = 2 ** order bytes 也就是2的order次方。

Features 编号

配置项为rbd_default_features = [3 or 61],这个值是由几个属性加起来的:

  • only applies to format 2 images
  • +1 for layering,
  • +2 for stripingv2,
  • +4 for exclusive lock,
  • +8 for object map
  • +16 for fast-diff,
  • +32 for deep-flatten,
  • +64 for journaling

所以61=1+4+8+16+32就是layering | exclusive lock | object map |fast-diff |deep-flatten这些属性的大合集,需要哪个不需要哪个,做个简单的加法配置好rbd_default_features就可以了。

总结

这篇文章还是有点残缺的:

  • RBD碎片在底层是以rbd\udata.101474b0dc51.0000000000000000__head_DDFD75CF__0这种命名方式保存的。
  • xfsinode猜测为证实。
  • CRUSHMAP的平移注入是否会产生相同的ceph osd map结果。
    • 答案:会产生相同的结果,但是不需要平移,只需要一个osdmap就好了。

慢慢来,等后面深入学习了相关知识之后,再来回头增添。。。

最简单的话来总结下RBD:Client从RBD内部看,RBD是一个整体,Ceph从RBD外部看,是若干的碎片。

Enjoy It!

转自:https://mp.weixin.qq.com/s?__biz=MzA3NjkwNjM4Nw==&mid=2651867888&idx=1&sn=7fc582e5206abd03e39ef35370d24bb1&chksm=84bec00cb3c9491a09379c7ba1effaa47790b39732ea460e6b2aa206f71edadac5dc4a547245&scene=21#wechat_redirect

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值