rbd对象探究
1. rbd镜像的创建与使用
为了更直观的感受rbd对象,本文从创建rbd池开始,构建了一个模拟的rbd使用环境,并以此探究rbd的对象。
首先是rbd池的创建与使用:
服务端:
1.创建rbd池
[root@node-1 ~]# ceph osd pool create rbd-pool 64 64 && rbd pool init rbd-pool
2.创建块设备镜像
[root@node-1 ~]# rbd create --size 1024 rbd-pool/rbd-pool-image-1
3.拷贝密钥文件,或者创建新用户,这里直接拷贝admin密钥及配置
[root@node-1 ~]# scp –r /etc/ceph 192.168.159.132:/etc/
客户端:
1.注意更新ceph yum源
2.安装ceph-common
[root@192.168.159.132 ~]# yum install ceph-common –y
3.挂载块设备镜像
[root@192.168.159.132 ~]# rbd map rbd-pool/rbd-pool-image-1
4.格式化,仅第一次挂载需要,块设备名称注意更改
[root@192.168.159.132 ~]# mkfs.xfs /rbd /dev/rbd0
5.挂载到目录
[root@192.168.159.132 ~]# mkdir /mnt/rbd0 && mount /dev/rbd0 /mnt/rbd0
6.卸载
[root@192.168.159.132 ~]# umount /mnt/rbd0 && rbd unmap rbd-pool/rbd-pool-image-1
注意:在rbd map时可能会报错,因为内核特性不支持。
rbd: sysfs write failed
RBD image feature set mismatch. You can disable features unsupported by the kernel with "rbd feature disable rbd-pool/rbd-pool-image-1 object-map fast-diff deep-flatten".
In some cases useful info is found in syslog - try "dmesg | tail".
rbd: map failed: (6) No such device or address
这里根据提示关闭某些特性即可。
2. 当我们创建镜像时,创建了什么?
[root@node-1 ~]# rados -p rbd-pool ls
rbd_object_map.20cc292949764 # rbd特性信息
rbd_header.20cc292949764 #image元数据
rbd_directory # 记录该rbd池中含有的image_name,id
rbd_info # rbd信息
rbd_id.rbd-pool-image-1 #此镜像的id号
通过以上命令及其结果可以看出,相关含义在《Ceph设计原理与实现》一书中有详细介绍,当我们创建完镜像后,rbd-pool中多了几个对象。其含义可在src/include/rbd_types.h文档中查看。
/* New-style rbd image 'foo' consists of objects
rbd_id.foo - id of image
rbd_header.<id> - image metadata
rbd_object_map.<id> - optional image object map
rbd_data.<id>.00000000
rbd_data.<id>.00000001
... - data
*/
目前rbd镜像中还没有文件,因此大部分对象是空的。通过“rados -p rbd-pool get <obj_name> <output_file_name>”命令可以导出对象。
经过查看,发现rbd_id.rbd-pool-image-1保存了镜像的id:20cc292949764。
rbd_info中保存了:overwrite validated。
rbd_object_map.20cc292949764中保存了:
{
"size": 256,
"bit_table": [
"0x00",
"0x00",
…
}//用于支持 object-map特性,具体作用在实践中遇到再来补充。
这里给出查看rbd_object_map命令: ceph-dencoder import ./rbd_object_map.20cc292949764 type BitVector<2> decode dump_json。
3. 向rbd写入一个文件后,实际写入的是什么?
在mkfs块设备后,我们查看下rbd-pool中的对象。
[root@node-1 data]# rados -p rbd-pool ls
rbd_data.20e5ff0224ec0.00000000000000e0 #image对象信息
rbd_directory #rbd管理image对象信息
rbd_header.20e5ff0224ec0 #image元数据
rbd_data.20e5ff0224ec0.0000000000000060 #
rbd_info #rbd信息
rbd_data.20e5ff0224ec0.0000000000000001
rbd_data.20e5ff0224ec0.00000000000000ff
rbd_data.20e5ff0224ec0.0000000000000080
rbd_id.rbd-pool-image-1 #记录image_name和image_id的映射关系
rbd_data.20e5ff0224ec0.00000000000000c0
rbd_data.20e5ff0224ec0.0000000000000082
rbd_data.20e5ff0224ec0.0000000000000020
rbd_data.20e5ff0224ec0.0000000000000040
rbd_data.20e5ff0224ec0.0000000000000081
rbd_data.20e5ff0224ec0.0000000000000000
rbd_data.20e5ff0224ec0.00000000000000a0
发现增加了12个rbd_data对象。rbd被xfs格式化以后会产生一些对象,这些对象是以16进制名称的方式存储在后台的,也就是rbd大小一定的情况下对象数目是一定的,也就是名称也是一定的。在后续的向文件中写入操作的过程中,还会发现,对象依然是从rbd_data.*****00的对象开始写入。这个实际和客户端格式化的文件系统有关。在《从ceph对象中提取rbd中的指定文件》一文中可以得知:rbd存储对象和文件系统的sectors是一一对应的。
客户端查看分区设备信息:
[root@localhost ~]# parted -s /dev/rbd0 unit s print
Model: 未知 (unknown)
Disk /dev/rbd0: 2097152s
Sector size (logical/physical): 512B/512B
Partition Table: loop
Disk Flags:
Number Start End Size File system 标志
- 1 0s 2097151s 2097152s xfs*
上面结果中“Start”对应的“0s”表示该分区设备从第0块sector开始写入,每块sector大小512B,共有2097152s 块。
服务端查看镜像信息:
[root@node-1 ~]# rbd info rbd-pool/rbd-pool-image-1
rbd image 'rbd-pool-image-1':
size 1 GiB in 256 objects #大小1GB,平均分配给256个对象
order 22 (4 MiB objects) # 每个对象大小4MB
snapshot_count: 0
id: 20e5ff0224ec0
block_name_prefix: rbd_data.20e5ff0224ec0
format: 2 # image 1 基本上不用
features: layering, exclusive-lock
op_features: # 该image支持的特性
flags:
create_timestamp: Wed May 26 16:42:36 2021
access_timestamp: Wed May 26 16:42:36 2021
modify_timestamp: Wed May 26 16:42:36 2021
上述结果可知镜像对应的块设备大小1GB,平均分配到256个对象,每个4MB。
简单计算可以得出:sector数量 * 单个sector大小 = 镜像大小
2097152 * 512 / 1024 / 1024 = 1024 MB = 1GB。
因此2097152个sector和image中的256个对象存在对应关系。已知sector从0块开始写入,那么obj应该也从0开始增长,写满4MB后,换到新的obj。
我在这里使用dd命令在客户端写入一个4MB对象,然后再去查看pool中对象信息。
这里我们在客户端上挂载镜像后,向该目录中写入一个无意义对象。
[root@localhost mnt]# dd if=/dev/zero of=/mnt/rbd0/file1 count=1 bs=4MB
然后,观察rbd-pool对象情况。
[root@node-1 ~]# rados -p rbd-pool ls
rbd_data.20e5ff0224ec0.00000000000000e0
rbd_directory
rbd_header.20e5ff0224ec0
rbd_data.20e5ff0224ec0.0000000000000060
rbd_info
rbd_data.20e5ff0224ec0.0000000000000001
rbd_data.20e5ff0224ec0.00000000000000ff
rbd_data.20e5ff0224ec0.0000000000000080
rbd_id.rbd-pool-image-1
rbd_data.20e5ff0224ec0.00000000000000c0
rbd_data.20e5ff0224ec0.0000000000000082
rbd_data.20e5ff0224ec0.0000000000000020
rbd_data.20e5ff0224ec0.0000000000000040
rbd_data.20e5ff0224ec0.0000000000000081
rbd_data.20e5ff0224ec0.0000000000000000
rbd_data.20e5ff0224ec0.00000000000000a0
似乎没有变化,但其实rbd_data.******00对象的大小增加了,先按下不表,我们看下此时rbd_data.******00对象的大小。
[root@node-1 ~]# rados -p rbd-pool stat rbd_data.20e5ff0224ec0.0000000000000000
rbd-pool/rbd_data.20e5ff0224ec0.0000000000000000 mtime 2021-05-26 18:22:26.000000, size 4034560
经查询,rbd_data.******00对象大小为4034560B。
我们再向客户端挂载的文件目录中写入一个有意义的小对象。
[root@localhost rbd0]# vi hellorbd
hello rbd
然后,查看hellorbd文件在块设备中的位置,顺便也可以看下file1的位置。
[root@localhost rbd0]# xfs_bmap -lvp /mnt/rbd0/hellorbd
/mnt/rbd0/hellorbd:
- EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS*
0: [0..7]: 7880..7887 0 (7880..7887) 8 01111
[root@localhost rbd0]# xfs_bmap -lvp /mnt/rbd0/file1
/mnt/rbd0/file1:
- EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS*
0: [0..7815]: 64..7879 0 (64..7879) 7816 01111
可以看出hellorbd在sector中分布在[7880~7887],file1为[64~7879]。hellorbd紧跟着file1,并且file1并非从0开始,而是从64开始,说明前面的sector已经被使用,这段64个sector(即 64 * 512 B)长度空间在格式化文件系统的时候已经被占用了。还有一点值得吐槽的是:dd命令中的4MB,实际写入大小只有4000000B。
根据hellorbd的位置信息,可以计算出它存储在在rbd-pool/rbd-pool-image-1中的对象的具体位置。
计算方法:
1.计算出hellorbd到rbd-pool对象的映射
sectors数量 / 对象数量 = 单个对象的sectors范围
2097152/256 = 8192
根据 hellorbd:[7880~7887],可以得出hellorbd在第0个对象中:rbd_data.******00。
2.计算出hellorbd在rbd_data对象中偏移量
(7880 - (0 * 8192))* 512B = 4030560
4034560!还记得我们上文中在写入file1之后,查看过rbd_data.******00对象的大小吗?就是4034560。
接下来使用rados命令和dd命令在服务端还原出rbdhello。
[root@node-1 ~]# rados -p rbd-pool get rbd_data.20e5ff0224ec0.0000000000000000 ./rbd_data.20e5ff0224ec0.0000000000000000
[root@node-1 ~]# dd if=rbd_data.20e5ff0224ec0.0000000000000000 of=hellorbd bs=512 count=8 skip=7880
- [root@node-1 ~]# cat hellorbd*
hello rbd
或者,
[root@node-1 ~]# dd if=rbd_data.20e5ff0224ec0.0000000000000000 of=hellorbd-1 bs=1 count=4096 skip=4034560
[root@node-1 ~]# cat hellorbd-1
hello rbd
可以得出,块设备在rbd中被切割成一个个对象,保存在rbd_data对象中,这样使得rbd管理起块设备的数据极为方便,而且也达到到了瘦分配的特性。rbd_data中还保存着客户端中这个设备的信息(文件系统,大小等),这个客户端在挂载时,可以直接把image映射为一个块设备,其所有读写操作的本质都是rbd_data。