NFS
来自 Wikipedia
- 网络文件系统(NFS)是Sun Microsystems于1984年开发的分布式文件系统协议,它允许客户端计算机上的用户通过网络以类似于访问本地存储的方式访问文件。
NFS FAQ 列出了 经过良好测试的文件系统,并详细介绍了与FAT32相关的限制。
- 默认情况下,NFS不加密。在处理敏感数据时,请配置#TLS加密,或配置Kerberos(
sec=krb5p提供基于Kerberos的加密),或通过加密的VPN(如WireGuard)隧道化NFS。 - 与Samba不同,NFS默认没有用户身份验证,客户端访问受其IP地址/主机名限制。如果需要更强的身份验证,则可使用Kerberos。
- NFS要求用户和/或用户组ID在客户端和服务器上相同(除非使用Kerberos)。使用NFSv4 ID映射,或在
/etc/exports中使用anonuid/anongid和all_squash手动覆盖UID/GID。 - NFS不支持POSIX ACLs。NFS服务器仍将强制执行ACL,但客户端将无法看到或修改它们。
安装
强烈建议使用时间同步守护进程来保持客户端/服务器时钟同步。在所有节点上没有精确的时钟,NFS可能会引入不必要的延迟。
服务器配置
全局配置选项在/etc/nfs.conf中设置。简单的配置用户不需要编辑此文件。
NFS服务器需要一个要共享的目录列表,其形式是导出(请参阅exports(5)获取详细信息),这些导出必须在/etc/exports或/etc/exports.d/*.exports中定义。默认情况下,目录会按原样导出;例如
/etc/exports
/data/music 192.168.1.0/24(rw)
上面的设置将目录/data/music导出为MyServer:/data/music,可用于NFSv3和NFSv4。
自定义导出根目录
共享可以相对于所谓的NFS根目录。一个良好的安全实践是定义一个NFS根目录在独立的目录树中,这将使用户局限于该挂载点。绑定挂载用于将共享挂载点链接到文件系统上其他位置的实际目录。过去,NFS根目录是NFSv4的必需项;现在它是可选的(从内核2.6.33和nfs-utils 1.2.2开始,它们实现了虚拟根)。
考虑以下示例:
- NFS根目录是
/srv/nfs。 - 通过绑定挂载到实际目标
/mnt/music来导出/srv/nfs/music。
# mkdir -p /srv/nfs/music /mnt/music # mount --bind /mnt/music /srv/nfs/music
要使绑定挂载在重启后持久化,请将其添加到fstab
/etc/fstab
/mnt/music /srv/nfs/music none bind 0 0
在/etc/exports中添加要共享的目录,并将其限制为允许挂载它们的客户端机器的地址范围(通过CIDR或主机名),例如:
/etc/exports
/srv/nfs 192.168.1.0/24(rw,fsid=root) /srv/nfs/music 192.168.1.0/24(rw,sync) /srv/nfs/home 192.168.1.0/24(rw,sync) /srv/nfs/public 192.168.1.0/24(ro,all_squash,insecure) desktop(rw,sync,all_squash,anonuid=99,anongid=99) # map to user/group - in this case nobody
使用NFSv4时,fsid=root或fsid=0选项表示“根”导出;如果存在这样的导出,则所有其他目录都必须在其下方。/etc/nfs.conf文件中的rootdir选项对此没有影响。默认行为是在没有fsid=0导出时,其行为与NFSv3相同。
在上面的示例中,因为/srv/nfs被指定为根目录,所以导出/srv/nfs/music现在可以通过NFSv4挂载为MyServer:/music – 请注意,根前缀已被省略。
- 对于NFSv3(NFSv4不需要),
crossmnt选项使得客户端可以访问标记有crossmnt的文件系统上所有挂载的文件系统,并且客户端不需要单独挂载每个子导出。请注意,如果子项与不同的地址范围共享,这可能不是期望的行为。 - 代替
crossmnt,也可以在子导出上使用nohide选项,以便在客户端挂载根导出时自动挂载它们。与crossmnt不同,nohide仍然尊重子导出的地址范围。请注意,该选项也特定于NFSv3;NFSv4*总是*表现得好像nohide已启用。 insecure选项允许客户端从1023以上的端口连接。(大概只有root用户才能使用低编号端口,因此默认情况下阻止其他端口提供了一个肤浅的访问障碍。实际上,省略或包含insecure选项都不会对安全性产生任何有意义的改善或损害。)- 使用星号(
*)允许从任何接口访问。
应注意,在服务器运行时修改/etc/exports将需要重新导出才能使更改生效。
# exportfs -arv
要查看当前加载的导出状态的更多详细信息,请使用
# exportfs -v
有关所有可用选项的更多信息,请参阅exports(5)。
fsid=1选项。启动服务器
协议版本4导出的老用户可能会想至少屏蔽rpcbind.service和rpcbind.socket,以防止多余的服务运行。参见FS#76453。此外,考虑屏蔽nfs-server.service,因为它也因某种原因被拉入。
限制NFS到特定接口/IP
默认情况下,启动nfs-server.service将监听所有网络接口上的连接,而不管/etc/exports。可以通过定义要监听的IP和/或主机名来更改此行为。
/etc/nfs.conf
[nfsd] host=192.168.1.123 # Alternatively, use the hostname. # host=myhostname
重启nfs-server.service以立即应用更改。
防火墙配置
为了允许NFSv4服务器通过防火墙访问,必须为入站连接打开TCP端口2049。(NFSv4使用静态端口号;它不使用像mountd或portmapper这样的辅助服务。)
要启用NFSv3服务器的访问,您还需要为portmapper(rpcbind)打开TCP/UDP端口111,以及MOUNT(rpc.mountd)端口。默认情况下,rpc.mountd动态选择端口,所以如果您在防火墙后面,您将希望编辑/etc/nfs.conf来设置一个静态端口。使用rpcinfo -p来检查NFSv3服务器上正在使用的确切端口。
$ rpcinfo -p
100003 3 tcp 2049 nfs 100003 4 tcp 2049 nfs 100227 3 tcp 2049 nfs_acl ...
客户端配置
打算使用带有Kerberos的NFS4的用户需要启动并启用nfs-client.target。
手动挂载
对于NFSv3,使用此命令显示服务器导出的文件系统。
$ showmount -e servername
对于NFSv4,挂载NFS根目录并查看可用的挂载。
# mount servername:/ /mountpoint/on/client
然后挂载,省略服务器的NFS导出根目录。
# mount -t nfs -o vers=4 servername:/music /mountpoint/on/client
如果挂载失败,请尝试包含服务器的导出根目录(Debian/RHEL/SLES需要-t nfs4而不是-t nfs)。
# mount -t nfs -o vers=4 servername:/srv/nfs/music /mountpoint/on/client
servername需要替换为有效的主机名(不只是IP地址)。否则,远程共享的挂载将挂起。使用/etc/fstab挂载
使用fstab对于始终在线的服务器很有用,并且NFS共享在客户端启动时可用。编辑/etc/fstab文件,并添加一个适当的行来反映设置。同样,服务器的NFS导出根目录被省略。
/etc/fstab
servername:/music /mountpoint/on/client nfs defaults,timeo=900,retrans=5,_netdev 0 0
一些额外的挂载选项可供考虑
- rsize和wsize
rsize值是读取服务器时使用的字节数。wsize值是写入服务器时使用的字节数。默认情况下,如果未指定这些选项,客户端和服务器将协商它们都能支持的最大值(有关详细信息,请参阅nfs(5))。更改这些值后,建议测试性能(参见#性能调优)。- soft或hard
- 确定NFS请求超时后NFS客户端的恢复行为。如果未指定任一选项(或指定了
hard选项),则NFS请求将无限期重试。如果指定了soft选项,则NFS客户端将在发送了retrans次重传后使NFS请求失败,导致NFS客户端向调用应用程序返回错误。
soft超时可能导致数据静默损坏。因此,仅当客户端响应性比数据完整性更重要时才使用soft选项。通过TCP使用NFS或增加retrans选项的值可以减轻使用soft选项的一些风险。- timeo
timeo值是以十分之一秒为单位的时间,在RPC超时后重传之前等待的时间。TCP上NFS的默认值是600(60秒)。第一次超时后,超时值会随着每次重试而加倍,最多60秒或直到发生主要超时。如果连接到慢速服务器或通过繁忙的网络,通过增加此超时值可以获得更好的稳定性。- retrans
- NFS客户端重试请求的次数,然后它会尝试进一步的恢复操作。如果未指定
retrans选项,则NFS客户端会尝试每个请求三次。NFS客户端在retrans次重试后会生成“服务器未响应”消息,然后尝试进一步恢复(取决于是否生效了硬挂载选项)。 - _netdev
_netdev选项告诉系统在尝试挂载共享之前等待网络启动 - systemd假定NFS也是如此。
fs_passno)设置为非零值可能导致意外行为,例如,当systemd自动挂载等待一个永远不会发生的检查时发生挂起。使用带systemd的/etc/fstab挂载
另一种方法是使用x-systemd.automount选项,该选项在访问时挂载文件系统。
/etc/fstab
servername:/home /mountpoint/on/client nfs _netdev,noauto,x-systemd.automount,x-systemd.mount-timeout=10,timeo=14,x-systemd.idle-timeout=1min 0 0
要使systemd识别fstab的更改,请重新加载systemd并[1]重启remote-fs.target。
noauto挂载选项将不会在启动时自动挂载NFS共享,直到访问它为止:使用auto则使其立即可用。
如果在网络未启动/可用时挂载失败,请启用NetworkManager-wait-online.service。它将确保在network.target启动之前所有链接都可用。users挂载选项允许用户挂载,但请注意它也意味着其他选项,例如noexec。x-systemd.idle-timeout=1min选项将在1分钟不使用后自动卸载NFS共享。适用于可能突然断开网络连接的笔记本电脑。- 如果关机/重启因NFS而花费时间过长,请启用
NetworkManager-wait-online.service以确保NetworkManager在NFS卷卸载之前不会退出。 - 不要添加
x-systemd.requires=network-online.target挂载选项,因为这可能导致systemd内部的排序循环[2]。systemd会自动为_netdev挂载添加network-online.target依赖。 - 使用
nocto选项可能会提高只读挂载的性能,但仅当服务器上的数据很少更改时才应使用。
作为 systemd 单元
在/etc/systemd/system下创建一个新的.mount文件,例如mnt-home.mount。有关详细信息,请参阅systemd.mount(5)。
mnt-home.mount只有在您要将共享挂载到/mnt/home下时才能使用。否则,可能会发生以下错误:systemd[1]: mnt-home.mount: Where= setting does not match unit name. Refusing.。如果挂载点包含非ASCII字符,请使用systemd-escape)。What= 共享的路径。
Where= 共享的挂载路径。
Options= 共享挂载选项。
- 网络挂载单元会自动获得对
remote-fs-pre.target、network.target和network-online.target的After依赖关系,并获得对remote-fs.target的Before依赖关系,除非设置了nofail挂载选项。对于后者,还会添加一个Wants单元。 - 将
noauto附加到Options,以防止在启动时自动挂载(除非被其他单元拉入)。 - 如果您想使用您要共享的服务器的主机名(而不是 IP 地址),请将
nss-lookup.target添加到After。这可能会避免启动时出现挂载错误,而这些错误在测试单元时不会出现。
/etc/systemd/system/mnt-home.mount
[Unit] Description=Mount home at boot [Mount] What=172.16.24.192:/home Where=/mnt/home Options=vers=4 Type=nfs TimeoutSec=30 [Install] WantedBy=multi-user.target
要使用mnt-home.mount,请启动该单元并启用它在系统启动时运行。
automount
要自动挂载共享,可以使用以下自动挂载单元:
/etc/systemd/system/mnt-home.automount
[Unit] Description=Automount home [Automount] Where=/mnt/home [Install] WantedBy=multi-user.target
禁用/停止mnt-home.mount单元,并启用/启动mnt-home.automount,以便在访问挂载路径时自动挂载共享。
使用autofs挂载
使用autofs对于多台计算机通过NFS连接很有用;它们既可以是客户端也可以是服务器。此方法优于前一种方法的原因是,如果服务器关闭,客户端不会抛出找不到NFS共享的错误。有关详细信息,请参阅autofs#NFS网络挂载。
技巧与提示
NFSv4 ID映射
- NFSv4 ID映射不能解决所有默认
sec=sys挂载选项的问题。参见#静态映射和[3] - NFSv4 ID映射需要在客户端和服务器上都启用。
- 另一种选择是确保用户和组ID(UID和GID)在客户端和服务器上匹配。
- 在客户端上,启用/启动
nfs-idmapd.service不是必需的,因为它已被新的ID映射器取代。
# dmesg | grep id_resolver
[ 3238.356001] NFS: Registering the id_resolver key type [ 3238.356009] Key type id_resolver registered
- 不要将
nfsidmap(仅用于NFS客户端)与nfs-idmapd.service混淆,后者由NFS服务器使用并派生rpc.idmapd进程。 rpc.idmapd和nfsidmap也共享idmapd.conf(5)中的一些配置。- 有关详细信息,请参阅idmapd(8)和nfsidmap(8)。
NFSv4协议将本地系统的UID和GID值在网络上表示为user@domain形式的字符串。从UID到字符串和从字符串到UID的转换过程称为ID映射。
域
- 默认情况下,字符串的域部分是系统的DNS域名称。如果系统是多宿主的,或者系统的DNS域名称与系统的Kerberos领域名称不匹配,也可以在/etc/idmapd.conf中指定。
- 当域未在/etc/idmapd.conf中指定时,将查询本地DNS服务器以获取_nfsv4idmapdomain文本记录。如果记录存在,则使用该记录作为域。当记录不存在时,将使用DNS域的域部分。
在stdout上显示系统的有效NFSv4域名称。
# nfsidmap -d
domain.tld
编辑以匹配服务器和/或客户端上的域。
/etc/idmapd.conf
[General] Domain = guestdomain.tld
静态映射
- 此映射仅用于客户端在本地映射uid。如果您创建了一个属于未在服务器上知的uid(例如1005)的文件。该文件以uid 1005的形式存储在服务器上,但“通过网络”显示时将不再是正确的uid。
- 与服务器交互(例如,列出文件)后,您可以看到密钥环中的所有条目。
# nfsidmap -l
7 .id_resolver keys found: uid:nobody user:1 uid:bin@domain.tld uid:foo@domain.tld gid:foo@domain.tld uid:remote_user@domain.tld uid:root@domain.tld
- 您可以使用
nfsidmap -c清除密钥环,但它不是必需的,在默认设置下,10分钟后条目会过期。
仅当服务器和客户端具有不同的用户/组名称时才需要执行这些步骤。更改仅在客户端配置文件中进行。
/etc/idmapd.conf
[Translation] # The default is nsswitch and other methods exist. method = static,nsswitch [Static] foo@domain.tld = local_foo remote_user@domain.tld = user
回退映射
仅在客户端配置中。当映射无法完成时使用的本地用户/组名称。
/etc/idmapd.conf
[Mapping] Nobody-User = nobody Nobody-Group = nobody
性能调优
当在一个拥有大量客户端的网络上使用NFS时,可以根据服务器/网络需求将默认NFS线程数从8增加到16甚至更高。
/etc/nfs.conf
[nfsd] threads=16
可能需要调整rsize和wsize挂载选项以满足网络配置的要求。
在较新的Linux内核(>2.6.18)中,NFS服务器允许的I/O操作的大小(默认最大块大小)取决于RAM大小,最大为1M(1048576字节),即使NFS客户端需要更大的rsize和wsize,也会使用服务器的最大块大小。参见https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/5/html/5.8_technical_notes/known_issues-kernel。可以通过在启动nfsd之前写入/proc/fs/nfsd/max_block_size来更改服务器允许的最大默认块大小。例如,以下命令将之前的默认iosize恢复为32k。
# echo 32768 > /proc/fs/nfsd/max_block_size
max_block_size可能会降低现代硬件上的NFS性能。为了使更改永久生效,请创建一个systemd-tmpfile。
/etc/tmpfiles.d/nfsd-block-size.conf
w /proc/fs/nfsd/max_block_size - - - - 32768
使用增加的rsize和wsize挂载选项进行挂载。
# mount -t nfs -o rsize=32768,wsize=32768,vers=4 servername:/srv/nfs/music /mountpoint/on/client
此外,尽管违反了NFS协议,但设置async而不是sync或sync,no_wdelay可能会带来显著的性能提升,尤其是在使用机械硬盘时。用此选项配置导出,然后执行exportfs -arv以应用。
/etc/exports
/srv/nfs 192.168.1.0/24(rw,async,crossmnt,fsid=0) /srv/nfs/music 192.168.1.0/24(rw,async)
async存在服务器崩溃或不正常重启时可能导致数据丢失或损坏的风险。自动挂载处理
此技巧对于无线网络和/或不可靠的网络上的NFS共享很有用。如果NFS主机变得不可达,NFS共享将被卸载,以期在尝试使用hard挂载选项时防止系统挂起[4]。
确保NFS挂载点在fstab中正确指示。
/etc/fstab
lithium:/mnt/data /mnt/data nfs noauto 0 0 lithium:/var/cache/pacman /var/cache/pacman nfs noauto 0 0
创建auto_share脚本,该脚本将由cron或systemd/Timers使用ICMP ping检查NFS主机是否可达。
/usr/local/bin/auto_share
#!/bin/bash
function net_umount {
umount -l -f $1 &>/dev/null
}
function net_mount {
mountpoint -q $1 || mount $1
}
NET_MOUNTS=$(sed -e '/^.*#/d' -e '/^.*:/!d' -e 's/\t/ /g' /etc/fstab | tr -s " ")$'\n'b
printf %s "$NET_MOUNTS" | while IFS= read -r line
do
SERVER=$(echo $line | cut -f1 -d":")
MOUNT_POINT=$(echo $line | cut -f2 -d" ")
# Check if server already tested
if [[ "${server_ok[@]}" =~ "${SERVER}" ]]; then
# The server is up, make sure the share are mounted
net_mount $MOUNT_POINT
elif [[ "${server_notok[@]}" =~ "${SERVER}" ]]; then
# The server could not be reached, unmount the share
net_umount $MOUNT_POINT
else
# Check if the server is reachable
ping -c 1 "${SERVER}" &>/dev/null
if [ $? -ne 0 ]; then
server_notok[${#server_notok[@]}]=$SERVER
# The server could not be reached, unmount the share
net_umount $MOUNT_POINT
else
server_ok[${#server_ok[@]}]=$SERVER
# The server is up, make sure the share are mounted
net_mount $MOUNT_POINT
fi
fi
done
# Check if the server is reachable
ping -c 1 "${SERVER}" &>/dev/null
与
# Check if the server is reachable
timeout 1 bash -c ": < /dev/tcp/${SERVER}/2049"
在上面的auto_share脚本中。确保脚本是可执行的。
接下来,配置脚本每X运行一次,在下面的示例中,这是每分钟一次。
Cron
# crontab -e
* * * * * /usr/local/bin/auto_share
systemd/Timers
/etc/systemd/system/auto_share.timer
[Unit] Description=Automount NFS shares every minute [Timer] OnCalendar=*-*-* *:*:00 [Install] WantedBy=timers.target
/etc/systemd/system/auto_share.service
[Unit] Description=Automount NFS shares After=network.target [Service] Type=oneshot ExecStart=/usr/local/bin/auto_share [Install] WantedBy=multi-user.target
使用NetworkManager调度器
NetworkManager也可以配置为在网络状态更改时运行脚本。
在网络状态更改时挂载共享的最简单方法是符号链接auto_share脚本。
# ln -s /usr/local/bin/auto_share /etc/NetworkManager/dispatcher.d/30-nfs.sh
但是,在这种特定情况下,只有在网络连接已被禁用后才会卸载,这不干净,并且可能导致KDE Plasma小程序的冻结等效果。
以下脚本在相关网络连接被禁用之前安全地卸载NFS共享,方法是监听down、pre-down和vpn-pre-down事件,确保脚本是可执行的。
/etc/NetworkManager/dispatcher.d/30-nfs.sh
#!/bin/sh
# Find the connection UUID with "nmcli con show" in terminal.
# All NetworkManager connection types are supported: wireless, VPN, wired...
WANTED_CON_UUID="CHANGE-ME-NOW-9c7eff15-010a-4b1c-a786-9b4efa218ba9"
if [ "$CONNECTION_UUID" = "$WANTED_CON_UUID" ]; then
# Script parameter $1: network interface name, not used
# Script parameter $2: dispatched event
case "$2" in
"up")
mount -a -t nfs4,nfs
;;
"down"|"pre-down"|"vpn-pre-down")
umount -l -a -t nfs4,nfs -f >/dev/null
;;
esac
fi
noauto选项的挂载,请移除此挂载选项或使用auto以允许调度器管理这些挂载。在 /etc/NetworkManager/dispatcher.d/pre-down 中创建一个符号链接以捕获 pre-down 事件。
# ln -s /etc/NetworkManager/dispatcher.d/30-nfs.sh /etc/NetworkManager/dispatcher.d/pre-down.d/30-nfs.sh
TLS加密
从Linux 6.5开始,可以使用xprtsec=tls挂载选项通过TLS加密NFS流量。首先,在客户端和服务器上安装ktls-utilsAUR包,并为每个包执行以下配置步骤。
服务器
创建私钥并获取包含服务器DNS名称的证书(有关更多详细信息,请参阅Transport Layer Security#获取证书)。这些文件不需要添加到系统的信任存储中。
编辑/etc/tlshd.conf以使用这些文件,使用您自己的x509.certificate和x509.private_key值。
/etc/tlshd.conf
[authenticate.server] x509.certificate= /etc/nfsd-certificate.pem x509.private_key= /etc/nfsd-private-key.pem
客户端
将上一步生成的服务器TLS证书添加到系统的信任存储(有关更多详细信息,请参阅Transport Layer Security#将证书添加到信任存储)。
现在您应该可以使用服务器的DNS名称挂载服务器。
# mount -o xprtsec=tls servername.domain:/ /mountpoint/on/client
检查客户端上的journalctl应该显示TLS握手已成功。
$ journalctl -b -u tlshd.service
Sep 28 11:14:46 client tlshd[227]: Built from ktls-utils 0.10 on Sep 26 2023 14:24:03 Sep 28 11:15:37 client tlshd[571]: Handshake with servername.domain (192.168.122.100) was successful
故障排除
有一个专门的文章NFS/故障排除。
参见
- 另请参阅Avahi,一个Zeroconf实现,允许自动发现NFS共享。
- HOWTO:无盘网络启动NFS根目录
- Microsoft Services for Unix NFS客户端信息
- Mac OS X Snow Leopard上的NFS
- http://chschneider.eu/linux/server/nfs.shtml
- 如何进行Linux NFS性能调优和优化
- Linux: 调优NFS性能
- 配置仅NFSv4的服务器