NFS是Network File System的缩写,即网络文件系统。它的主要功能是通过网络(一般是局域网)让不同的主机系统之间可以共享文件或目录。NFS客户端可以通过挂载(mount)的方式将NFS服务端共享的数据目录挂载到NFS客户端本地系统中(就是某一个挂载点下)。从NFS客户端的机器本地看,NFS服务端共享的目录就好像是客户自己的磁盘分区或者目录一样,而实际上确是远端的NFS服务端的目录。NFS主要用于类Unix系统之间的共享, 最早是由Sun公司发展出来的。

NFS网络文件系统类似windows系统的网络共享、安全功能、网络驱动器映射,这也和linux系统里的samba服务类似。应用于互联网中小型集群架构后端作为数据共享,如果是大型网站,那么有可能还会用到更复杂的分布式文件系统,例如Moosefs(mfs)、glusterfs、FastDFS。

一、NFS原理流程及RPC服务的介绍

既然NFS是通过网络来进行服务器端和客户端之间的数据传输,那么两者之间要传输数据就要有相对应的网络端口,NFS服务器到底使用哪个端口来进行数据传输呢?基本上NFS这个服务的端口开在2049,但由于文件系统非常复杂。因此NFS还有其他的程序去启动额外的端口,这些额外的用来传输数据的端口是随机选择的,是小于1024的端口;既然是随机的那么客户端又是如何知道NFS服务器端到底使用的是哪个端口呢?这时就需要通过远程过程调用(Remote Procedure Call,RPC)协议来实现了!

NFS RPC最主要的功能就是记录每个NFS功能所对应的端口号,并且在NFS客户端请求时将该端口和功能对应的信息传递给请求数据的NFS客户端,让客户端可以链接到正确的端口上去,从而实现数据传输。只在第一次建立连接时候帮助网络应用程序找到正确的port,当双方正确连接时,端口就和应用绑定,PPC就无用了。相当于媒婆。

RPC怎样知道NFS的每个端口呢?
原因是,当NFS服务启动时会随机取用数个端口,并主动向RPC服务注册取用的相关端口信息,这样,RPC服务就可以知道每个端口所对应的NFS功能了,然后RPC服务使用固定的端口号111来监听NFS客户端提交的请求,并将正确的NFS端口应答给NFS客户端,这样一来,就可以让NFS客户端与服务端进行数据传输了。
提示:在启动NFS SERVER之前,首先要启动RPC服务(即rpcbind服务,下同)否则NFS SERVER就无法向RPC服务注册,另外,如果RPC服务重新启动,原来已经注册好的NFS端口数据就会全部丢失。因此此时RPC服务管理的NFS程序也要重新启动以重新向RPC注册。特别注意:一般修改NFS配置文档后,是不需要重启NFS的,直接在命令执行exportfs –rv即可使修改的/etc/exports生效。
客户端NFS和服务端NFS通讯过程

  1. 首先服务器端启动RPC服务,并开启111端口
  2. 启动NFS服务,并向RPC注册端口信息
  3. 客户端启动RPC(rpcbind服务),向服务端的RPC(rpcbind)服务请求服务端的NFS端口
  4. 服务端的RPC(rpcbind)服务反馈NFS端口信息给客户端。
  5. 客户端通过获取的NFS端口来建立和服务端的NFS连接并进行数据的传输

1273933-20181109163226059-1076752898.png

二、NFS服务端软件的安装

  1. 安装nfs服务主程序nfs-kernel-server
    这个软件提供nfs的主要服务,提供文件系统的完整功能。这里要提醒一下,NFS服务器会直接使用到内核的模块,所以内核必须要支持 NFS 才行。如果操作系统的版本是自行编译的内核的话,需要注意编译NFS的内核支持。安装命令如下:
    -------------------------------------------------
    root@debian:~# apt install nfs-kernel-server
    -------------------------------------------------
  2. 安装RPC主程序rpcbind以前的程序名叫做portmap
    NFS 服务都是通过 RPC 来具体实现的,所以要正常使用NFS服务,需要启动 rpcbind来实现端口的映射工作,正常安装完nfs-kernel-server程序会
    自动安装上rpcbind,如果没有安装通过下面命令安装:
    ------------------------------------------------
    root@debian:~# apt install rpcbind
    ------------------------------------------------

三、查看NFS相关服务是否启动

  1. 产看NFS主服务:
    -----------------------------------------------------------
    root@debian:~# systemctl status nfs-kernel-server.service
    -----------------------------------------------------------
    nfs-kernel-server.service和nfs-server.service是一个,查看那个都可以
  2. 查看rpcbind服务
    ---------------------------------------------------
    root@debian:~# systemctl status rpcbind.service
    ---------------------------------------------------
    如果上面的服务没有启动手动进行启动
  3. 设置NFS主程序开机启动
    ------------------------------------------------------------
    root@debian:~# systemctl enable nfs-kernel-server.service
    ------------------------------------------------------------
    正常安装完程序后会启动服务并添加开机启动。
  4. 设置rpcbind主程序开机启动
    ------------------------------------------------
    root@debian:~# systemctl enable rpcbind.service
    ------------------------------------------------
    正常安装完程序后会启动服务并添加开机启动。

四、NFS server端的配置

  1. 主要的配置文件:/etc/exports
    这个文件是NFS的主要配置文件,如果不存在新建这个文件,后面介绍他的用法。
  2. NFS文件系统的维护命令:exportfs
    这个是维护NFS共享资源的命令,我们可以利用这个命令重新共享/etc/exports更新的目录资源、将NFS server共享的的目录卸载或重新共享等,这是NFS系统里面相当重要的一个命令,后面会具体介绍。

五、/etc/exports配置文件的语法与参数

  1. 先举个例子:将/media/目录共享给局域网中192.168.1.0/24网段的用户可读写并且数据同步写入到硬盘和内存,所以用户只读。/etc/exports配置文件的内容如下:
    =============================================
    root@debian:~# vim.tiny /etc/exports
    /media     192.168.1.0/24(rw,sync)        *(ro)
    [共享目录]   [可以访问的主机(权限)]       [*代表所有用户(权限)]
    =============================================

    这个配置文件很简单,每一行前面是要共享的目录,是以目录为单位。然后这个目录可以依照不同的权限共享给不同的主机,不同的主机用空格分开,主机后面是以小括号"()"定义权限参数,若权限参数不只一个时,则以逗号","分开,并且主机名与小括号是连在一起的,主机与共享目录之间用空格分开,在这个文件内也可以利用#号来进行注释。

  2. 主机设置主要有以下几种方式:
    可以使用完整的IP或者网络号,例如:
    单个主机:192.168.1.100 网络号:192.169.1.0/24或192.168.1.0/255.255.255.0
    可以使用主机名,但这个主机名必须要在/etc/hosts内,或可以使用DNS找到该名称,重点是可以找到IP地址,如果是主机名可以支持通配符,例如"*"或"?"均可接受。
  3. 小括号内常用的权限参数如下:
    ================================================================
    ro:共享目录只读;
    rw:共享目录可读可写
    sync:同步,将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性;
    async:异步,将数据先保存在内存缓冲区中,必要时才写入磁盘,效率高,但有丢失数据的风险;
    wdelay(默认):如果有多个客户端要对同一个共享目录进行写操作,则将这些操作集中执行。对有很多小的IO写操作时,使用该选项可以有效的提高效率;
    no_wdelay:如果有多个客户端要对同一个共享目录进行写操作则立即写入。当设置了async选项时,no_wdelay选项无效,应与sync配合使用;
    root_squash(默认):将来访的root用户映射为匿名用户或用户组;
    no_root_squash:来访的root用户保持root帐号权限;
    all_squash:所有访问用户都映射为匿名用户或用户组;
    no_all_squash(默认):访问用户先与本机用户匹配,匹配失败后再映射为匿名用户或用户组;
    anonuid=<UID>:指定匿名访问用户的本地用户UID,默认为nfsnobody(65534);
    anongid=<GID>:指定匿名访问用户的本地用户组GID,默认为nfsnobody(65534);
    secure(默认):限制客户端只能从小于1024的tcp/ip端口连接服务器;
    insecure:允许客户端从大于1024的tcp/ip端口连接服务器;
    subtree_check :若输出目录是一个子目录,则nfs服务器将检查其父目录的权限;
    no_subtree_check(默认) :即使输出目录是一个子目录,nfs服务器也不检查其父目录的权限,这样可以提高效率;
    hide:共享一个目录时,不共享该目录的子目录;
    no_hide:共享子目录;
    ================================================================

    如果想了解更多的参数,可以使用man exports

六、NFS权限控制

NFS服务器的架设比较简单,最大的问题在于权限方面的管理。前面已经介绍了NFS提供了ro和rw选项,可以控制客户端对共享文件的读或写权限。其实NFS共享文件的访问权限并不仅仅由这些选项决定,它由三方面控制,第一方面就是服务器端nfs配置文件里权限参数的控制,第二方面就是服务器端文件在操作系统里的权限控制,包括共享的目录和里面文件的权限,也就是文件的属性中的rwx(读、写、执行)。第三方面就是客户端,共享的目录需要先挂载到客户端指定的目录上,客户端才能通过挂在目录访问,所以客户端挂载目录的权限也起作用,就是目录属性中的rwx(读、写、执行)后面客户端将再介绍。只有客户端的用户同时满足上面的三个条件才能访问共享目录里面的文件。

操作系统对用户的判断其实并不是通过用户名,而是通过/etc/passwd文件中所记录的UID号,由于NFS客户端和NFS服务器是两台不同的机器,即使他们的操作系统有相同的用户名,其UID也不一定一样,那么客户端用户访问NFS服务器的过程是怎样的那?下面举例说明下。
查看用户UID的命令如下:

--------------------------------
root@debian:~# id -u 用户名
--------------------------------

客户端用户访问共享目录文件的过程如下:

先假设客户端用户名为laopi,UID暂定,用于挂载的共享目录为/Client目录,用户laopi对这个目录的权限为读写执行(目录的执行权限是可以进入这个目录),服务器端共享目录为/Server目录权限为drwxr-xr-x(目录的所有者用户为pipci,UID为1000),nfs共享配置文件里设置为rw

  1. 当客户端用户laopi的UID与服务器端用户pipci的UID相同时,即laopi的UID也为1000,这种情况由于用户laopi对挂载目录/Client具有读写权执行限,nfs共享配置文件里设置为读写,NFS服务器上pipci用户对共享目录/Server具有读写执行权限,所以客户端用户对共享目录/Server也具有读写执行权限,因为UID相同。
  2. 当客户端用户laopi的UID与服务器端用户pipci的UID不同时,假设laopi的UID为1010,这种情况由于用户laopi对挂载目录/Client具有读写权执行限,nfs共享配置文件里设置为读写,但是NFS服务器上UID为1010的用户对共享目录/Server只具有读写权限,所以客户端用户对共享目录/Server也只具有读写权限,因为UID为1010的用户对/Server目录来说是其他用户。如果也想让laopi用户具有读写执行的权限可以为目录/Server添加其他用户的执行权限
  3. 当客户端用户为root,因为正常所有Linux操作系统里的root用户UID都为0,所以限制权限的只有nfs共享配置文件里设置参数了,如果权限参数有root_squash选项和rw选项,则就的看共享目录对匿名用有什么权限,如果权限参数root_squash改为no_root_squash选项,那就没有限制了。

七、exportfs命令:输出共享目录

NFS服务启动时会读取/etc/exports配置文件中的内容,把文件中设置的共享目录输出供客户端使用,在NFS服务启动后,如果对/etc/exports进行了更改,需要通过exports命令对共享的目录进行输出,输出完成后,客户端才能访问新设置的共享目录。exportfs命令的用法如下:
root@debian:~# exportfs [参数]

参数:
-a :全部输出或取消输出/etc/exports中共享的内容
-r :重新读取/etc/exports中的配置
-u :取消一个或多个共享目录的输出
-i :忽略/etc/exports中的配置,而使用默认或命令行中指定的选项
-o :通过命令添加共享目录,重启后失效。
-v :如果不跟其他选项一起使用,则显示当前共享的所有目录及他们的选项设置,如果输出或取消

输出共享目录,则显示进行了那些操作。
具体例子:

root@debian:~# exportfs -v        #产看所以共享目录
root@debian:~# exportfs -rv       #使/etc/exports更改的配置生效
root@debian:~# exportfs -au       #取消当前所有的共享目录
root@debian:~# exportfs -av       #只查看/etc/exports中共享的目录同时恢复取消的共享目录
root@debian:~# exportfs -o rw,async 192.168.2.100:/mnt     #添加共享目录/mnt,注意命令格式-o参数后面是权限,多个权限用逗号分开,权限后面是可以访问的主机或网段,后面是共享的目录,主机和共享目录之间有冒号(:)并且不可以有空格。
root@debian:~# exportfs -uv 192.168.2.100:/mnt     #取消exportfs命令添加的共享目录

八、NFS客户端配置

  1. 安装客户软件nfs-common和rpcbind
    ---------------------------------------------
    root@debian:~# apt install nfs-common
    root@debian:~# apt install rpcbind
    ---------------------------------------------
    正常安装完nfs-common这个软件rpcbind也会一同安装上
  2. 查看rpcbind服务的状态
    -------------------------------------------------
    root@debian:~# systemctl status rpcbind.service
    -------------------------------------------------
    没有启动要手动启动这个服务,并设置开机启动这个服务
  3. 通过命令showmount查看共享的目录
    在挂载远程NFS共享目录前,做好先使用showmount命令查看NFS服务器的共享目录列表,已确定这些共享目录是否运行本地访问。showmount命令的格式如下:
    root@debian:~# showmount  [选项]  [主机的IP或名称]
    常用的选项如下:
    -a:该选项一般在NFS服务器上使用,用于显示已经挂载了服务器共享目录的客户端及他们所使用的共享目录。
    -d:与-a类似,但只显示目录,不显示具体的客户端。
    -e:显示指定NFS服务器输出的共享目录列表
    -h:显示帮助信息
    -v:显示版本信息
    --no-headers:不输出标题信息
    具体例子:
    root@debian:~$ showmount -e 192.168.1.102       #显示NFS服务器192.168.1.102 输出的共享目录
  4. 创建挂载点并挂载共享目录
    用户可以自定义挂载点,而与需与NFS服务器上共享目录一样的路径,用户可以创建多个挂载点,挂载同一个共享目录。创建的目录如果由使用者全权控制,使用者对挂载目录应该有读写执行的权限。
    挂载共享目录使用的命令与挂载本地文件系统使用的命令一样,都是使用mount命令,其格式如下:
    root@debian:~# mount  [选项]  NFS服务器IP或主机名:共享目录  挂载点
    命令格式中的冒号(:)一定不要少,他是在NFS服务器IP或主机名和共享目录中间,没有空格,mount命令与nfs相关选项说明如下:
    -t nfs:指定要挂载的文件系统类型为NFS,不加这个选项也可以,mount命令会自动识别
    -o ro:只挂载的文件系统为只读
    -o rw:可读写
    -o port=n:指定连接NFS服务器使用的端口号
    -o retry=n:指定放弃挂载前尝试的时间,单位为分钟。前台挂载的默认值为2,后台挂载的默认值为10000
    -o fg:指定以前台方式完成挂载工作。如果与NFS服务器之间的连接存在问题,那么mount命令会一直重复尝试挂载,直到成功或超时为止。在这个过程中,mount命令会占用终端窗口,用户无法在窗口中运行其他命令
    -o bg:与fg相反,使用后台方式完成挂载工作。如果与NFS服务器之间的连接存在问题,那么mount命令会在后台进行挂载,而不会占用终端窗口。
    注意:-o选项可以通过逗号(,)分隔,联合使用。

    具体例子: 把NFS服务器192.168.1.102的共享目录/media挂在到本地的/mnt目录,挂载选项设置为只读,后台挂载方式,放弃挂载前尝试的时间为1分钟
    -------------------------------------------------------------------------------
    root@debian:~# mount -t nfs -o rw,bg,retry=1 192.168.1.102:/media /mnt
    -------------------------------------------------------------------------------

  5. 卸载NFS文件系统
    与卸载普通的本地文件系统一样,可以通过umount命令把它卸载,终止与NFS服务器的连接。但在卸载前,应该确保已经没有任何进程在使用该文件系统。
    卸载NFS文件系统的命令格式如下所示:
    root@debian:~# umount [远程文件系统或挂载点]
    卸载上面例子挂载的NFS文件系统的方法:
    root@debian:~# umount /media 或root@debian:~# umount 192.168.1.102:/media

九、使用autofs按需挂载共享目录

NFS文件系统具有动态性,即需要的时候才有必要挂载。我们怎么才可以做到仅在访问时候才动态挂载共享目录那,我们用autofs服务来实现。Autofs与Mount/Umount的不同之处在于,它是一种看守程序。如果它检测到用户正试图访问一个尚未挂载的文件系统,它就会自动检测该文件系统,如果存在,那么Autofs会自动将其挂接。另一方面,如果它检测到某个已挂接的文件系统在一段时间内没有被使用,那么Autofs会自动将其卸载。因此一旦运行了Autofs后,用户就不再需要手动完成文件系统的挂接和卸载。

  1. 安装autofs软件
    ------------------------------------
    root@debian:~# apt install autofs
    ------------------------------------
  2. 产看autofs服务状态
    ------------------------------------------------
    root@debian:~# systemctl status autofs.service
    ------------------------------------------------
    上面的服务如果没有启动,启动他
  3. 设置autofs服务开机启动
    -----------------------------------------------
    root@debian:~# systemctl enable autofs.service
    -----------------------------------------------
  4. autofs配置文件设置
    /etc/auto.master是autofs的主要配置文件,该文件的配置比较简单,只需要设置挂载点的父目录和映射文件即可,格式如下所示:
    -------------------------------
    挂载点父目录 映射文件
    ------------------------------
    挂载点父目录:例如要把共享目录挂载到本地的/mnt/nfsdd目录下,那么这个挂载点父目录就是/mnt,而子目录nfsdd并不需要手工创建,他会由autofs服务管理,在需要挂载时动态创建,这个父目录最好是空白的,因为如果不是空的那么当启动这个服务时这个父目录里的文件会全部不可见。
    映射文件:该文件是由用户自行制定并创建(一般设置为/etc/auto.nfs),在该文件中设置了NFS文件系统应该如何挂载。
    映射文件格式如下:
    -----------------------------------------------------
    挂载点 挂载选项 NFS服务器IP或主机名:共享目录
    -----------------------------------------------------
    其中挂载点为/etc/auto.master文件中设置的父目录下的子目录,子目录为相对目录,如上面的挂载点应该是nfsdd而不是/mnt/nfsdd ,挂载选项与mount命令中的选项一样,但是选项前面需要加一个减号(-),配置文件更改后需要重启autofs服务才能使配置生效。
  5. 举例说明: 假设需要把NFS服务器192.168.1.102共享目录/media自动挂载到本地目录/mnt/nfsdd
    先配置/etc/auto.master文件,内容如下:
    -----------------------------------
    /mnt /etc/auto.nfs
    -----------------------------------
    在配置映射文件/etc/auto.nfs,内容如下:
    ------------------------------------------------
    nfsdd -rw,bg,retry=1 192.168.1.102:/media
    ------------------------------------------------
    选项前面需要加一个减号(-)
    配置完上面的文件重启autofs服务
    root@debian:~# systemctl restart autofs.service
    在目录/media下直接cd nfsdd就可以完成进入nfsdd目录并且已经挂载成功
    root@debian:/mnt# cd nfsdd
    root@debian:/mnt/nfsdd#

十、NFS服务器防火墙配置

当NFS服务器上启用了防火墙,客户端通过命令showmount -e 192.168.1.102查看共享的目录可能会出现clnt_create: RPC: Port mapper failure - Unable to receive: errno 0 (Success)这个错误,解决的办法有两种,一种是直接关闭防火墙,第二种是添加策略,添加策略比较麻烦因为除了固定的端口port 111和2049之外还有很多服务开启不固定的端口,通过下面的方法处理:

  1. 查看NFS需要开启的服务和对应的端口号
    -------------------------------------------
    root@debian:~# rpcinfo -p
    program vers proto port service
    ​ 100000 4 tcp 111 portmapper
    ​ 100000 3 tcp 111 portmapper
    ​ 100000 2 tcp 111 portmapper
    ​ 100000 4 udp 111 portmapper
    ​ 100000 3 udp 111 portmapper
    ​ 100000 2 udp 111 portmapper
    ​ 100005 1 udp 57974 mountd
    ​ 100005 1 tcp 60537 mountd
    ​ 100005 2 udp 36805 mountd
    ​ 100005 2 tcp 35311 mountd
    ​ 100005 3 udp 60721 mountd
    ​ 100005 3 tcp 54945 mountd
    ​ 100003 3 tcp 2049 nfs
    ​ 100003 4 tcp 2049 nfs
    ​ 100227 3 tcp 2049
    ​ 100003 3 udp 2049 nfs
    ​ 100003 4 udp 2049 nfs
    ​ 100227 3 udp 2049
    ​ 100021 1 udp 41235 nlockmgr
    ​ 100021 3 udp 41235 nlockmgr
    ​ 100021 4 udp 41235 nlockmgr
    ​ 100021 1 tcp 38331 nlockmgr
    ​ 100021 3 tcp 38331 nlockmgr
    ​ 100021 4 tcp 38331 nlockmgr
    root@debian:~#
    ----------------------------------------------
    通过上面的显示可以看到NFS启动了portmapper、mountd、nfs、nlockmgr四个服务和对应的端口号, portmapper和nfs是固定端口,我们可以将剩下的两个服务的端口也固定下来。
  2. 通过配置文件/etc/default/nfs-kernel-server可以设置mountd服务的固定端口,添加如下的内容:
    ----------------------------------------------------------
    root@debian:~# vim.tiny /etc/default/nfs-kernel-server
    RPCMOUNTDOPTS="-p 32767" #设置规定端口好为32767,重启服务生效
    ----------------------------------------------------------
  3. 通过配置文件/etc/modprobe.d/local.conf可以设置nlockmgr服务的固定端口,添加如下的内容:
    --------------------------------------------------------
    root@debian:~# vim.tiny /etc/modprobe.d/local.conf #这个local.conf文件没有需要创建,重启电脑生效
    options lockd nlm_udpport=32768 nlm_tcpport=32768 #设置规定端口好为32768
    options nfs callback_tcpport=32764
    --------------------------------------------------------
    有关更多设置可以查看这个网址:https://wiki.debian.org/SecuringNFS
  4. 重启电脑从新查看对应的端口
    --------------------------------------------
    root@debian:~# rpcinfo -p
    program vers proto port service
    ​ 100000 4 tcp 111 portmapper
    ​ 100000 3 tcp 111 portmapper
    ​ 100000 2 tcp 111 portmapper
    ​ 100000 4 udp 111 portmapper
    ​ 100000 3 udp 111 portmapper
    ​ 100000 2 udp 111 portmapper
    ​ 100005 1 udp 32767 mountd
    ​ 100005 1 tcp 32767 mountd
    ​ 100005 2 udp 32767 mountd
    ​ 100005 2 tcp 32767 mountd
    ​ 100005 3 udp 32767 mountd
    ​ 100005 3 tcp 32767 mountd
    ​ 100003 3 tcp 2049 nfs
    ​ 100003 4 tcp 2049 nfs
    ​ 100227 3 tcp 2049
    ​ 100003 3 udp 2049 nfs
    ​ 100003 4 udp 2049 nfs
    ​ 100227 3 udp 2049
    ​ 100021 1 udp 32768 nlockmgr
    ​ 100021 3 udp 32768 nlockmgr
    ​ 100021 4 udp 32768 nlockmgr
    ​ 100021 1 tcp 32768 nlockmgr
    ​ 100021 3 tcp 32768 nlockmgr
    ​ 100021 4 tcp 32768 nlockmgr
    root@debian:~#
    ---------------------------------------------
    查看已经改过来了
  5. 添加防火墙规则
    --------------------------------------------
    root@debian:~# iptables -A INPUT -p tcp --dport 111 -j ACCEPT
    root@debian:~# iptables -A INPUT -p udp --dport 111 -j ACCEPT
    root@debian:~# iptables -A INPUT -p tcp --dport 2049 -j ACCEPT
    root@debian:~# iptables -A INPUT -p udp --dport 2049 -j ACCEPT
    root@debian:~# iptables -A INPUT -p tcp --dport 32767 -j ACCEPT
    root@debian:~# iptables -A INPUT -p udp --dport 32767 -j ACCEPT
    root@debian:~# iptables -A INPUT -p tcp --dport 32768 -j ACCEPT
    root@debian:~# iptables -A INPUT -p udp --dport 32768 -j ACCEPT
    --------------------------------------------