ceph的数据存储之路(11)----- cephfs 文件系统

烈酒焚心 提交于 2019-11-30 05:32:57

cephfs 文件系统

cephfs 文件系统的使用:

1.首先你要搭建一个ceph集群。如何搭建ceph集群在前面已经介绍过了。如果要使用cephfs文件系统,则必须要有管理文件元数据的mds节点。

2.在集群上创建文件系统,

root@cephmon:~/ceph/ceph-0.94.2/src# ./ceph fs new cephfs2 cephfs_metadata cephfs_data
*** DEVELOPER MODE: setting PATH, PYTHONPATH and LD_LIBRARY_PATH ***
new fs with metadata pool 2 and data pool 1

fs new 表示需要创建一个新的文件系统。

cephfs2 表示新的文件系统的名字叫做cephfs2。

cephfs_metadata 表示文件系统元数据保存信息的存储pool。

cephfs_data 表示文件系统的数据保存信息的存储pool。

如果我再新建一个文件系统cephfs3,则会出现如下提示:

root@cephmon:~/ceph/ceph-0.94.2/src# ./ceph fs new cephfs3 fsmeta fsdata
*** DEVELOPER MODE: setting PATH, PYTHONPATH and LD_LIBRARY_PATH ***
Error EINVAL: A filesystem already exists, use `ceph fs rm` if you wish to delete it

这就表明 ceph集群上只能创建一个文件系统。

 

3.客户端上的挂载:

说明:网络文件系统想要使用必须要在本地进行mount操作,mount操作后,即可 像本地目录一样操作。ceph的rbd 块设备在使用时提供了两种使用方式 librbd和kernel rbd,同样的cephfs也提供了两种使用方式,一种是基于用户空间的文件系统fuse使用,一个是基于内核空间的ceph 挂载使用,各有利弊,fuse使用和修改上更为方便,但是性能不得不说略差与kernel的使用方式,这尤其是表现在各个公司竞标pk时的表现。

需要获得secret key才能与集群进行对话。cat keyring文件

[mon.]
	key = AQCs8mxXxs5GNhAAUKCQuZ4XxBmeTfcEdIphCw==
	caps mon = "allow *"
[client.admin]
	key = AQCs8mxXrUwTNxAARQarCkQwsBYh7pyF4cUZsQ==
	auid = 0
	caps mds = "allow *"
	caps mon = "allow *"
	caps osd = "allow *"
获得admin用户的 key ,AQCs8mxXrUwTNxAARQarCkQwsBYh7pyF4cUZsQ==

在本地创建一个用于挂载的节点 mkdir  /mnt/cephfs

a、使用kernel形式进行挂载:在客户机上

mount   -t   ceph    mon_ipaddr:port:/    /mnt/cephfs   -o    name=admin,secret= AQCs8mxXrUwTNxAARQarCkQwsBYh7pyF4cUZsQ==

挂载后可以查询:直接输入mount命令
192.168.121.226:6789:/    on    /mnt/cephfs      type      ceph (rw, relatime  , name =  admin ,secret=<hidden>,nodcache)

b、使用fuse形式进行挂载:

使用ceph fuse需要先进行安装ceph fuse工具,收先先查找下安装包

root@cephmon:~/ceph/ceph-0.94.2/src# apt-cache search ceph-fuse
ceph-fuse - FUSE-based client for the Ceph distributed file system
ceph-fuse-dbg - debugging symbols for ceph-fuse

这里可以发现,可以直接安装ceph-fuse。
root@cephmon:~/ceph/ceph-0.94.2/src# apt-get install ceph-fuse 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
………………..
update-initramfs: deferring update (trigger activated)
Processing triggers for initramfs-tools ...
update-initramfs: Generating /boot/initrd.img-3.10.36-openstack-amd64

root@cephmon:~/ceph/ceph-0.94.2/src#c eph-fuse    –k    ./keyring   -m 192.168.121.226:6789   /mnt/cephfs
ceph-fuse[4743]: starting ceph client 
ceph-fuse[4743]: starting fuse
root@cephmon:~/ceph/ceph-0.94.2/src# df
Filesystem                                             1K-blocks     Used Available Use% Mounted on
rootfs                                                 101016992 16514904  79347684  18% /
udev                                                       10240        0     10240   0% /dev
tmpfs                                                    1002252      328   1001924   1% /run
/dev/disk/by-uuid/6a5b4c32-fd65-4e62-a953-102b0a3fe9ea 101016992 16514904  79347684  18% /
tmpfs                                                       5120        0      5120   0% /run/lock
tmpfs                                                    2423300        0   2423300   0% /run/shm
ceph-fuse                                              303050752 65007616 238043136  22% /mnt/cephfs
root@cephmon:~/ceph/ceph-0.94.2/src#

这里可以看到已经mount成功了,可以正常使用/mnt/cephfs目录进行存储。

 

卸载:

umount    /mnt/cephfs

cephfs的两种使用方法已经交代完毕,下面就来具体说说文件系统那点事儿吧

文件系统从何说起呢?

linux文件系统简介

cephfs的 新创建一个文件系统 过程

cephfs的mount流程

cephfs的 创建目录或者文件的流程。

这里我想结合linux文件系统来聊一聊cephfs,所以在接下来的介绍中也都使用了linux kernel的方式对cephfs进行挂载使用。当然这里面设计到了一些kernel的知识,参考的linux kernel代码为4.1.4 ceph 代码0.94.2。

 

linux 文件系统简介:

在linux系统中有着很重要的一句话就是,一切皆是文件,可见文件的重要性,所以文件系统是重要中的重要了。这里存在一个重要的角色那就是文件系统的tree。

所有的文件系统都有一个tree,tree从一个主干开始(也叫做root根),然后可以在这个上长出叶子(文件file)或者枝条(目录dir)。然后枝条上还可以继续长出叶子或者枝条。

在linux启动之初,系统还未发现可用的磁盘,这时会先创建一个叫做rootfs的内存文件系统,这个文件系统伴随内存的生而生,死而死。这个rootfs创建了linux文件的第一个根 “\”目录。

但是为什么在系统启动之后,我们去查看mount情况时,却没有发现任何关于rootfs的信息呢?

我们使用mount命令查看,发现mount的并不是rootfs信息。这个后面看完自然会明白

 

好了,这时有了这个rootfs的根目录了,这样就可以加载其他的文件系统了。

这里先说明下,磁盘在使用之前会进行分区,每个分区都需要建立一个文件系统,这些文件系统可以type相同也可以不同,在每一个磁盘分区建立独立的文件系统来管理这部分空间,但是他们是各自独立的孤岛,因为还没加入到内存中进行管理,所以他们这些文件系统还不能使用。

 

当磁盘上线后,会使用工具对磁盘进行分区,在分区上建立文件系统,然后对分区sda(这里假设该分区叫做sda)进行mount 到 “/“目录, 什么是mount? 哈哈可以自行百度。

mount操作之后,对于“/“目录的操作都转化为对sda的操作。

并且可以在“/“ 中创建其他的目录,然后把磁盘的剩余分区进行mount操作。

如上图,把分区1mount在“/“目录上,在”/“目录中创建几个目录 /home,/var,/tmp,/usr,/bin,/boot,/sbin等目录。然后分区2挂载到/home目录,这样使用/home目录存储文件时,数据全部都保存在分区2上,同理挂载分区3到/var目录,挂载分区4到/tmp目录。剩余的目录/usr,/bin,/boot,/sbin没有被挂载其他的磁盘,所以他们继续使用分区1的空间

当然除了需要理解上面这部分内容,你还需要去看看 linux vfs系统,linux系统调用,linux的某种本机文件系统(比如简单的ext2文件系统原理)。

当然假设上面举例的这几部分你都了解过,可以继续下面的内容。

 

ceph文件系统

在monitor节点上会时刻的mds节点发送过来心跳信息 ,当接收到心跳信息后,在monitor上会对mdsmap进行比较,但是由于还没有建立文件系统,所以mds仍然不再mdsmap中,这样monitor每次收到mds发送的心跳信息后都暂不做任何处理。

mds节点上运行这ceph_mds程序,这个程序目前负责收发心跳和信息传递等。当ceph_mds接受到monitor节点的回应后,会查是否mdsmap会发生改变。如果mds发生变化则调用handle_mds_map进行处理。

 

在ceph集群上创建文件系统:

运行命令 ceph   fs   new   cephfs   metadatapool  datapool

命令被发送到了mdsmonitor上,在这里进行处理。MDSMonitor::management_command,该函数用来处理文件系统的创建和删除

首先是prefix==fs new,创建一个新的文件系统。

创建一个新的文件系统当然要有很多的检查了。

a、metedata_pool 是否存在。当然对这些pool还是有要求的,不能是tier类型的pool。

b、data_pool是否存在。当然对这些pool还是有要求的,不能是tier类型的pool。

c、fs name是否与已经存在的文件系统重名。

d、如果已经存在了文件系统,在创建第二个文件系统时,失败处理。

1232:看的出,这里就是代表所有前面的检查都通过了。

1233:新创建一个MDSMap。

1235:用这个MDSMap去更新 pending_mdsmap。

1236:增加版本号

1238:create_new_fs,这里开始创建新的fs,但是仅仅是更新这个pending_mdsmap。

0085:创建一个文件系统开始。

0087:mdsmap.enable 表示文件系统正式生效。

0088:mdsmap.fs_name 表示文件系统的名字。

剩余的见名知意。不再多说。

这个时候新建立了一个pending_map 并且对pending_mdsmap的epoch做了更新。

那么MDSMonitor会对这个pending_mdsmap与MDSMap进行检查,版本出现了更新,会推送新的MDSmap到mds上。这里会发生了一些mdsmap提议选举投票之类的操作,将mds加入到MDSMap中,将pending_mdsmap的信息替换到MDSMap上,然后将MDSMap推向相应的mds上。这里不再细讲这个过程(因为我也不是特别了解),首先在mds上线后向mon发送消息,并且验证mds的权限。然后mon回复mds,告知mds拥有这个集群的权限。mds启动后开始发送mon_subscribe消息给mon,mon将mdsmap,monmap,osdmap等map信息回复给mds。mds拥有这些信息开始处理,比如handle_mds_map就是专门用来处理mdsmap的流程。mds发现了mdsmap中没有自己,开始尝试加入mdsmap中,开始发送消息请求mdsbeacon,交给mon处理,然后在发还给mds信息,则mds开始了自己的状态转化 boot -> standby -> creating -> active.这些状态都在handle_mds_map中找到踪迹。

 

在handle_mds_map中有一个重要的步骤—创建root目录。之前也讲过一个文件系统的根本就是这个root目录。root是所有目录的基础。

 

MDS::handle_mds_map(MMDSMap *m) – >boot_create();

1877:如果文件系统的根目录保存在 我自己的mds上,则创建根目录。由于mds集群的扩展,根目录会主要的保存在一个primary mds上和其他几个stary mds上。所以创建时需要判断是否是属于本mds的。

1880:这里开始创建一个根目录,由于根目录的名字是“\”所以使用create_empty_hierarchy()。

1884:这里还需要创建一个我的目录,因为不是所有的目录都会保存在本mds上,所以本地要管理自己的目录。create_mydir_hierarchy()。

 

create_empty_hierarchy 和 create_mydir_hierarchy之间的区别不大,无非是一个创建“/”目录,另外一个创建“mds%s”。来看看empty_hierarchy()函数。

0405:创建root,这里的类型的CInode。文件系统中所有的文件或者目录都有两个最重要的东西inode和dentry。inode中定义了文件的操作相关处理和数据管理,dentry是为了表达目录层级关系,可用于路径的搜索。

0408:这里用于获取目录分片CDir的,可能一个目录过于庞大或者成为热点目录的时候,就要对这个目录进行切片,分散到不同的mds上面去。这个就是获取当然mds上的分片。

 

继续来看create_root_inode();用于创建root目录的CInode节点。

create_root_inode()  - >  create_system_inode(MDS_INO_ROOT, S_IFDIR|0755); MDS_INO_ROOT是一个宏,代表这个inode的编号,值为1。

0385:参数ino就是上面说的MDS_INO_ROOT=1,mode就是目录的权限。

0388:创建一个新的CInode。这个CInode与当前的MDCache相关。

0389:CInode与ino、mode等进行初始化操作。绑定。

0390:在mds上,很多的文件节点inode都保存在内存中,这些inode由MDCache进行管理。在MDCache中有个inode_map用于保存所有的inode。如果这个inode是root或者mydir 在MDCache中会有指针特别指出这些inode,方便用于查找。

现在已经在mds上创建了cephfs的根目录了,就等待客户端的mount操作。

客户端的mount操作,这里客户端mount操作采用直接mount形式,采用kernel mount的方式,fuse mount的操作原理差不多相同。为了简化描述,采用kernel mount的形式。

在cephfs kernel mount使用前,你要确保你的内核有cephfs模块。ceph模块的代码不再ceph源码包里,而且在linux kernel的源码包里,所以你可以去下载完成kernel包(https://www.kernel.org/)   有 了kernel源码包就可以分析下面的代码了

来看下 ceph 内核模块加载的过程。

1041:ceph内核模块加载的初始化函数。

1043:申请和初始化需要用到的cache。这个cache是用于分配inode\file\cap\dentry的高速缓存。

1047:初始文件锁之类的。

1048:初始化cephfs系统的 文件和目录的 attr属性的操作方法。

1049:文件系统快照初始化。

1052:这里是将文件系统ceph_fs_type进行注册。linux内核中所有的文件系统type结构都会通过一个链表file_systems连接起来。查找时遍历链表file_systems即可。那么接下来看看 ceph_fs_type中都定义了什么?

1032:定义ceph_fs_type类型。

1033:模块的所有者。

1034:文件系统的名字。后续在mount时  -t参数指定的名字。

1035:定义这个ceph文件系统进行mount时的操作。 那是如果调用到这里的呢?

 

来看看cephfs是怎么进行kernel mount的。

mount –t ceph mon_ip_addr:port:\   \mnt\cephfs  -o name=admin,secret=key

mount 命令 通过系统调用系统调用sys_mount 带着参数来到内核,解析-t 参数是叫做ceph,所以在内核的file_systems链表中查找名字叫做ceph类型的文件系统,然后可以找到结构ceph_fs_type。在这个type中找到可以ceph_mount操作。然后进入ceph_mount中,对ceph文件系统进行真正的mount操作。

接下来看看ceph_mount中做了什么

0944:开始对参数进行解析,解析出fsopt用于mount的基本信息,具体可以查看数据结构。opt是ceph集群的相关信息。dev_name用于mount的目录名字,path用于mount到客户端本机的路径名(可以是客户端机器中的绝对路径或者相对路径)。

0952:创建fs客户端,这里面包括与mon通信的monclient 与osd通信的osdclient信息。

0960:创建fsc中的mdsclient。与mds进行通信的结构,这里叫做mdsc。

 

建立了与集群通信相关的操作,接下来就是本地初始化了。在本地建立superblock。啥是superblock?可以自行百度。。。。superblock是文件系统中最高的管理者。保存了很多重要的信息。看看superblock的创建。

0971:试图获取sb,如果获取sb失败,则创建新的sb。这里对sb的初始化等操作放在ceph_set_super这个参数中。最重要的是所有的文件系统sb数据结构都是一样的,但是sb->data是可以保存自己私有的数据。ceph_fs的sb->data 保存的就是上面介绍的与ceph集群通信fsc结构。

0978:查看sb中保存的fsc与当前的fsc是否相同。

 

那么看看ceph_set_super中做了什么,他要对sb负责。

0843:从sb->data中恢复出fsc的结构。

0848:设置flags的相关信息。

0849:设置sb的最大字节数

0851:设置这个xattr的操作方法。  这个非常重要

0855:设置了s_op的操作方法。该方法由ceph_super_ops实现。 这个非常重要,非常重要。非常重要。

0856:设置其他的操作方法,该方法又ceph_export_ops实现。非常重要。

这时我们先看看ceph_super_ops的定义吧。

0693:定义操作方法ceph_super_ops

0694:定义ceph如何申请一个inode。

0695:定义ceph如何销毁一个inode。

其他的以此类推咯,就是该文件系统想要和inode的相关操作从这查找就可以。

 

sb 掌握了这些信息以后,我们再回到ceph_mount中继续下面的操作。

接下来进行,ceph_real_mount操作,这个适合本地文件系统mount操作的处理,让ceph文件系统无缝的连接在客户端本地的文件系统上。

 

看一看,瞧一瞧 ceph_real_mount做了哪些见不得人的事儿。

ceph_real_mount内部实现。

0784:建立与mon、osd的集群连接。保证req的发送。

0789:在本地打开ceph的root目录,返回值为这个目录 dentry类型的root目录。

这里由客户端打开root目录就算是mount结束了。那主要的流程是要看看如何打开这个root目录。

0720:这里创建一个请求mds的req。操作码是CEPH_MDS_OP_GETATTR,可以发给任意一个mds。

0724:设置这个req请求的路径。这个路径就是“/”根目录。

0730~0735:设置req,初始化一系列的值。

0736:向mds发送这个req。这是一个同步的下发的接口,会在里面等待命令的完成。

0739:在mds返回的消息中可知,这个已经打开inode。

0745:创建一个dentry 与这个inode匹配。然后返回这个root。

之后会将这个fsc->sb->s_root = root; 便于下次查找。

 

好的,现在算是这个文件系统创建完成,并且mount成功。来看看相关的信息

查看mount信息:

查看空间信息:

这里还要说明下请求是怎么来处理的,又是怎么 回到客户端的。这个中间经历了很多过程。

 

我们在打开root目录时,发送这样的一个请求,请求的操作码是CEPH_MDS_OP_GETATTR ,如下图

该请求经过客户端的封装,通过ceph_mdsc_do_request统一发送到mds上。这个消息发送传递这一块 就不细说了,可以了解这一块,理解ceph的通信协议模式,数据包格式等信息。但是这个不是本文的重点,所以不多说,想了解的可以研究下这部分代码。

再来看mds上的处理(ceph代码),当然前面有一些消息解析的动作,但是这些先不讲,只来看看消息被解析后的处理。mds上处理消息的接口为dispatch_client_request。在该函数里查找对的CEPH_MDS_OP_GETATTR处理。

1473:这里对应处理CEPH_MDS_OP_GETATTR的函数为handle_client_getattr();在这个函数里自然就是怎么来解决打开root目录的操作了。最终的目标就是返回root目录的Inode的相关信息。然后调用respond_to_request() -> reply_client_request() -> client_con->send_message(reply); 将消息回复给客户端。在respond_to_request()函数中声明这样一条消息MClientReply,该消息继承与Message,而且这个Message中使用了CEPH_MSG_CLIENT_REPLY进行构造。所以在客户端中使用该操作码解析。

 

客户端用于处理消息应答的接口handle_reply()(linux内核代码)。该接口适用于CEPH_MSG_CLIENT_REPLY 操作码,即客户端向mds发送消息,然后mds使用该操作码回复消息。最后通过接口handle_reply()处理消息。

 

在handle_reply()中,需要对打开的节点inode进行处理。首先就是要根据你的返回信息,然后在客户端本地进行缓存。调用 handle_reply() -> ceph_fill_trace() -> fill_inode()  

0787:在fill_node中解析节点的类型。

0823:如果节点是S_IFDIR类型,也就是目录类型的文件。

0824:对Inode的i_op 项赋值为 ceph_dir_iops;

0825:对Inode的i_fop项赋值为ceph_dir_fops;

0829~0833 :对其目录文件的其他信息进行解读,包括文件,目录数量等。

 

这几步非常的重要。一个是拿到了目录中已经包含的所有信息。另一个是该步定义了这个目录操作的方法 i_op 和 i_fop。

 

i_op 的原型为inode_operations。表示inode节点的定义操作列表。

i_fop的原型为file_operations。表示inode节点的 默认定义操作列表。

 

简单的看下i_op中定义了什么?

1393:定义ceph_dir_iops的操作方法。

1394:定义目录中子节点的查找方法。如果要调用该节点的lookup方法会转嫁到ceph_lookup上进行处理。同理 一下的其他方法也会被转嫁。

1395:定义该目录的权限处理方法----ceph_permission。

1396:定义该目录的属性获取方法----ceph_getattr

1397:定义该目录的属性设置方法----ceph_setattr

1398:定义该目录的拓展属性获取方法----ceph_getxattr。xattr用于存储inode之外的信息,一般使用key/value 形式。

1399:定义该目录的拓展属性设置方法----ceph_setxattr

1400:定义该目录的拓展属性列举方法----ceph_listxattr

1401:定义该目录的拓展属性移除方法----ceph_removexattr

1402:定义该目录的acl属性获取方法----ceph_getacl

1403:定义该目录的acl属性设置方法----ceph_setacl

1404:定义该目录中创建子节点的方法----ceph_mknod

1405:定义该目录创建符号连接的方法----ceph_symlink

1406:定义该目录创建子目录的方法----ceph_mkdir

1407:定义该目录创建硬链接的方法----ceph_link

1408:定义该目录解除硬链接的方法----ceph_unlink

1409:定义该目录删除rmdir的方法 ----ceph_unlink

1410:定义该目录重命名的方法----ceph_rename

1411:定义该目录创建文件的方法 ----ceph_create

1412:定义该目录原子打开的方法 ----ceph_atomic_open

 

这样打开这个root的inode后,这个inode上携带者这么多的操作方法。然后inode返回到open的操作open_root_dentry()中。

总结:

前面叙述的东西有点多。在这里简单复述下:

1、在monitor上先运行了cephfs的创建命令,cephfs 创建文件系统,创建文件系统后。然后mds加入mdsmap中,将新的mdsmap发送给mds处理。然后在mds上创建文件系统的根root目录。

2、保证客户端的机器上有加载ceph的内核模块。在ceph内核模块加载的时候,就将cephfs文件系统注册到系统中。

3、在客户端进行mount操作。在客户端上首先根据ceph关键字在系统中找到这个cephfs文件系统。在cephfs文件系统中定义文件的方法。尝试打开这个root目录,组织msd_request。发送给mds进程。在mds上对root目录进行查找打开,将获取到的结果通过消息回复给client。client在对回复的解析中,根据inode->mode形式对inode赋予操作方法。

4、根据inode的操作方法,就可以对inode进行操作了。上面举例子是目录文件,那么普通文件呢,inode->i_op = &ceph_file_iops;  inode->i_fop = &ceph_file_fops;,具体的细节你可以去查看这两个操作的定义ceph_file_iops、ceph_file_fops.定义的这些操作 比如创建目录、创建文件、操作文件、读写文件、修改文件属性等。但是这些操作只是客户端上的操作,这些操作的背后都是需要与mds进行通信,将请求传递到mds上。简单的流程图如下:

5、如果要操作一个普通文件则必须先打开这个文件,如果要操作一个目录文件则也必须要打开这个目录文件,所以不管是目录文件还是普通文件都必须先打开这个文件,打开操作会获得这个文件的inode,这个inode中i_op或者i_fop包含了文件的操作方法,所有的文件操作都会自己去这里面查到方法。而这些方法必须与mds进行通信才能操作元数据。mds的进程上同样定义相同操作的实现方法,最后在mds上操作元数据。后面的创建文件、创建子目录同学们可以自行的去了解了。

6、当然 还有人喜欢使用fuse的方法mount目录,对于fuse中的实现也是类似的,有兴趣可以自己看下ceph_fuse的代码。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!