View File System (ViewFs) は、複数の Hadoop ファイルシステム名前空間 (または名前空間ボリューム) を管理する方法を提供します。これは、HDFS フェデレーションで複数の NameNode を持つクラスタ、つまり複数の名前空間を持つクラスタに特に役立ちます。ViewFs は、一部の Unix/Linux システムにおけるクライアント側のマウントテーブルに類似しています。ViewFs を使用して、パーソナライズされた名前空間ビューを作成したり、クラスタごとの共通ビューを作成したりできます。
このガイドは、複数のクラスタを持つ Hadoop システムのコンテキストで提示されています。各クラスタは、複数の名前空間にフェデレートされている場合があります。また、フェデレーションされた HDFS で ViewFs を使用して、クラスタごとのグローバル名前空間を提供し、アプリケーションがフェデレーション前の世界と同様の方法で動作できるようにする方法についても説明します。
HDFS フェデレーション以前の旧来の世界では、クラスタには 1 つの NameNode があり、そのクラスタに対して 1 つのファイルシステム名前空間を提供していました。複数のクラスタがあると仮定します。各クラスタのファイルシステム名前空間は完全に独立しており、分離されています。さらに、物理ストレージはクラスタ間で共有されません (つまり、DataNode はクラスタ間で共有されません)。
各クラスタの core-site.xml
には、デフォルトのファイルシステムをそのクラスタの NameNode に設定する構成プロパティがあります。
<property> <name>fs.default.name</name> <value>hdfs://namenodeOfClusterX:port</value> </property>
このような構成プロパティにより、スラッシュ相対名を使用して、クラスタの NameNode を基準とするパスを解決できます。たとえば、上記の構成を使用すると、パス /foo/bar
は hdfs://namenodeOfClusterX:port/foo/bar
を参照します。
この構成プロパティは、クラスタ上の各ゲートウェイと、JobTracker や Oozie などのそのクラスタの主要なサービスにも設定されます。
したがって、core-site.xml
が上記のように設定されているクラスタ X では、典型的なパス名は次のようになります。
/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
クラスタ内では、(2) のような完全修飾 URI の代わりに、上記のタイプ (1) のパス名を使用することをお勧めします。完全修飾 URI はアドレスに似ており、アプリケーションがそのデータとともに移動することを許可しません。
複数のクラスタがあると仮定します。各クラスタには、1 つ以上の NameNode があります。各 NameNode には独自の名前空間があります。NameNode は、1 つのクラスタにのみ属します。同じクラスタ内の NameNode は、そのクラスタの物理ストレージを共有します。クラスタ間の名前空間は以前と同様に独立しています。
オペレーションは、ストレージのニーズに基づいて、クラスタ内の各 NameNode に何が格納されるかを決定します。たとえば、すべてのユーザーデータ (/user/<username>
) を 1 つの NameNode に、すべてのフィードデータ (/data
) を別の NameNode に、すべてのプロジェクト (/projects
) をさらに別の NameNode に配置する場合があります。
旧来の世界との透明性を提供するために、ViewFs ファイルシステム (つまり、クライアント側のマウントテーブル) を使用して、各クラスタに独立したクラスタ名前空間ビューを作成します。これは、旧来の世界の名前空間に似ています。Unix マウントテーブルのようなクライアント側のマウントテーブルであり、古い命名規則を使用して新しい名前空間ボリュームをマウントします。次の図は、4 つの名前空間ボリューム /user
、/data
、/projects
、および /tmp
をマウントするマウントテーブルを示しています。
ViewFs は、HDFS やローカルファイルシステムと同様に、Hadoop ファイルシステムインターフェイスを実装します。他のファイルシステムへのリンクのみを許可するという意味で、これは単純なファイルシステムです。ViewFs は Hadoop ファイルシステムインターフェイスを実装しているため、Hadoop ツールを透過的に操作できます。たとえば、すべてのシェルコマンドは、HDFS やローカルファイルシステムと同様に ViewFs で動作します。
各クラスタの設定では、デフォルトのファイルシステムが、以下に示すように、そのクラスタのマウントテーブルに設定されます (シングル NameNode クラスタの設定と比較してください)。
<property> <name>fs.defaultFS</name> <value>viewfs://clusterX</value> </property>
URI の viewfs://
スキームに続くオーソリティは、マウントテーブル名です。クラスタのマウントテーブルは、クラスタ名で命名することをお勧めします。その後、Hadoop システムは 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>
したがって、core-site.xml
がそのクラスターのマウントテーブルを使用するようにデフォルトのfsを設定しているクラスターXでは、典型的なパス名は次のようになります。
/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
クラスター内では、(2)のような完全修飾URIの代わりに、上記の(1)のようなタイプのパス名を使用することをお勧めします。さらに、アプリケーションはマウントポイントの知識を使用するべきではなく、特定のネームノード内のファイルを参照するために、hdfs://namenodeContainingUserDirs:port/joe/foo/bar
のようなパスを使用するべきではありません。代わりに /user/joe/foo/bar
を使用する必要があります。
古い環境では、ネームノードまたはクラスターを跨いでファイルまたはディレクトリの名前を変更することはできませんでした。これは新しい環境でも同様ですが、さらに別の点が追加されています。たとえば、古い環境では、以下のコマンドを実行できます。
rename /user/joe/myStuff /data/foo/bar
/user
と /data
が実際にはクラスター内の異なるネームノードに保存されている場合、新しい環境ではこれは機能しません。
HDFSおよびその他の分散ファイルシステムは、ブロックレプリケーションやより高度な分散エンコーディングなどの何らかの冗長性によってデータ回復力を提供します。ただし、最新の設定は、複数のHadoopクラスター、エンタープライズファイラー、オンプレミスおよびオフプレミスでホストされる場合があります。Nflyマウントポイントを使用すると、1つの論理ファイルを複数のファイルシステムによって同期的にレプリケートすることが可能になります。これは、最大1ギガバイトの比較的小さなファイル向けに設計されています。一般に、ロジックがFsShellやMapReduceタスクなどのViewFsを使用する単一のクライアントJVMに存在するため、単一のコア/単一のネットワークリンクのパフォーマンスの関数です。
Nflyの基本構成を理解するために、次の例を検討してください。ディレクトリ ads
を、URI: uri1
、uri2
、uri3
で表される3つのファイルシステムに複製するとします。
<property> <name>fs.viewfs.mounttable.global.linkNfly../ads</name> <value>uri1,uri2,uri3</value> </property>
プロパティ名に2つの連続する ..
があることに注意してください。これは、マウントポイントの高度な調整のための空の設定が原因で発生します。これについては、以降のセクションで説明します。プロパティ値は、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シェルを介してファイルを作成する場合
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
は、例外なしに書き込み変更を処理する必要がある宛先の最小数を決定します。この数が、ターゲットURIの数よりも小さい場合、Nflyの書き込みは失敗します。minReplication
をターゲットURIの数よりも大きくすることは構成エラーです。デフォルトは2です。
minReplication
がターゲットURIの数よりも小さい場合、一部のターゲットURIに最新の書き込みがない可能性があります。これは、次の設定によって制御される、よりコストのかかる読み取り操作を使用することで補正できます。
readMostRecent=boolean
を true
に設定すると、Nflyクライアントは、トポロジー順序に基づく最初のファイルシステムだけでなく、すべてのターゲットURIの下にあるパスを確認します。現時点で利用可能なすべてのファイルシステムの中から、最も最近の変更時間を持つものが処理されます。
repairOnRead=boolean
を true
に設定すると、Nflyは最新のレプリカを古いターゲットにコピーして、後続の読み取りを最も近いレプリカから再び安価に実行できるようにします。
Nflyは、「最も近い」ターゲットURIからの読み取りを満たすように努めます。
このために、Nflyは、ターゲット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(つまり、hdfs://datacenter-east/ads/_nfly_tmp_f1
、hdfs://datacenter-west/ads/_nfly_tmp_f1
など)の下に、不可視ファイル _nfly_tmp_f1
を作成します。これは、基盤となるファイルシステムで create
を呼び出すことによって行われ、4つの出力ストリームすべてをラップする FSDataOutputStream
オブジェクト out
を返します。
したがって、out
でのその後の書き込みは、各ラップされたストリームに転送できます。
out.close
で、すべてのストリームが閉じられ、ファイルの名前が _nfly_tmp_f1
から f1
に変更されます。すべてのファイルは、この手順の開始時点でのクライアントシステム時間に対応する同じ変更時間を受け取ります。
少なくとも minReplication
個の宛先が失敗なしに手順1〜3を通過した場合、Nflyはトランザクションが論理的にコミットされたと見なします。それ以外の場合は、可能な限り一時ファイルのクリーンアップを試みます。
4はベストエフォートの手順であり、クライアントJVMがクラッシュして作業を再開しない可能性があるため、そのような _nfly_tmp
ファイルをパージするために何らかのcronジョブをプロビジョニングすることをお勧めします。
非フェデレーション環境からフェデレーション環境に移行する際に、異なるボリュームのネームノードを追跡する必要がありますか?
いいえ、必要ありません。上記の例を参照してください。相対名を使用し、デフォルトのファイルシステムを利用するか、パスを hdfs://namenodeCLusterX/foo/bar
から viewfs://clusterX/foo/bar
に変更するかのいずれかです。
オペレーションがクラスター内の1つのネームノードから別のネームノードにファイルを移動するとどうなりますか?
オペレーションは、ストレージ容量の問題に対処するために、ファイルをあるネームノードから別のネームノードに移動する場合があります。アプリケーションが中断しないように、これを実行します。いくつかの例を見てみましょう。
例1:/user
と /data
が1つのネームノードにあり、後で容量の問題に対処するために別のネームノードに配置する必要がある場合。実際、オペレーションは /user
と /data
用の別々のマウントポイントを作成したでしょう。変更前は、/user
と /data
のマウントは同じネームノード(たとえば、namenodeContainingUserAndData
)を指していました。オペレーションは、マウントポイントがそれぞれ namenodeContaingUser
と namenodeContainingData
に変更されるように、マウントテーブルを更新します。
例2:すべてのプロジェクトが1つのネームノードに収まっていましたが、後で2つ以上のネームノードが必要になった場合。ViewFsを使用すると、/project/foo
や /project/bar
のようなマウントが可能です。これにより、マウントテーブルを更新して、対応するネームノードを指すようにすることができます。
マウントテーブルは、各 core-site.xml
にあるのか、それとも独自の別のファイルにあるのか?
計画では、マウントテーブルを別のファイルに保持し、core-site.xml
が xinclude するようにします。これらのファイルを各マシンにローカルに保持することもできますが、集中ロケーションからHTTPを使用してアクセスする方が適切です。
構成には、1つのクラスターのみのマウントテーブル定義を含める必要がありますか、それともすべてのクラスターのマウントテーブル定義を含める必要がありますか?
distcpのように、他のクラスターのデータにアクセスする必要があるため、構成にはすべてのクラスターのマウント定義を含める必要があります。
オペレーションが時間の経過とともにマウントテーブルを変更する可能性がある場合、マウントテーブルは実際にいつ読み込まれますか?
マウントテーブルは、ジョブがクラスターに送信されるときに読み込まれます。core-site.xml
の XInclude
は、ジョブの送信時に展開されます。これは、マウントテーブルが変更された場合、ジョブを再送信する必要があることを意味します。このため、マウントテーブルを変更する必要性を大幅に削減するマージマウントを実装したいと考えています。さらに、将来的にはジョブ開始時に初期化される別のメカニズムを介してマウントテーブルを読み込みたいと考えています。
JobTracker(またはYarnのリソースマネージャー)自体がViewFsを使用しますか?
いいえ、必要ありません。NodeManagerも同様です。
ViewFsはトップレベルでのマウントのみを許可しますか?
いいえ、より一般的です。たとえば、/user/joe
と /user/jane
をマウントできます。この場合、マウントテーブルに /user
用の内部読み取り専用ディレクトリが作成されます。/user
に対するすべての操作は有効ですが、/user
は読み取り専用です。
アプリケーションがクラスターを跨いで動作し、ファイルパスを永続的に保存する必要がある場合、どのパスを保存する必要がありますか?
アプリケーションの実行時に使用するのと同じように、viewfs://cluster/path
型のパス名を保存する必要があります。これにより、操作が透過的な方法で移動を行う限り、クラスタ内のネームノード内でのデータの移動から保護されます。ただし、データがクラスタ間を移動する場合は保護されません。古い(フェデレーション以前の)環境では、いずれにしてもクラスタ間でのこのようなデータの移動から保護されていませんでした。
委任トークンはどうでしょうか?
ジョブを送信するクラスタ(そのクラスタのマウントテーブルのすべてのマウントされたボリュームを含む)と、マップリデュースジョブの入力および出力パス(指定された入力および出力パスのマウントテーブルを介してマウントされたすべてのボリュームを含む)の委任トークンはすべて自動的に処理されます。さらに、特別な状況のために、ベースクラスタ構成に追加の委任トークンを追加する方法もあります。
View File System Overload Scheme Guideを参照してください。
Viewファイルシステムのマウントポイントは、キーと値に基づいたマッピングシステムでした。これは、マッピング構成をルールに抽象化できるユースケースには適していません。例えば、ユーザーはユーザーごとにGCSバケットを提供したいと考えており、合計で数千人のユーザーがいる可能性があります。従来のキーと値に基づくアプローチは、いくつかの理由からうまく機能しません。
マウントテーブルはFileSystemクライアントによって使用されます。すべてのクライアントに構成を配布するコストがかかるため、可能であれば避ける必要があります。View File System Overload Scheme Guideは、中央マウントテーブル管理によって配布を支援できます。ただし、マウントテーブルは変更のたびに更新する必要があります。ルールベースのマウントテーブルを提供すれば、変更を大幅に回避できます。
クライアントは、マウントテーブル内のすべてのKVを理解する必要があります。マウント可能項目が数千に増えると、これは理想的ではありません。例えば、ユーザーが1つだけを必要とする場合でも、数千のファイルシステムが初期化される可能性があります。また、構成自体が大規模になると肥大化します。
キーと値に基づくマウントテーブルでは、viewファイルシステムはすべてのマウントポイントをパーティションとして扱います。すべてのパーティションで操作を実行するファイルシステムAPIがいくつかあります。例えば、複数のマウントを持つHDFSクラスタがあります。ユーザーは、ローカルディスクからHDFSクラスタにデータをコピーするために、「hadoop fs -put file viewfs://hdfs.namenode.apache.org/tmp/」コマンドを実行したいと考えています。このコマンドは、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}
インターセプターは、解決プロセスでソースまたはターゲットを変更するために導入されたメカニズムの1つです。これはオプションであり、特定の文字を置き換えたり、単語を置き換えたりするなど、ユーザーのユースケースを満たすために使用できます。インターセプターは、正規表現マウントポイントでのみ機能します。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
)を参照することをお勧めします。mountTable.xml
を参照するために、次の構成要素をcore-site.xml
に追加します。
<configuration xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="mountTable.xml" /> </configuration>
ファイルmountTable.xml
には、3つのネームノードによって管理される3つのネームスペースボリュームのフェデレーションである仮想クラスタ「ClusterX」のマウントテーブルの定義があります。
ここで、/home
と/tmp
はネームノードnn1-clusterx.example.com:8020によって管理されるネームスペースにあり、プロジェクト/foo
と/bar
はフェデレーションされたクラスタの他のネームノードでホストされています。ホームディレクトリのベースパスは/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>