Cloudera Engineering BlogにてCDH5.5にDistCpの高速化が実装されたと紹介されていたので、試してみました。
リンク先の説明を読んだ限り、2つのHDFS Snapshot間の差分情報を使って
- 削除・名前変更はDistCpを使わずに反映
- 新規作成・変更されたファイルだけをDistCpで同期
することで、高速化を図っているようです。
試した環境は、
- CentOS: 7.2 (64bit)
- CDH 5.7.0 (擬似分散)
です。
HDFSの準備
今回は、
- 同期元ディレクトリ:/user/hadoop/source
- 同期先ディレクトリ:/user/hadoop/target
という状況とします。
[hadoop@localhost ~]$ hdfs dfs -mkdir source
[hadoop@localhost ~]$ hdfs dfs -mkdir target
[hadoop@localhost ~]$ hdfs dfs -ls
Found 2 items
drwxr-xr-x - hadoop supergroup 0 2016-04-08 21:25 source
drwxr-xr-x - hadoop supergroup 0 2016-04-08 21:25 target
sourceディレクトリには適当なファイルを入れておきます。
[hadoop@localhost ~]$ echo foobar > Data.txt
[hadoop@localhost ~]$ hdfs dfs -put Data.txt source
[hadoop@localhost ~]$ hdfs dfs -ls source
Found 1 items
-rw-r--r-- 1 hadoop supergroup 7 2016-04-08 21:26 source/Data.txt
両方のディレクトリをスナップショット取得可能にしておきます。
hdfs dfsadmin
コマンドの-allowSnapshot
オプションで設定します。
[root@localhost ~]# sudo -u hdfs hdfs dfsadmin -allowSnapshot /user/hadoop/source
Allowing snaphot on /user/hadoop/source succeeded
[root@localhost ~]# sudo -u hdfs hdfs dfsadmin -allowSnapshot /user/hadoop/target
Allowing snaphot on /user/hadoop/target succeeded
スナップショット可能になっていることを確認します。
hdfs lsSnapshottableDir
コマンドで確認できます。
[hadoop@localhost ~]$ hdfs lsSnapshottableDir
drwxr-xr-x 0 hadoop supergroup 0 2016-04-08 21:26 0 65536 /user/hadoop/source
drwxr-xr-x 0 hadoop supergroup 0 2016-04-08 21:25 0 65536 /user/hadoop/target
HDFS SnapshotとDistCpによる差分更新の手順
同期元ディレクトリのスナップショットを取得
スナップショット名はs1とします。
取得にはhdfs dfs
コマンドの-createSnapshot
オプションを利用します。
[hadoop@localhost ~]$ hdfs dfs -createSnapshot source s1
Created snapshot /user/hadoop/source/.snapshot/s1
取得したスナップショットは対象ディレクトリの.snapshotディレクトリ下に論理的に配置されるためhdfs dfs -ls
で確認できます。
[hadoop@localhost ~]$ hdfs dfs -ls source/.snapshot
Found 1 items
drwxr-xr-x - hadoop supergroup 0 2016-04-08 21:29 source/.snapshot/s1
DistCpにてスナップショット状態の同期元ディレクトリを同期先にコピー
[hadoop@localhost ~]$ hadoop distcp -update source/.snapshot/s1 target
同期が取れたことを確認します。
[hadoop@localhost ~]$ hdfs dfs -ls target
Found 1 items
-rw-r--r-- 1 hadoop supergroup 7 2016-04-08 21:32 target/Data.txt
同期先ディレクトリのスナップショットを取得
distcpのベースにしたスナップショットと同名のs1で取得します。
[hadoop@localhost ~]$ hdfs dfs -createSnapshot target s1
Created snapshot /user/hadoop/target/.snapshot/s1
[hadoop@localhost ~]$ hdfs dfs -ls target/.snapshot
Found 1 items
drwxr-xr-x - hadoop supergroup 0 2016-04-08 21:33 target/.snapshot/s1
同期元ディレクトリの情報を更新
新しくファイルを追加します。
[hadoop@localhost ~]$ echo hogehoge > Data2.txt
[hadoop@localhost ~]$ hdfs dfs -put Data2.txt source
[hadoop@localhost ~]$ hdfs dfs -ls source
Found 2 items
-rw-r--r-- 1 hadoop supergroup 7 2016-04-08 21:26 source/Data.txt
-rw-r--r-- 1 hadoop supergroup 9 2016-04-08 21:34 source/Data2.txt
同期元ディレクトリのスナップショットを取得
今度はs2という名前にします。
[hadoop@localhost ~]$ hdfs dfs -createSnapshot source s2
Created snapshot /user/hadoop/source/.snapshot/s2
[hadoop@localhost ~]$ hdfs dfs -ls source/.snapshot
Found 2 items
drwxr-xr-x - hadoop supergroup 0 2016-04-08 21:29 source/.snapshot/s1
drwxr-xr-x - hadoop supergroup 0 2016-04-08 21:35 source/.snapshot/s2
スナップショット間の差分情報を確認しておきます。
[hadoop@localhost ~]$ hdfs snapshotDiff source s1 s2
Difference between snapshot s1 and snapshot s2 under directory /user/hadoop/source:
M .
+ ./Data2.txt
確かにData2.txtが追加(「+」で表現されます)されていますね。
DistCpで差分更新
sourceディレクトリの2つのスナップショットs1, s2の差分情報を使って、targetディレクトリにデータを差分更新します。
コマンドの引数は例によってHadoopのドキュメントではさっぱりわからないのですが、Clouderaのブロクの情報より
$ hadoop distcp -update -diff <snapshotName1> <snapshotName2> <sourceDir> <targetDir>
とのこと。
今回だと、以下のようになります。
[hadoop@localhost ~]$ hadoop distcp -update -diff s1 s2 /user/hadoop/source /user/hadoop/target
[hadoop@localhost ~]$ hdfs dfs -ls target
Found 2 items
-rw-r--r-- 1 hadoop supergroup 7 2016-04-08 21:32 target/Data.txt
-rw-r--r-- 1 hadoop supergroup 9 2016-04-08 23:08 target/Data2.txt
ハマりどころ
distcp -diff
の際に同期元と同期先のディレクトリのパスを相対パスで指定すると、以下のようなエラーになりました。
[hadoop@localhost ~]$ hadoop distcp -update -diff s1 s2 source target
16/04/08 21:37:14 INFO tools.DistCp: Input Options: DistCpOptions{atomicCommit=false, syncFolder=true, deleteMissing=false, ignoreFailures=false, maxMaps=20, sslConfigurationFile='null', copyStrategy='uniformsize', sourceFileListing=null, sourcePaths=[source], targetPath=target, targetPathExists=true, preserveRawXattrs=false, filtersFile='null'}
16/04/08 21:37:14 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
16/04/08 21:37:15 ERROR tools.DistCp: Exception encountered
java.lang.IllegalArgumentException: java.net.URISyntaxException: Relative path in absolute URI: hdfs://localhost:8020./source/.snapshot/s2
at org.apache.hadoop.fs.Path.initialize(Path.java:206)
at org.apache.hadoop.fs.Path.<init>(Path.java:197)
at org.apache.hadoop.tools.SimpleCopyListing.getPathWithSchemeAndAuthority(SimpleCopyListing.java:193)
at org.apache.hadoop.tools.SimpleCopyListing.addToFileListing(SimpleCopyListing.java:202)
at org.apache.hadoop.tools.SimpleCopyListing.doBuildListingWithSnapshotDiff(SimpleCopyListing.java:243)
at org.apache.hadoop.tools.SimpleCopyListing.doBuildListing(SimpleCopyListing.java:172)
at org.apache.hadoop.tools.CopyListing.buildListing(CopyListing.java:86)
at org.apache.hadoop.tools.DistCp.createInputFileListingWithDiff(DistCp.java:388)
at org.apache.hadoop.tools.DistCp.execute(DistCp.java:164)
at org.apache.hadoop.tools.DistCp.run(DistCp.java:123)
at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
at org.apache.hadoop.tools.DistCp.main(DistCp.java:436)
Caused by: java.net.URISyntaxException: Relative path in absolute URI: hdfs://localhost:8020./source/.snapshot/s2
at java.net.URI.checkPath(URI.java:1823)
at java.net.URI.<init>(URI.java:745)
at org.apache.hadoop.fs.Path.initialize(Path.java:203)
... 11 more
どうも同期元のパスとして絶対パスを期待しているようなので、ここは素直に従います。
なお、この事象はHDFS-10216に報告されていました。
distcp -diff relative path exception
https://issues.apache.org/jira/browse/HDFS-10216
同期先ディレクトリの状態をスナップショットで保存
差分更新した状態のスナップショットs2として取得しておきます。
[hadoop@localhost ~]$ hdfs dfs -createSnapshot target s2
Created snapshot /user/hadoop/target/.snapshot/s2
[hadoop@localhost ~]$ hdfs dfs -ls target/.snapshot
Found 2 items
drwxr-xr-x - hadoop supergroup 0 2016-04-08 22:29 target/.snapshot/s1
drwxr-xr-x - hadoop supergroup 0 2016-04-08 23:10 target/.snapshot/s2
後はこれを繰り返すだけ。
感想
DistCpの際に全データを対象としなくなるので、DistCpを定期的に実行する際にはかなり便利だと思われます。
ブロクに書かれているように、スナップショットの差分情報から更新すべき情報に変換するロジックはそれなりに大変そうです。HDFSはファイルのUPDATEができないという制約があるのがせめてもの救いかもしれません。
しかし、HDFS Snapshotのコマンドの統一性の無さはどうにかしてほしいわ・・・
以上!