网络文件系统,年近五十岁,老当益壮
前面我们介绍了本地文件系统,本地文件系统是管理块设备资源的文件系统,他将块设备的线性空间转化为易于使用的树型空间(如下图所示)。这里的块设备可以是本地硬盘或者通过SAN映射过来的硬盘,但归根到底文件系统是在本地的。
我们继续文件系统系统的介绍,本文主要介绍一下网络文件系统的相关内容。网络文件系统(Network File System)是一种将远端的文件系统映射到本地的文件系统。这里的远端通常是指一个存储系统,这个存储系统通常称为NAS存储。本地是指主机端或客户端,通常是运行业务的服务器。存储系统可以是专属的存储设备,如一些存储厂商的存储产品,也可以是基于普通服务器搭建的存储系统。
早些时候的企业级架构,数据存储普遍采用网络文件系统。网络文件系统中最为著名的就是Sun的NFS了。微软也有类似的网络文件系统,也就是CIFS。网络文件系统的原理很简单,其目的就是将存储系统上的文件系统映射到计算节点(比如Web服务器)。具体映射示意图如下图所示。
在上图中,网络文件系统将远程的目录树映射到本机,成为本机目录树中的一棵子树。并且,两颗树中的元素是一一对应的,也就是本地的一个文件对应着存储设备上一个文件;一个目录对应着存储设备上的一个目录。对于普通用户来说,访问该子树中的内容与访问本地其他子目录内容没有任何差异。也就是用户不会感知到该子树的内容是在远端,也没必要感知这种存在。
同时,通过上图可以看出,对于网络文件系统而言通常存在两部分组件,一部分是客户端的文件系统;另一部分则是服务端的服务程序。客户端文件系统的逻辑与本地文件系统无异,对用户呈现的仍然是树型结构,差异是在读写数据时不是访问磁盘等设备,而是通过网络将请求传输到服务端。服务端负责客户端请求的处理,将数据存储到磁盘等存储介质上。
客户端的实现可以基于操作系统的文件系统框架来实现一个文件系统,该文件系统负责接收应用的请求,并将请求转发到服务端,具体如NFS文件系统在Linux或者Windows的实现。但是也不一定,由于网络文件系统通信本身是基于以太网的协议,因此也可以实现一个库函数来实现相关功能。当然,这种实现方式就无法实现直观的文件操作了,只能供开发应用程序使用。
服务端数据的存储可以借助于普通的本地文件系统,也可以实现自己的文件系统。比如Linux下的NFS实现,在NFS服务端通常是使用常规的本地文件系统来存储数据的,如Ext4、XFS或者Btrfs等等。而一些存储设备提供商则通常实现自己的文件系统,比如EMC的UFS64文件系统和NetApp的WAFL等。
服务端数据的存储并不一定需要基于某种文件系统,甚至可以基于KV数据块来实现。对于服务端来说,只需要能够实现对主机端请求的解析即可。这些请求包括创建删除文件、数据读写、扩展属性和权限等等。如果对这里的描述不理解也没关系,我们在后续章节会详细介绍。
1网络文件系统与本地文件系统的异同
从普通用户的角度来看,网络文件系统与本地文件系统没有明显的差异。在Linux平台下,挂载文件系统时,本地文件系统是将一个块设备挂载到某个目录下面,而网络文件系统则是将一个包含IP地址的远程路径挂载到某个目录下面。
网络文件系统与本地文件系统的差异在于数据的访问过程。以写数据为例,本地文件系统的数据是持久化到磁盘(或者其他块设备)上的,而网络文件系统则需要将数据传输到服务端进行持久化处理。
另外一个差异点是本地文件系统需要做格式化处理才可以使用。而网络文件系统则不需要主机端进行格式化操作,通常其只需要挂载到主机端就可以直接使用。当然在服务端通常是要做一些配置工作的,这其中通常包括格式化操作。
网络文件系统最主要的特性是实现了数据的共享。基于数据共享的特性,使得网络文件系统有很多优势,比如增大存储空间的利用效率(降低成本)、方便组织间共享数据和易于实现系统的高可用等。
2常见的网络文件系统简析
网络文件系统有很多,其中有两个标准的网络文件系统最为出名,一个是Linux下经常使用的NFS(Network File System)文件系统,另外一个是Windows下经常使用的CIFS(Common Internet File System)。除此之外,其实还有很多其他的网络文件系统,如AFS(Andrew File System)等。
NFS是一个非常老牌的网络文件系统了,于1980年由Sun公司开发,并随SunOS一起发布。NFS不仅仅是指网络文件系统,现在更多的是指一种协议,而且是一种开放的网络文件系统协议。
关于NFS最新的描述可以通过RFC7531获得更多描述信息。NFS从第一次发布到现在已经几十年了,也发展了不同的版本,从最初的内部使用的1.0版本到现在的4.X版本,已经有4个大版本。
前文我们提到网络文件系统包含客户端文件系统和服务端服务2个组件。对于客户端而言,目前主流的操作系统都有对NFS的支持,这包括Linux、Unix和Windows等等。
在服务端,通常需要实现一个服务软件,实现对本地文件系统的导出。这样通过NFS协议可以将文件系统导出到主机端。由于协议是开放的,因此其实有很多NFS的服务端实现,Linux内核中本身集成了一个内核模块(nfsd)。在用户态有一个比较有名的NFS服务端软件---NFS-Ganesha。
在Windows下也有一套用于在网络实现文件共享的协议,称为SMB。SMB的全称是服务(器)消息块(Server Message Block),是一套基于NetBIOS的文件共享协议。随着以太网技术的发展,SMB逐渐与NetBIOS脱离。在Windows2000中,SMB可以直接运行在TCP/IP协议之上。
SMB不仅仅可以用于文件共享,其本身是用于进行节点间消息传输的协议,可以用在文件共享、打印机共享及其他信息共享领域。
随着互联网的发展,微软基于SMB是实现了一个通用的文件共享协议,也就是CIFS。CIFS的全称是通用因特网文件系统(Common Internet File System),可以认为CIFS是SMB的一个具体实现。
其实CIFS/SMB的情况与NFS非常类似,其架构也是C/S架构,而且协议也是开放的。因此在客户端和服务端有很多具体的实现。
主流的操作系统都有CIFS的实现,Windows自然不用多说。在Linux也有CIFS的实现,如果阅读源代码的话,CIFS就在与NFS同级的目录下。
服务端的实现也很多,除了Windows操作系统本身的实现外,在Linux下也有一个能够提供CIFS的服务,也就是Samba。除了开源软件之外,还有很多商用的CIFS服务,其中比较有名的如MoSMB、Tuxera SMB和Likewise等。
3网络文件系统关键技术
网络文件系统本质上也是一个文件系统,因此本地文件系统所使用的技术在网络文件系统中通常也都是要使用的,比如缓存技术、文件锁、快照克隆和权限管理等等。区别在于,这些特性大多是借助于位于服务端的文件系统实现,网络文件系统本身通常并不实现。对于网络文件系统而言,只需要通过协议将请求透传到服务端进行处理即可。鉴于网络文件系统比较核心的功能是实现对文件或目录操作的透传(实际并不完全是透传,可以暂时这么理解),本文重点介绍一下这方面的内容。
我们知道,Linux下面对文件的操作主要包括打开文件、读数据和写数据等操作;对于目录的操作主要包括创建目录、删除目录和遍历目录等。网络文件系统的比较核心的功能就是将这些操作传递到存储设备进行处理。要实现上述功能,其实就是定义了一个通信协议,比如NFS或者CIFS等。
由于通常网络文件系统基于以太网进行连接,因此网络文件系统的协议通常也是基于TCP或者UDP协议而实现。如果简单的理解,我们可以将网络文件系统的协议理解为TCP/IP应用层的协议(但实际情况要复杂一些)。
以NFS为例,网络文件系统的协议的定义类似函数调用,包含ID(可以理解为函数名称),参数和返回值。其定义是非常清晰的,其语义与文件系统操作的语义基本上一一对应。以经常使用的NFSv3为例,我们这里列出其部分协议内容,如下表所示。
名称 | 例程编码(ID) | 操作系统API(Linux) | 说明 |
CREATE | 8 | create / open | 创建一个常规文件 |
REMOVE | 12 | remove / unlink | 删除一个常规文件 |
WRITE | 7 | write | 向文件写入数据 |
READ | 6 | read | 从文件读取数据 |
LOOKUP | 3 | --- | 查找文件 |
MKDIR | 9 | mkdir | 创建一个目录 |
READDIR | 16 | readdir | 读目录中的内容 |
RMDIR | 13 | rmdir | 删除目录 |
COMMIT | 21 | flush | 提交缓存中的数据 |
通过上表可以看出,在NFS协议中定义的语义与我们对文件系统的操作有非常明确的对应关系。对于文件来说有创建、删除、读写和查找等,对于目录也有类似的命令。当然,本节只是展示了NFSv3的部分内容,更多细节请参考后续章节。
SMB(CIFS)协议与NFS类似,也是实现了一些与文件系统语义对应的协议命令。如下表是部分SMB2的命令。
名称 | 编码(ID) | 说明 |
CREATE | 0x0005 | 创建一个文件 |
CLOSE | 0x0006 | 删除一个文件 |
FLUSH | 0x0007 | 刷新缓存 |
READ | 0x0008 | 向文件写入数据 |
WRITE | 0x0009 | 从文件读取数据 |
QUERY_DIRECTORY | 0x000e | 获取目录中的内容 |
QUERY_INFO | 0x0010 | 查询文件和命名管道等对象的信息 |
通过NFS和SMB的协议可以看出,无论哪种协议,都有一组与文件系统语义对应的协议命令。这样,客户端对网络文件系统的访问都可以通过协议传输到服务端进行相应的处理。由于文件系统语义与协议命令清晰的对应关系,网络文件系统协议并不复杂,只是内容比较多。
网络文件系统定义了客户端(或者称为主机端)与服务端交互的协议(例如NFS),网络文件系统的协议是用过函数调用的方式定义的,主要内容包含ID、参数和返回值等。客户端到服务端访问通常是通过网络来访问的,因此在具体实现的时候需要将协议定义的函数形态转化成网络数据包,然后在服务端收到数据包后执行预定的动作后给客户端发送反馈。
由于在客户端与服务端都要实现对协议数据的封装和解析,因此实现起来比较复杂。为了降低复杂性,通常会在文件系统业务层与TCP/IP层之间实现一层交互层,这就是RPC。这种分层的方式是计算机领域经常用到的处理问题的方式,比如TCP/IP的协议栈,MVC模式等等。
RPC的全称是远程过程调用(Remote Procedure Call),是TCP/IP模型中应用层的网络协议(或者OSI模型中会话层的协议)。RPC通过一种类似函数调用的方式实现了客户端对服务端功能的访问,简化了客户端访问服务端功能的复杂度。
在客户端调用RPC函数时,该函数会调用RPC库的接口将该函数调用转化为一个网络消息转发到服务端,而服务端的RPC库则对网络数据包进行反向解析,调用服务端注册的函数集(存根)中的函数实现功能,最后将执行的结果反馈给客户端。
对于基于RPC协议的服务如下图,通常包括RPC运行库、客户端/服务端存根(stub)和实现业务的应用程序。
RPC运行时库是通常是一个公共的库,实现了RPC的公共的功能,比如请求的封装与解析、消息收发和网络层面的错误处理等等。
存根(stub)是定义的函数集,该函数集根据应用业务的需求而确定。以网络文件系统为例,函数集包括创建文件、删除文件、写数据和读数据等等。函数集通常需要分别在客户端和服务端定义一套接口,而且客户端的函数集与服务端的函数集是一一对应的。
应用则是基于PRC实现的具体应用程序。以网络文件系统为例,在客户端的应用是指文件系统,而服务端的应用则是文件系统服务,如NFSD或者Ganesha等。
需要注意的是RPC并不是网络文件系统专用的协议,而是在分布式系统的很多地方都有应用。RPC不仅在操作系统内核中有实现,也有很多用户态实现,例如gRPC、Dubbo和Thrift等。需要注意的是不同的RPC协议的定义并不一样。
由于公众号文章不能自由编辑,一些历史文章难免有些错误。本号整理了一些系统的文档,有兴趣的同学可以下载阅读。
相关文章阅读
为什么都说NFS读写性能差,如何进行优化? NFS协议端到端实例解析之写数据流程 NFS协议端到端实例解析之文件锁流程 Linux NFSD软件架构与代码解析 Ceph的文件系统架构及使用实战 分布式存储的软件架构与案例解析 什么是文件系统,以Linux中的Ext4为例细聊一下 对象存储,从单机到分布式的演进 什么是云存储,从对象存储说起?