视图文件系统(ViewFs)提供了一种管理多个Hadoop文件系统命名空间(或命名空间卷)的方式。对于在HDFS联邦中拥有多个名称节点(因此具有多个命名空间)的集群特别有用。ViewFs类似于某些Unix/Linux系统中的客户端挂载表。ViewFs可用于创建个性化的命名空间视图,也可用于创建每个集群的公共视图。
本指南针对具有多个集群的Hadoop系统环境编写,每个集群可能被联合为多个命名空间。它还描述了如何在联合HDFS中使用ViewFs来为每个集群提供全局命名空间,使应用程序能够以类似于联合前世界的方式运行。
在HDFS Federation出现之前的旧世界中,一个集群只有一个namenode,为该集群提供单一的文件系统命名空间。假设存在多个集群,每个集群的文件系统命名空间完全独立且互不相交。此外,物理存储不会在集群之间共享(即Datanodes不会跨集群共享)。
每个集群的core-site.xml
都有一个配置属性,用于将默认文件系统设置为该集群的namenode:
<property> <name>fs.default.name</name> <value>hdfs://namenodeOfClusterX:port</value> </property>
这样的配置属性允许使用斜杠相对名称来解析相对于集群名称节点的路径。例如,路径/foo/bar
通过上述配置指向hdfs://namenodeOfClusterX:port/foo/bar
。
此配置属性在集群的每个网关上设置,也在该集群的关键服务(如JobTracker和Oozie)上设置。
因此在集群X上,当core-site.xml
按上述设置时,典型的路径名是
/foo/bar
hdfs://namenodeOfClusterX:port/foo/bar
。hdfs://namenodeOfClusterX:port/foo/bar
/foo/bar
,因为这允许在需要时将应用程序及其数据透明地移动到另一个集群。hdfs://namenodeOfClusterY:port/foo/bar
distcp hdfs://namenodeClusterY:port/pathSrc hdfs://namenodeClusterZ:port/pathDest
webhdfs://namenodeClusterX:http_port/foo/bar
http://namenodeClusterX:http_port/webhdfs/v1/foo/bar
和 http://proxyClusterX:http_port/foo/bar
在集群内部时,建议使用上述类型(1)的路径名,而非类似(2)的完整限定URI。完整限定URI类似于地址,会限制应用程序随数据迁移的能力。
假设存在多个集群。每个集群拥有一个或多个namenode。每个namenode管理自己的命名空间。一个namenode仅属于一个集群。同一集群内的namenode共享该集群的物理存储。不同集群间的命名空间保持相互独立。
运维人员根据存储需求决定集群中每个namenode上存储的内容。例如,他们可能将所有用户数据(/user/
)放在一个namenode中,将所有feed数据(/data
)放在另一个namenode中,将所有项目(/projects
)放在另一个namenode中,等等。
为了与旧世界保持透明性,ViewFs文件系统(即客户端挂载表)被用于为每个集群创建独立的集群命名空间视图,这类似于旧世界中的命名空间。客户端挂载表类似于Unix挂载表,它们使用旧的命名约定来挂载新的命名空间卷。下图展示了一个挂载表挂载四个命名空间卷/user
、/data
、/projects
和/tmp
:
ViewFs实现了Hadoop文件系统接口,类似于HDFS和本地文件系统。从某种意义上说,它是一个简单的文件系统,因为它只允许链接到其他文件系统。由于ViewFs实现了Hadoop文件系统接口,它可以透明地与Hadoop工具协同工作。例如,所有shell命令都可以像操作HDFS和本地文件系统一样操作ViewFs。
在每个集群的配置中,默认文件系统被设置为该集群的挂载表,如下所示(请与单Namenode集群中的配置进行比较)。
<property> <name>fs.defaultFS</name> <value>viewfs://clusterX</value> </property>
URI中viewfs://
协议后的权限部分表示挂载表名称。建议集群的挂载表应以集群名称命名。随后Hadoop系统会在配置文件中查找名为"clusterX"的挂载表。运维人员需确保所有网关和服务机器都包含所有集群的挂载表配置,对于每个集群,其默认文件系统需按上述方式设置为该集群对应的ViewFs挂载表。
挂载表的挂载点是在标准的Hadoop配置文件中指定的。所有viewfs
的挂载表配置项都以fs.viewfs.mounttable.
为前缀。使用link
标签来指定链接到其他文件系统的挂载点。建议将挂载点名称设置为与链接的文件系统目标位置相同。对于挂载表中未配置的所有命名空间,可以通过linkFallback
将它们回退到默认文件系统。
在下面的挂载表配置中,命名空间/data
被链接到文件系统hdfs://nn1-clusterx.example.com:8020/data
,/project
被链接到文件系统hdfs://nn2-clusterx.example.com:8020/project
。所有未在挂载表中配置的命名空间,例如/logs
,将被链接到文件系统hdfs://nn5-clusterx.example.com:8020/home
。
<configuration> <property> <name>fs.viewfs.mounttable.clusterX.link./data</name> <value>hdfs://nn1-clusterx.example.com:8020/data</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./project</name> <value>hdfs://nn2-clusterx.example.com:8020/project</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./user</name> <value>hdfs://nn3-clusterx.example.com:8020/user</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./tmp</name> <value>hdfs://nn4-clusterx.example.com:8020/tmp</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.linkFallback</name> <value>hdfs://nn5-clusterx.example.com:8020/home</value> </property> </configuration>
或者,我们可以通过linkMergeSlash
将挂载表的根目录与另一个文件系统的根目录合并。在下面的挂载表配置中,clusterY的根目录与位于hdfs://nn1-clustery.example.com:8020
的根文件系统进行了合并。
<configuration> <property> <name>fs.viewfs.mounttable.clusterY.linkMergeSlash</name> <value>hdfs://nn1-clustery.example.com:8020/</value> </property> </configuration>
因此在集群X上,core-site.xml
被设置为使用该集群挂载表作为默认文件系统时,典型的路径名是
/foo/bar
viewfs://clusterX/foo/bar
。如果在旧的未联邦化环境中使用此类路径名,那么向联邦环境的过渡将是透明的。viewfs://clusterX/foo/bar
/foo/bar
,因为它允许在需要时将应用程序及其数据透明地迁移到另一个集群。viewfs://clusterY/foo/bar
distcp viewfs://clusterY/pathSrc viewfs://clusterZ/pathDest
viewfs://clusterX-webhdfs/foo/bar
http://namenodeClusterX:http_port/webhdfs/v1/foo/bar
和 http://proxyClusterX:http_port/foo/bar
在集群内部时,建议使用上述类型(1)的路径名,而非类似(2)的完整限定URI。此外,应用程序不应依赖挂载点信息,也不应使用类似hdfs://namenodeContainingUserDirs:port/joe/foo/bar
的路径来引用特定名称节点中的文件,而应使用/user/joe/foo/bar
路径。
请注意,在旧版本中无法跨名称节点或集群重命名文件或目录。在新版本中同样如此,但增加了一个限制。例如,在旧版本中可以执行以下命令。
rename /user/joe/myStuff /data/foo/bar
如果/user
和/data
实际上存储在集群内的不同名称节点上,这在新环境中将无法工作。
HDFS和其他分布式文件系统通过某种冗余机制(如块复制或更复杂的分布式编码)提供数据弹性。然而,现代架构可能由多个Hadoop集群、企业级存储设备组成,部署在本地和云端。Nfly挂载点使得单个逻辑文件能够被多个文件系统同步复制。该设计适用于相对较小的文件(最大1GB)。由于相关逻辑运行在使用ViewFs(如FsShell或MapReduce任务)的单个客户端JVM中,其性能通常受限于单核CPU/单网络链路。
通过以下示例来理解Nfly的基本配置。假设我们希望保持目录ads
在由URI表示的三个文件系统上复制:uri1
、uri2
和uri3
。
<property> <name>fs.viewfs.mounttable.global.linkNfly../ads</name> <value>uri1,uri2,uri3</value> </property>
注意属性名中连续的两个..
。它们之所以出现是因为挂载点高级调优的空设置,我们将在后续章节展示。属性值是以逗号分隔的URI列表。
URI可能指向不同区域的不同集群 hdfs://datacenter-east/ads
, s3a://models-us-west/ads
, hdfs://datacenter-west/ads
或者在最简单的情况下指向同一文件系统下的不同目录,例如 file:/tmp/ads1
, file:/tmp/ads2
, file:/tmp/ads3
在全局路径viewfs://global/ads
下执行的所有修改操作,如果底层系统可用,将会传播到所有目标URI。
例如,如果我们通过hadoop shell创建一个文件
hadoop fs -touchz viewfs://global/ads/z1
我们将在后续配置中通过本地文件系统找到它
ls -al /tmp/ads*/z1 -rw-r--r-- 1 user wheel 0 Mar 11 12:17 /tmp/ads1/z1 -rw-r--r-- 1 user wheel 0 Mar 11 12:17 /tmp/ads2/z1 -rw-r--r-- 1 user wheel 0 Mar 11 12:17 /tmp/ads3/z1
从全局路径读取数据时,将由第一个不引发异常的文件系统处理。文件系统的访问顺序取决于它们当前是否可用以及是否存在拓扑顺序。
挂载点 linkNfly
可以通过传递以逗号分隔的键值对参数列表进行进一步配置。以下是当前支持的参数。
minReplication=int
用于确定在不引发异常的情况下必须处理写入操作的最小目标数量,如果低于该值则nfly写入将失败。若minReplication值高于目标URI数量则属于配置错误。默认值为2。
如果minReplication低于目标URI的数量,我们可能会有一些目标URI没有最新的写入。可以通过采用以下设置控制的更昂贵的读取操作来补偿
readMostRecent=boolean
如果设置为true
,将导致Nfly客户端检查所有目标URI下的路径,而不仅仅是基于拓扑顺序的第一个路径。在目前所有可用的路径中,将处理修改时间最新的那个。
repairOnRead=boolean
如果设置为true
,将导致Nfly将最新的副本复制到过时的目标,这样后续读取可以再次从最近的副本廉价地完成。
Nfly 旨在从"最近"的目标 URI 满足读取请求。
为此,Nfly将Rack Awareness的概念扩展到了目标URI的权限范围。
Nfly应用NetworkTopology来解析URI的权限。在异构设置中最常用的是基于脚本的映射。我们可以使用一个提供以下拓扑映射的脚本
URI | 拓扑结构 |
---|---|
hdfs://datacenter-east/ads |
/us-east/onpremise-hdfs |
s3a://models-us-west/ads |
/us-west/aws |
hdfs://datacenter-west/ads |
/us-west/onpremise-hdfs |
如果目标URI没有权限部分,例如file:/
,Nfly会注入客户端的本地节点名称。
<property> <name>fs.viewfs.mounttable.global.linkNfly.minReplication=3,readMostRecent=true,repairOnRead=false./ads</name> <value>hdfs://datacenter-east/ads,hdfs://datacenter-west/ads,s3a://models-us-west/ads,file:/tmp/ads</value> </property>
FileSystem fs = FileSystem.get("viewfs://global/", ...); FSDataOutputStream out = fs.create("viewfs://global/ads/f1"); out.write(...); out.close();
上述代码将导致以下执行结果。
在每个目标URI下创建一个不可见文件_nfly_tmp_f1
,例如hdfs://datacenter-east/ads/_nfly_tmp_f1
、hdfs://datacenter-west/ads/_nfly_tmp_f1
等。这是通过调用底层文件系统的create
方法实现的,并返回一个包装了所有四个输出流的FSDataOutputStream
对象out
。
因此,对out
的每次后续写入都可以转发到每个包装的流。
在out.close
时,所有流都会被关闭,文件会从_nfly_tmp_f1
重命名为f1
。所有文件都会获得相同的修改时间,该时间对应客户端系统在此步骤开始的时间。
如果至少有minReplication
个目标节点成功完成步骤1-3且没有失败,Nfly会认为该事务在逻辑上已提交;否则它会尽力尝试清理临时文件。
请注意,由于步骤4是基于最大努力原则执行的,客户端JVM可能会崩溃且无法恢复工作,因此建议配置某种定时任务来清理这些_nfly_tmp
文件。
当我从非联邦环境迁移到联邦环境时,需要跟踪不同卷的namenode,该如何操作?
不需要。参考上面的示例——您要么使用相对路径并利用默认文件系统,要么将路径从hdfs://namenodeCLusterX/foo/bar
改为viewfs://clusterX/foo/bar
。
如果运维人员将一些文件从集群中的一个namenode移动到另一个namenode,会发生什么情况?
操作可能会将文件从一个namenode移动到另一个namenode,以解决存储容量问题。它们会以避免应用程序中断的方式进行此操作。让我们来看一些例子。
示例1:/user
和/data
最初位于同一个namenode上,后来由于容量问题需要分别放在不同的namenode上。实际上,运维人员会为/user
和/data
创建独立的挂载点。在变更前,/user
和/data
的挂载点都指向同一个namenode,比如namenodeContainingUserAndData
。运维人员将更新挂载表,使挂载点分别改为namenodeContaingUser
和namenodeContainingData
。
示例2:所有项目最初都部署在一个namenode上,但后来需要两个或更多namenode。ViewFs允许挂载类似/project/foo
和/project/bar
的路径。这使得可以更新挂载表以指向相应的namenode。
每个挂载表是在 core-site.xml
中还是单独存放在自己的文件中?
计划是将挂载表保存在单独的文件中,并通过xincluding方式包含到core-site.xml
中。虽然可以在每台机器上本地保存这些文件,但最好使用HTTP从中央位置访问它们。
配置中应该只包含一个集群的挂载表定义,还是所有集群的挂载表定义?
配置应包含所有集群的挂载定义,因为需要能够访问其他集群中的数据,例如使用distcp时。
考虑到操作可能会随时间改变挂载表,挂载表实际在何时被读取?
挂载表在作业提交到集群时被读取。core-site.xml
中的XInclude
会在作业提交时展开。这意味着如果挂载表发生变更,则需要重新提交作业。基于这个原因,我们希望实现merge-mount功能,这将大幅减少修改挂载表的需求。此外,我们计划未来通过另一种在作业启动时初始化的机制来读取挂载表。
JobTracker(或Yarn的Resource Manager)本身会使用ViewFs吗?
不会,它不需要。NodeManager也不需要。
ViewFs是否只允许在顶层挂载?
不是;它更为通用。例如,可以挂载/user/joe
和/user/jane
。在这种情况下,挂载表中会为/user
创建一个内部只读目录。对/user
的所有操作都是有效的,只是/user
是只读的。
一个应用程序需要跨集群工作并持久化存储文件路径。它应该存储哪些路径?
你应该存储viewfs://cluster/path
类型的路径名称,与运行应用程序时使用的路径相同。只要操作以透明方式移动数据,这可以使你免受集群内namenode之间数据移动的影响。但如果数据从一个集群移动到另一个集群,这并不能提供保护;无论如何,在旧版(联邦前)环境中也无法保护你免受这种跨集群数据移动的影响。
关于委托令牌(Delegation tokens)的处理方式?
系统会自动处理以下委托令牌:您提交作业的目标集群(包括该集群挂载表中的所有挂载卷),以及MapReduce作业的输入输出路径(包括通过挂载表为指定输入输出路径挂载的所有卷)。此外,还提供了一种方法可以在特殊情况下向基础集群配置添加额外的委托令牌。
请参考查看文件系统过载方案指南
The view file system mount points were a Key-Value based mapping system. It is not friendly for user cases which mapping config could be abstracted to rules. E.g. Users want to provide a GCS bucket per user and there might be thousands of users in total. The old key-value based approach won’t work well for several reasons:
挂载表由文件系统客户端使用。将配置分发到所有客户端存在成本,应尽可能避免。View File System Overload Scheme Guide可通过集中式挂载表管理来帮助分发。但每次变更仍需更新挂载表。如果提供基于规则的挂载表,则可大幅减少变更需求。
客户端必须理解挂载表中的所有键值对。当挂载项增长到数千个条目时,这种设计并不理想。例如即使用户只需要访问一个文件系统,也可能初始化数千个文件系统。而且配置本身在大规模使用时将变得臃肿。
在基于键值的挂载表中,视图文件系统将每个挂载点视为一个分区。存在多个文件系统API会导致对所有分区的操作。例如,有一个包含多个挂载点的HDFS集群。用户希望运行"hadoop fs -put file viewfs://hdfs.namenode.apache.org/tmp/"命令将数据从本地磁盘复制到我们的HDFS集群。该命令将触发ViewFileSystem调用setVerifyChecksum()方法,该方法会为每个挂载点初始化文件系统。对于基于正则表达式规则的挂载表条目,在解析之前我们无法知道对应的路径是什么。因此在这种情况下会忽略基于正则表达式的挂载表条目。文件系统(ChRootedFileSystem)将在访问时创建。但底层文件系统会被ViewFileSystem的内部缓存所缓存。
<property> <name>fs.viewfs.rename.strategy</name> <value>SAME_FILESYSTEM_ACROSS_MOUNTPOINT</value> </property>
这是一个基础正则表达式挂载点配置的示例。${username} 是 Java 正则表达式中的命名捕获组。
<property> <name>fs.viewfs.mounttable.hadoop-nn.linkRegx./^(?<username>\\w+)</name> <value>gs://${username}.hadoop.apache.org/</value> </property>
解析示例。
viewfs://hadoop-nn/user1/dir1 => gs://user1.hadoop.apache.org/dir1 viewfs://hadoop-nn/user2 => gs://user2.hadoop.apache.org/
src/key的格式为
fs.viewfs.mounttable.${VIEWNAME}.linkRegx.${REGEX_STR}
Interceptor(拦截器)是一种在解析过程中用于修改源路径或目标路径的机制。它是可选的,可用于满足诸如替换特定字符或某些单词等用户场景。拦截器仅对正则表达式挂载点生效。目前RegexMountPointResolvedDstPathReplaceInterceptor是唯一内置的拦截器。
这是一个设置了RegexMountPointResolvedDstPathReplaceInterceptor的正则表达式挂载点条目示例。
<property> <name>fs.viewfs.mounttable.hadoop-nn.linkRegx.replaceresolveddstpath:_:-#./^(?<username>\\w+)</name> <value>gs://${username}.hadoop.apache.org/</value> </property>
replaceresolveddstpath:_:-
是一个拦截器设置。"replaceresolveddstpath"表示拦截器类型,"_"表示待替换的字符串,"-"表示替换后的字符串。
解析示例。
viewfs://hadoop-nn/user_ad/dir1 => gs://user-ad.hadoop.apache.org/dir1 viewfs://hadoop-nn/user_ad_click => gs://user-ad-click.hadoop.apache.org/
src/key的格式为
fs.viewfs.mounttable.${VIEWNAME}.linkRegx.${REGEX_STR} fs.viewfs.mounttable.${VIEWNAME}.linkRegx.${interceptorSettings}#.${srcRegex}
通常,用户无需定义挂载表或修改core-site.xml
即可使用挂载表。这些操作由运维团队完成,正确的配置会像当前处理core-site.xml
一样设置在适当的网关机器上。
挂载表可以在core-site.xml
中定义,但最好在core-site.xml
中使用间接引用来引用单独的配置文件,例如mountTable.xml
。将以下配置元素添加到core-site.xml
中以引用mountTable.xml
:
<configuration xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="mountTable.xml" /> </configuration>
在文件mountTable.xml
中,定义了一个名为"clusterX"的挂载表,用于假设由三个名称节点管理的三个命名空间卷组成的联邦集群
这里 /home
和 /tmp
位于由namenode nn1-clusterx.example.com:8020管理的命名空间中,而项目 /foo
和 /bar
则托管在联邦集群的其他namenode上。主目录基础路径设置为 /home
,以便每个用户都可以使用FileSystem/FileContext中定义的getHomeDirectory()方法访问其主目录。
<configuration> <property> <name>fs.viewfs.mounttable.clusterX.homedir</name> <value>/home</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./home</name> <value>hdfs://nn1-clusterx.example.com:8020/home</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./tmp</name> <value>hdfs://nn1-clusterx.example.com:8020/tmp</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./projects/foo</name> <value>hdfs://nn2-clusterx.example.com:8020/projects/foo</value> </property> <property> <name>fs.viewfs.mounttable.clusterX.link./projects/bar</name> <value>hdfs://nn3-clusterx.example.com:8020/projects/bar</value> </property> </configuration>