从ceph对象中提取RBD中的指定文件

前言

之前有个想法,是不是有办法找到rbd中的文件与对象的关系,想了很久但是一直觉得文件系统比较复杂,在fs 层的东西对ceph来说是透明的,并且对象大小是4M,而文件很小,可能在fs层进行了合并,应该很难找到对应关系,最近看到小胖有提出这个问题,那么就再次尝试了,现在就是把这个实现方法记录下来

这个提取的作用个人觉得最大的好处就是一个rbd设备,在文件系统层被破坏以后,还能够从rbd提取出文件,我们知道很多情况下设备的文件系统一旦破坏,无法挂载,数据也就无法读取,而如果能从rbd中提取出文件,这就是保证了即使文件系统损坏的情况下,数据至少不丢失

本篇是基于xfs文件系统情况下的提取,其他文件系统有时间再看看,因为目前使用的比较多的就是xfs文件系统

本篇也回答了一个可能会经常被问起的问题,能告诉我虚拟机里面的文件在后台存储在哪里么,看完本篇就知道存储在哪里了

XFS文件系统介绍

1
2
3
4
5
6
7
8
9
10
11
[root@lab8106 ~]# mkfs.xfs -f /dev/rbd0p1 
warning: device is not properly aligned /dev/rbd0p1
meta-data=/dev/rbd0p1 isize=256 agcount=9, agsize=162816 blks
= sectsz=512 attr=2, projid32bit=1
= crc=0 finobt=0
data = bsize=4096 blocks=1310475, imaxpct=25
= sunit=1024 swidth=1024 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=0
log =internal log bsize=4096 blocks=2560, version=2
= sectsz=512 sunit=8 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0

XFS文件系统采取是AG管理的,每个AG维护自己的inode和数据,所以XFS文件系统是一种很容易扩展的文件系统,本篇里面主要用到的命令是xfs_bmap这个命令

1
2
3
4
[root@lab8106 ~]# xfs_bmap -lvp /etc/fstab
/etc/fstab:
EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS
0: [0..7]: 26645424..26645431 1 (431024..431031) 8 00000

一个文件最小就是8个block(512b),也就是4k,这个因为上面默认的xfs的格式化就是data bsize=4K,这个值可以自行调整的,本篇尽量用默认常规的参数来讲例子

查看man xfs_bmap这个命令可以看到:

Holes are marked by replacing the startblock..endblock with hole. All the file offsets and disk blocks are in units of 512-byte blocks, no matter what the filesystem’s block size is.

意思是这个查询到的里面的计数单位都是512-byte,不管上层设置的block大小是多少,我们知道文件系统底层的sector就是512-byte,所以这个查询到的结果就可以跟当前的文件系统的sector的偏移量联系起来,这里强调一下,这个偏移量的起始位子为当前文件系统所在分区的偏移量,如果是多分区的情况,在计算整个偏移量的时候就要考虑分区的偏移量了,这个会在后面用实例进行讲解的

rbd的对象是不清楚内部分区的偏移量,所以在rbd层进行提取的时候是需要得到的是分区当中的文件相对整个磁盘的一个sector的偏移量

rbd的对象结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@lab8106 ~]# rados -p rbd ls|grep data
rbd_data.25a636b8b4567.00000000000009ff
rbd_data.25a636b8b4567.00000000000001dd
rbd_data.25a636b8b4567.0000000000000000
rbd_data.25a636b8b4567.000000000000009f
rbd_data.25a636b8b4567.0000000000000459
rbd_data.25a636b8b4567.000000000000027e
rbd_data.25a636b8b4567.00000000000004ff
rbd_data.25a636b8b4567.000000000000027c
rbd_data.25a636b8b4567.000000000000027d
rbd_data.25a636b8b4567.0000000000000001
rbd_data.25a636b8b4567.000000000000013e
rbd_data.25a636b8b4567.00000000000003ba
rbd_data.25a636b8b4567.000000000000031b
rbd_data.25a636b8b4567.00000000000004f8

rbd被xfs格式化以后会产生一些对象,这些对象是以16进制名称的方式存储在后台的,也就是rbd大小一定的情况下对象数目是一定的,也就是名称也是一定的

1
2
3
4
5
6
7
8
9
10
[root@lab8106 ~]# parted -s /dev/rbd0 unit s print
Model: Unknown (unknown)
Disk /dev/rbd0: 20971520s
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number Start End Size File system Name Flags
1 1953s 10485759s 10483807s xfs primari
2 10485760s 20963327s 10477568s primari

上面可以看到rbd0的sector个数为20971520s
20971520s*512byte=10737418240byte=10485760KB=10240MB
sector的大小一定,总rbd大小一定的情况下sector的数目也是一定的,本篇实例的rbd大小

1
2
3
4
5
6
7
8
9
[root@lab8106 ~]# rbd info zp
rbd image 'zp':
size 10000 MB in 2500 objects
order 22 (4096 kB objects)
block_name_prefix: rbd_data.25a776b8b4567
format: 2
features: layering
flags:
create_timestamp: Sat Jul 22 18:04:12 2017

sector和ceph object的对应关系的查询

这个就像个map一样,需要把这个关系给找到,一个sector的区间对应到object的map,这里我用python写个简单的方法来做查询,也可以自己用其他语言来实现

首先查询到rbd的对象数目

1
2
3
4
5
6
7
8
9
[root@lab8106 ~]# rbd info zp
rbd image 'zp':
size 10000 MB in 2500 objects
order 22 (4096 kB objects)
block_name_prefix: rbd_data.25a776b8b4567
format: 2
features: layering
flags:
create_timestamp: Sat Jul 22 18:04:12 2017

处理脚本如下:

1
vim getsecob.py

添加下面内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#! /bin/python
# *-* conding=UTF-8 *-*

import commands

def main():
getmap(2500)


def getmap(object):
sector=int(object)*4096*1024/512
print "object:"+str(object)
print "sector:"+str(sector)
incre=sector/object
for item in range(int(object)):
a=int(item*8192)
b=int((item+1)*8192-1)
print str([a,b])+" --> "+"%016x" %item

if __name__ == '__main__':
main()

其中getmap后面为对象数目
输出是这个形式的:

1
2
3
4
5
6
7
8
9
10
[root@lab8106 ~]# python getsecob.py
object:2500
sector:20480000
[0, 8191] --> 0000000000000000
[8192, 16383] --> 0000000000000001
[16384, 24575] --> 0000000000000002
[24576, 32767] --> 0000000000000003
[32768, 40959] --> 0000000000000004
[40960, 49151] --> 0000000000000005
···

对rbd0进行分区,分区后的结果如下

1
2
3
4
5
6
7
8
9
10
[root@lab8106 ~]# parted -s /dev/rbd0 unit s print
Model: Unknown (unknown)
Disk /dev/rbd0: 20480000s
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number Start End Size File system Name Flags
1 1953s 10240000s 10238048s primari
2 10248192s 20471807s 10223616s primari

这个是个测试用的image,大小为10G分成两个5G的分区,现在我们在两个分区里面分别写入两个测试文件,然后经过计算后,从后台的对象中把文件读出

1
2
3
4
mount /dev/rbd0p1 /mnt1
mount /dev/rbd0p2 /mnt2
cp /etc/fstab /mnt1
cp /etc/hostname /mnt2

首先获取文件在分区上的sector的偏移量

1
2
3
4
[root@lab8106 ~]# xfs_bmap -lvp /mnt1/fstab 
/mnt1/fstab:
EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS
0: [0..7]: 8224..8231 0 (8224..8231) 8 01111

可以得到是(8224..8231)共8个sector
从上面的分区1的start的sector可以知道起始位置是1953,那么相对于磁盘的偏移量就变成了

(8224+1953..8231+1953) = (10177..10184)

这里说下,这个地方拿到偏移量后,直接通过对rbd设备进行dd读取也可以把这个文件读取出来,这个顺带讲下,本文主要是从对象提取:

1
dd if=/dev/rbd0 of=a bs=512 count=8 skip=10177

bs取512是因为sector的单位就是512b
这样就把刚刚的fstab文件读取出来了,skip就是文件的sector相对磁盘的起始位置,count就是文件所占的block数目

继续我们的对象提取方式,上面的(10177..10184)这个我们根据上面那个脚本输出的对象列表来找到对象

[8192, 16383] –> 0000000000000001
获取名称,这个因为我的是测试环境,就只有一个匹配,多个image的时候要过滤出对用的rbd的对象,用prifix过滤即可

1
2
[root@lab8106 ~]# rados -p rbd ls|grep 0000000000000001
rbd_data.25a776b8b4567.0000000000000001

下载对象

1
[root@lab8106 ~]# rados -p rbd get rbd_data.25a776b8b4567.0000000000000001 rbd_data.25a776b8b4567.0000000000000001

根据偏移量计算对象中的偏移量

1
2
(10177..10184)
[8192, 16383] --> 0000000000000001

得到

1
2
3
10177-8192=1985

dd if=rbd_data.25a776b8b4567.0000000000000001 of=a bs=512 count=8 skip=1985

得到的文件a的内容即为之前文件的内容

准备取第二个分区的文件

1
2
3
4
[root@lab8106 ~]# xfs_bmap -lvp /mnt2/hostname 
/mnt2/hostname:
EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL FLAGS
0: [0..7]: 8224..8231 0 (8224..8231) 8 01111

8224+10248192..8231+10248192=10256416..10256423

从磁盘方式

1
[root@lab8106 ~]# dd if=/dev/rbd0 of=a bs=512 count=8 skip=10256416

从对象方式
10256416..10256423 对应
[10256384, 10264575] –> 00000000000004e4
对象偏移量

1
10256416-10256384=32
1
2
rados -p rbd get 
[root@lab8106 ~]# rados -p rbd get rbd_data.25a776b8b4567.00000000000004e4 rbd_data.25a776b8b4567.00000000000004e4

获取文件

1
[root@lab8106 ~]# dd if=rbd_data.25a776b8b4567.00000000000004e4 of=a bs=512 count=8 skip=32

如果文件比较大的情况,可能出现就是文件是跨对象的,那么还是跟上面的提取方法一样,然后进行提取后的文件进行合并即可

总结

在存储系统上面存储的文件必然会对应到底层磁盘的sector,而sector也是会一一对应到后台的对象的,这个在本文当中得到了验证,所以整个逻辑就是,在文件系统层找到文件对应的sector位置,然后再在底层把sector和对象关系找好,就能从找到文件在对象当中的具体的位置,也就能定位并且能提取了,本篇是基于xfs的,其他文件系统只要能定位文件的sector,就可以在底层找到文件,这个以后会补充其他文件系统进来

变更记录

Why Who When
创建 武汉-运维-磨渣 2017-07-22