李守中

NFS 笔记

Table of Contents

1 NFS 服务端使用记录

1.1 Linux NFS 服务端

1.1.1 安装服务

使用 RH 系发行版:

  1. sudo dnf install nfs-utils 安装 nfs-utils 软件包。
  2. sudo systemctl enable --now nfs-server 开机自动启动服务器。
  3. sudo firewall-cmd --add-service={rpc-bind,mountd,nfs} --permanent 开放端口。
  4. sudo firewall-cmd --reload 重载防火墙使添加的服务生效。

使用 Debian 系发行版:

  1. sudo apt install nfs-kernel-server 安装 NFS 服务端。
  2. sudo systemctl enable --now nfs-server.service 启动 NFS Server 并设置开机启动。

硬盘挂载方式为 /etc/systemd/system/mnt-hgst3ta.mount 服务:

[Install]
WantedBy=multi-user.target

[Mount]
Options=defaults
Type=ext4
What=/dev/disk/by-uuid/cd2949ed-9e14-4a34-a703-6dcbd761e9d3
Where=/mnt/hgst3ta

[Unit]
Description=Mount 2nd HGST 3T disk under /mnt/hgst3ta.

挂载硬盘到 /mnt/hgst3ta 后 /mnt/hgst3ta 的权限为 drwxr-xr-x. 2 root root 4.0K ...

sudo chmod 777 /mnt/hgst3ta 对所有用户开放所有权限。

配置文件 /etc/exports 的内容为 /mnt/hgst3ta (rw,sync,root_squash)

执行 sudo exportfs -ra 命令根据 /etc/exports 文件重新载入共享配置。

exportfs 常用的选项有:

  • -r: 打开或取消所有目录共享。它使 /var/lib/nfs/xtab 和 /etc/exports 同步,将 /etc/exports 中已删除的条目从 /var/lib/nfs/xtab 中删除,将内核共享表中任何不再有效的条目移除。
  • -a: 根据将其它选项传递给 exportfs 导致所有目录被导出或取消导出。如果没有指定其他选项, exportfs 会导出 /etc/exports 中指定的所有文件系统。
  • -o <file_systems>: 指定导出没有在 /etc/exports 中列出的目录。格式必须与在 /etc/exports 中的相同。这个选项通常用来测试导出的文件系统,然后再将其永久添加到导出的文件系统列表中。
  • -i: 忽略 /etc/exports,用命令行给出的选项定义导出的文件系统。
  • -u: 取消导出所有共享目录。
    • exportfs -ua 命令挂起所有的 NFS 共享,同时保持所有 NFS 服务。
    • exportfs -r 可新启用 NFS 共享。
  • -v: 在执行 exportfs 命令时,详细显示要导出或取消导出的文件系统。

1.1.2 exports 常用配置参数

/etc/exports 管理 NFS 的共享配置,每个条目都有以下结构:

export host(options)
export host1(options1) host2(options2) host3(options3)
export *(options)

/mnt/hgst8ta *(rw,async,root_squash,no_subtree_check)
/mnt/hgst3ta *(rw,async,root_squash,no_subtree_check)

配置的选项有:

  • ro: read-only 远程主机无法更改文件系统中共享的数据。
  • rw: read-write 远程主机可以更改文件系统中共享的数据。
  • sync: 同步写入。在数据写入磁盘前,NFS 服务器不返回写入成功。
  • async: 异步写入。在数据写入磁盘前,NFS 服务器返回写入成功。
  • wdelay: 如果 NFS 服务器预期另外一个写入请求即将发生,则 NFS 服务器会延迟写入磁盘。它减少了必须使用独立写入命令访问磁盘的次数,从而减少写入开销。要禁用此选项,指定 no_wdelay 选项,该选项仅在同时指定默认同步选项时才可用。
  • root_squash 挂载 NFS 目录的用户如果是 root 那么这个用户的权限将被压缩成为匿名用户,相应的 UID 与 GID 都会变成 nobody 系统账号的身份。
  • no_root_squash 挂载 NFS 目录的用户如果是 root 那么对于这个共享目录来说,挂载方具有 root 权限。 这个特性有安全问题,不建议用。
  • all_squash 挂载 NFS 目录的所有用户均被压缩成为匿名用户,即以 nobody 用户的身份登录。
  • anonuidanongid 明确指定 NFS 服务器上用户的 uid 和 gid,挂载方的用户必须拥有相同的 uid 和 gid 才能正常访问 NFS 目录。配置写成 export host(anonuid=uid,anongid=gid) 这样的。
  • secure 限制挂载方只能从小于 1024 的 tcp/ip 端口连接 NFS 服务器 ( 默认设置 )。改为 insecure 允许客户端从大于 1024 的 tcp/ip 端口连接服务器。
  • subtree_check 若输出目录是一个子目录,则 NFS 服务器将检查其父目录的权限。
  • no_subtree_check 即使输出目录是一个子目录,NFS 服务器也不检查其父目录的权限。

如果服务器支持 NFSv3 则客户端使用 sudo showmount --exports <server-ip> 查看有哪些共享目录。

如果服务器支持 NFSv4 则客户端可以直接挂载根目录后进行查找:

mount <my-server>:/ /mnt/
ls /mnt/

1.1.3 仅开启 NFSv4

NFSv4 只需要 TCP/UDP 2049 一个端口,也不需要 portmap 这个包。

首先把 /etc/default/nfs-common 内容修改为:

# If you do not set values for the NEED_ options, they will be attempted
# autodetected; this should be sufficient for most people. Valid alternatives
# for the NEED_ options are "yes" and "no".

# Do you want to start the statd daemon? It is not needed for NFSv4.
NEED_STATD="no"

# Options for rpc.statd.
#   Should rpc.statd listen on a specific port? This is especially useful
#   when you have a port-based firewall. To use a fixed port, set this
#   this variable to a statd argument like: "--port 4000 --outgoing-port 4001".
#   For more information, see rpc.statd(8) or http://wiki.debian.org/SecuringNFS
STATDOPTS=

# Do you want to start the idmapd daemon? It is only needed for NFSv4.
NEED_IDMAPD="yes"

# Do you want to start the gssd daemon? It is required for Kerberos mounts.
NEED_GSSD=

然后把 /etc/default/nfs-kernel-server 内容修改为:

# Number of servers to start up
RPCNFSDCOUNT=8

# Runtime priority of server (see nice(1))
RPCNFSDPRIORITY=0

# Options for rpc.mountd.
# If you have a port-based firewall, you might want to set up
# a fixed port here using the --port option. For more information,
# see rpc.mountd(8) or http://wiki.debian.org/SecuringNFS
# To disable NFSv4 on the server, specify '--no-nfs-version 4' here
RPCMOUNTDOPTS="--manage-gids -N 2 -N 3"

# Do you want to start the svcgssd daemon? It is only required for Kerberos
# exports. Valid alternatives are "yes" and "no"; the default is "no".
NEED_SVCGSSD=""

# Options for rpc.svcgssd.
RPCSVCGSSDOPTS=""

RPCNFSDOPTS="-N 2 -N 3"

NFSv4 中的 rpcbind 已经没有用了,但它依旧会随 nfs-server.service 启动。最后需要停掉它们:

sudo systemctl mask rpcbind.socket
sudo systemctl mask rpcbind.service

1.2 FreeBSD NFS 服务端

1.2.1 启动服务

FreeBSD 系统内置了 NFS 的服务端与客户端,所以不需要安装其他的包。

让 NFS 服务开机自启动,需要在 /etc/rc.conf 里添加:

rpcbind_enable="YES"
nfs_server_enable="YES"
nfs_server_flags="-u -t -n 4"
mountd_flags="-r"

# 开启 nfsv4
nfsv4_server_enable="YES"
nfsuserd_enable="YES"

注: 只要 nfs_server_enable 被设置为 "YES"mountd 就能自动运行。

要让被导出的文件系统能被支持 NFS v4 协议的 client 挂载,还要在 /etc/exports 里面加入:

V4: /

用 root 权限执行下面的命令,可立即启动 NFS 服务:

rpcbind
nfsd -u -t -n 4
mountd -r

修改 /etc/exports 文件后,必须让 mountd 服务根据新的内容重新生成配置。一种方法是通过给正在运行的服务程序发送 HUP 信号来完成:

# root
kill -HUP `cat /var/run/mountd.pid`
# 普通用户
sudo kill -HUP `sudo cat /var/run/mountd.pid`

或指定适当的参数来运行 mountd rc(8) 脚本:

/etc/rc.d/mountd onereload

1.2.2 exports 常用配置参数

比如,指定两个 client 操作服务器数据时具有 lsz 用户的身份,其他 client 只能读取:

V4: /

/mnt/mymir2/data -alldirs -mapall=lsz 192.168.1.20
/mnt/mymir2/data -alldirs -mapall=lsz 192.168.1.30
/mnt/mymir2/data -alldirs -mapall=lsz -ro

/mnt/mymir2/videos -alldirs -mapall=lsz 192.168.1.20
/mnt/mymir2/videos -alldirs -mapall=lsz 192.168.1.30
/mnt/mymir2/videos -alldirs -mapall=lsz -ro
  • V4: / 表示在 / 路径下的所有 NFS share 都能被 client 以 NFS v4 协议挂载。
  • -ro 表示 read only 所有挂载此 NFS share 的 client 不能做读取之外的其他操作。
  • -maproot=user 如果 client 以 root 身份操作服务器,那么所有服务器的操作会交由本机的 user 来执行。
  • -mapall=user 所有 client 的操作都交由本机 user 用户执行。
  • -alldirs client 可以将 NFS share 的任意子目录设为挂载点。即,如果在 /etc/exports 导出 /usr 目录,那么 client 可以挂载 /usr/include 目录。
  • -network {IP}-mask {MASK} 指定允许连接的 IP 段。

1.2.3 ZFS 的坑

1.2.3.1 不推荐使用 ZFS 的 sharenfs 功能

Solaris 上 ZFS 的 sharenfs 可以直接操作内核里的 NFS。但在 FreeBSD 上,sharenfs 做的是把配置写入 /etc/zfs/exports 再使用 FreeBSD 的 NFS 服务器根据这个文件重新生成导出配置。

但是 FreeBSD ZFS 上这样的 sharenfs 操作有时会出一些奇怪的问题。至少在 FreeBSD 13 还没解决。另外,FreeBSD 用的是 ZOL ( ZFS On Linux ) 而不是 Solaris 的 ZFS 代码,我猜这可能是问题的起因吧。

虽然对于一些简单的 NFS share 使用 sharenfs 没什么问题,但还是不建议用它。

使用传统的 exports(5) 文件是最稳妥的选择。

1.2.3.2 嵌套挂载的 zfs 文件系统需要分别导出

现在有这么一个 zpool:

  pool: mymir2
 state: ONLINE
  scan: resilvered 2.46T in 06:52:59 with 0 errors on Sun Aug 15 17:50:49 2021
config:

        NAME        STATE     READ WRITE CKSUM
        mymir2      ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            da4     ONLINE       0     0     0
            da5     ONLINE       0     0     0

errors: No known data errors

在这个 zpool 上有这些 zfs 文件系统:

% zfs list
NAME                 USED  AVAIL     REFER  MOUNTPOINT
mymir2              2.55T  81.1G      104K  /mnt/mymir2
mymir2/data         99.7G  81.1G     99.7G  /mnt/mymir2/data
mymir2/videos       2.46T  81.1G     2.46T  /mnt/mymir2/videos

也就是说,以 mymir2 为根节点的树上,有 3 个节点,其中 mymir2/datamymir2/videos 为两个同级的叶子节点。

此时要导出整个 mymir2 根节点 ( 即客户端挂载根节点的 NFS share 后可以直接操作表现为两个文件夹的两个叶子节点 ),那么如果在 /etc/exports 里写:

/mnt/mymir2 -alldirs -maproot=root -network 192.168.1.0 -mask 255.255.255.0

执行 kill -HUP mountd 重载配置文件之后,在客户端 ( 比如 windows ) 使用 mount -o nolock -o mtype=hard -o timeout=60 \\192.168.1.10\mnt\mymir2 K:\ 进行挂载。

此时在 windows 上的 K:\ 路径下只会看到 datavideos 两个空文件夹。这意味着,对应的 mymir2/datamymir2/videos 两个子文件系统 ( 叶子节点 ) 并没有被 NFS share 导出。对于 K:\dataK:\videos 的读写操作会直接作用在 mymir2 这个根节点上,不会进入叶子节点。

如果想要导出一个根节点和两个叶子节点 ( 一共 3 个 zfs 文件系统 ),只能在 /etc/exports 里这样写,分别导出:

/mnt/mymir2 -alldirs -maproot=root -network 192.168.1.0 -mask 255.255.255.0
/mnt/mymir2/data -alldirs -maproot=root -network 192.168.1.0 -mask 255.255.255.0
/mnt/mymir2/videos -alldirs -maproot=root -network 192.168.1.0 -mask 255.255.255.0

每个 exports 项只能导出一个文件系统。挂载时,也只能分别将导出的文件系统挂载到不同的挂载点。

2 NFS 客户端使用记录

2.1 踩坑

2.1.1 客户端挂载后无法读写

挂载点为 /mnt/hgst3ta,权限为 drwxr-xr-x. 2 root root 4.0K ...

/etc/exports 共享配置为 /mnt/hgst3ta *(rw,sync,root_squash)

因为客户端用户的 uid 和 gid 都是 1000,对应在 NFS 服务器上的 1000:1000 用户不能操作 /mnt/hgst3ta 文件夹,所以客户端的 1000:1000 用户也没有权限操作这个 NFS 共享。

解决方案有两个:

  • sudo chmod 777 /mnt/hgst3ta 让所有用户都可读写。
  • sudo usermod -aG <username-with-uid-1000> root 把这个用户加入 root 用户组,再 sudo chmod 774 /mnt/hgst3ta 让用户组有读写权限。

注意: 第一个配置会使文件夹对所有用户开放,任何挂载 /mnt/hgst3ta 的用户都可以修改文件夹中的文件。

2.2 Linux 客户端

使用 RH 系发行版: sudo dnf install nfs-utils 安装 nfs-utils 软件包。

使用 Debian 系发行版: sudo apt install nfs-common 安装 NFS 客户端与服务端。

注: 网上教程也有说用 sudo apt install nfs-client 只安装客户端就行。但是自从 Debian 11 开始的软件库里不再有 nfs-clientsudo apt install nfs-client 会被自动定向到安装 nfs-common

sudo showmount 192.168.1.10 -e 查看服务器共享了哪些文件夹。

在客户机上用 sudo mount -t nfs -o nfsvers=4 192.168.1.10:/mnt/hgst3ta ~/NetworkStorage/hgst3ta/ 挂载。

注: man nfs 可以查看所有的挂载选项。

sudo umount 192.168.1.10:/mnt/hgst3ta 卸载。

可以创建 ~/.config/systemd/user/mnt-r6-FreeBSDa-hgst3ta.service 在用户登入时执行挂载的 .service 文件。内容如下:

[Unit]
Description=Mount r6 vm100 2nd HGST 8T disk under %h/NetworkStorage/hgst3ta/.

[Service]
ExecStart=sudo mount.nfs4 192.168.1.10:/mnt/hgst3ta %h/NetworkStorage/hgst3ta/
ExecStop=sudo umount 192.168.1.10:/mnt/hgst3ta
RemainAfterExit=yes

[Install]
WantedBy=default.target

systemctl --user enable mnt-r6-FreeBSDa-hgst3ta.service 在用户登入后自动执行。

2.3 FreeBSD 客户端

FreeBSD 系统内置了 NFS 的服务端与客户端,所以不需要安装其他的包。

让 NFS 客户端开机自启动,需要在 /etc/rc.conf 里添加:

nfs_client_enable="YES"

用 root 权限执行 nfsiod -n 4 可立即启动 NFS 客户端。

mount <ip>:/<shared_path> /<path_to_mount> 挂载 NFS 共享。

要让 NFS share 在系统启动时自动挂载,则需要在 /etc/fstab 文件中添加:

<ip>:/<shared_path> /<path_to_mount> nfs rw 0 0

注: man mount_nfs 可以查看所有的挂载参数

2.4 Windows 客户端

Windows 只能把 NFS 共享挂载在某个驱动器号下,不能挂载在某个文件夹下。

Win+R 输入 OptionalFeatures 回车,勾选 NFS 服务 -> NFS 客户端

不需要勾选 管理工具 ,那玩意是给 windows server 用的,非 Windows Server 只能靠 映射网络驱动器 挂载 NFS。

列出被 NFS 服务器导出的文件夹:

  • showmount -e [server] 显示 NFS 服务器导出的所有共享。
  • showmount -a [server] 列出客户端主机名或 IP 地址,以及使用“主机:目录”格式显示的安装目录。
  • showmount -d [server] 显示 NFS 服务器上当前由某些 NFS 客户端安装的目录。

Win+R 输入 regedit 回车,进入路径为 计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ClientForNFS\CurrentVersion\Default 的注册表,新建两个 DWORD(32位) 注册表项:

  • 数值名称为: AnonymousUID ,数值数据为: NFS 服务器上对应的 UID 值,基数为十进制值。
  • 数值名称为: AnonymousGID ,数值数据为: NFS 服务器上对应的 GID 值,基数为十进制值。

针对 windows 挂载 NFS 共享后可能出现乱码的情况,需要在 控制面板 -> 时钟和区域 -> 区域 -> 管理 -> 非 Unicode 程序的语言 -> 更改系统区域设置 -> 给 Beta 版: 使用 Unicode UTF-8 提供全球语言支持 打勾 -> 重启。

最后打开 CMD ( 不是 PowerShell ) 执行 mount -o nolock -o mtype=hard -o timeout=60 \\{ip}\{path_to_share} K:\ 进行挂载。

注: 执行 mount -h 可以查看所有的挂载选项。

除了使用 mount 命令也可使用 net use 命令来挂载 NFS 共享,比如这样:

net use P: \\192.168.1.10\mnt\hgst8ta
net use Q: \\192.168.1.10\mnt\hgst3ta


Last Update: 2023-05-18 Thu 08:33

Contact: [email protected]     Generated by: Emacs 27.1 (Org mode 9.3)

若正文中无特殊说明,本站内容遵循: 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议