NFS 客户端

NFS 客户端

NFS 版本 2 协议最早在 RFC1094(1989 年 3 月)中进行了记录。此后,又发布了两个主要的 NFS 版本,其中 NFSv3 在 RFC1813(1995 年 6 月)中记录,而 NFSv4 在 RFC3530(2003 年 4 月)中记录。

Linux NFS 客户端当前支持所有上述发布的版本,并且正在努力添加对 NFSv4 协议的次要版本 1 的支持。

本文档的目的是提供有关 NFS 客户端的某些特殊功能的信息,这些功能可以由系统管理员配置。

nfs4_unique_id 参数

NFSv4 要求客户端使用唯一的字符串向服务器标识自己。在一个客户端和一个服务器之间共享的文件打开和锁定状态与此标识相关联。为了支持健壮的 NFSv4 状态恢复和透明状态迁移,此标识字符串在客户端重新启动时不得更改。

如果没有其他干预,Linux 客户端将使用包含本地系统节点名称的字符串。但是,系统管理员通常不会注意确保节点名称是完全限定的,并且在客户端系统的整个生命周期内不会更改。节点名称可能具有其他管理要求,这些要求需要特定的行为,而这些行为不能很好地作为 nfs_client_id4 字符串的一部分。

nfs.nfs4_unique_id 引导参数指定一个唯一的字符串,该字符串可以与系统的节点名称一起使用,当 NFS 客户端向服务器标识自己时。因此,如果系统的节点名称不是唯一的,则其 nfs.nfs4_unique_id 可以帮助防止与其他客户端发生冲突。

nfs.nfs4_unique_id 字符串通常是 UUID,尽管它可以包含任何被认为在所有 NFS 客户端中都是唯一的任何内容。在安装客户端系统时应选择 nfs4_unique_id 字符串,就像系统的根文件系统在安装时在其标签中获得新的 UUID 一样。

该字符串应在客户端的生命周期内保持固定。如果注意确保客户端干净地关闭并且所有未完成的 NFSv4 状态都已过期,则可以安全地更改它,以防止 NFSv4 状态丢失。

此字符串可以存储在 NFS 客户端的 grub.conf 中,也可以通过诸如 PXE 之类的网络引导工具提供。它也可以指定为 nfs.ko 模块参数。

除非通过写入 /sys/fs/nfs/net/nfs_client/identifier 的值来覆盖此唯一标识符字符串,否则在容器中运行的所有 NFS 客户端都将相同,该值将是写入进程的网络命名空间的本地值。

DNS 解析器

NFSv4 允许一个服务器通过特殊的 “fs_locations” 属性将 NFS 客户端引用到已迁移到另一台服务器的数据。请参阅 RFC3530 第 6 节:文件系统迁移和复制NFSv4 中引荐的实现指南

fs_locations 信息可以采用 IP 地址和路径的形式,也可以采用 DNS 主机名和路径的形式。后者要求 NFS 客户端执行 DNS 查找以挂载新卷,因此需要向上调用以允许用户空间提供此服务。

假设用户已在通常的 /var/lib/nfs/rpc_pipefs 中挂载了 ‘rpc_pipefs’ 文件系统,则向上调用包括以下步骤

  1. 该进程检查 dns_resolve 缓存以查看其是否包含有效条目。如果是,则返回该条目并退出。

  2. 如果不存在有效的条目,则运行帮助程序脚本 ‘/sbin/nfs_cache_getent’(可以使用 ‘nfs.cache_getent’ 内核引导参数更改),并带有两个参数:- 缓存名称 “dns_resolve” - 要解析的主机名

  3. 在查找相应的 IP 地址后,帮助程序脚本以以下(文本)格式将结果写入 rpc_pipefs 伪文件 ‘/var/lib/nfs/rpc_pipefs/cache/dns_resolve/channel’ 中

    “<IP 地址> <主机名> <ttl>n”

    其中 <IP 地址> 采用通常的 IPv4 (123.456.78.90) 或 IPv6 (ffee:ddcc:bbaa:9988:7766:5544:3322:1100, ffee::1100, ...) 格式。<主机名> 与帮助程序脚本的第二个参数相同,<ttl> 是此缓存条目的“生存时间”(以秒为单位)。

    注意

    如果 <IP 地址> 无效,例如字符串“0”,则会创建一个负条目,这将导致内核将主机名视为没有有效的 DNS 转换。

一个基本的示例 /sbin/nfs_cache_getent

#!/bin/bash
#
ttl=600
#
cut=/usr/bin/cut
getent=/usr/bin/getent
rpc_pipefs=/var/lib/nfs/rpc_pipefs
#
die()
{
    echo "Usage: $0 cache_name entry_name"
    exit 1
}

[ $# -lt 2 ] && die
cachename="$1"
cache_path=${rpc_pipefs}/cache/${cachename}/channel

case "${cachename}" in
    dns_resolve)
        name="$2"
        result="$(${getent} hosts ${name} | ${cut} -f1 -d\ )"
        [ -z "${result}" ] && result="0"
        ;;
    *)
        die
        ;;
esac
echo "${result} ${name} ${ttl}" >${cache_path}