SlideShare a Scribd company logo
© 2019 NTT DATA Corporation 1 © 2019 NTT DATA Corporation
NTTデータ テクノロジーカンファレンス 2019
データインターフェースとしてのHadoop
2019年9月5日
株式会社NTTデータ 岩崎正剛
© 2019 NTT DATA Corporation 2
クラウドサービスの発展により、大量のデータを格納して処理す
るための選択肢は多くなりました。
Hadoopは、分散ファイルシステム(HDFS)と各種クラウドスト
レージ等を透過的に併用して、データ処理を行うための機能を提
供しており、Sparkに代表される分散処理フレームワークのユー
ザは、あまり意識せずにこれを活用しています。
本セッションでは、Hadoopが提供するデータアクセスのための
インターフェースやモジュールに焦点をあて、データストアの併
用や使い分けの勘所などを解説します。
概要
© 2019 NTT DATA Corporation 3
Hadoopのファイルシステム
© 2019 NTT DATA Corporation 4
Hadoop Distributed File System
Hadoop = 分散FS + 分散処理FW
Hadoopアプリケーション =
HDFS上のデータを処理するもの
HDFS
https://hadoop.apache.org/docs/r3.2.0/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
© 2019 NTT DATA Corporation 5
core-site.xmlに書く基本的な設定項目
HDFSのアクセス先を書く
ネームサービス名
またはNameNodeのホスト名:port
hdfs://以外もある?
defaultではないFSもある?
fs.defaultFS
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster/</value>
</property>
© 2019 NTT DATA Corporation 6
ファイルのpathをURI形式で指定
schemeに応じてよしなにデータを読み書き
データストア間のデータコピー/移動にも便利
distcpなどでも同じ要領
schemeなしのただのpathだとfs.defaultFS
Hadoopのデータストア抽象化
$ hadoop fs -cp file:///a/b hdfs://ns1/a/
$ hadoop fs -cp hdfs://ns1/a/b hdfs://nn1:8020/a/
$ hadoop fs -cp webhdfs://nn1:9870/a/b s3a://bc/a/
© 2019 NTT DATA Corporation 7
Hadoop Compatible File Systems
Hadoop FileSystem API
Hadoop
Application
HDFS Local FS Amazon S3
Azure
Data Lake
Storage gen2
...
Distributed
FileSystem
Local
FileSystem
S3A
FileSystem
AzureBlob
FileSystem
SparkMapReduce
Spark
Application
MapReduce
Application
WebHdfs
FileSystem
Ozone
Ozone
FileSystem
© 2019 NTT DATA Corporation 8
Google Cloud Storage
https://github.com/GoogleCloudPlatform/bigdata-interop/tree/master/gcs
Oracle Cloud Infrastructure
https://github.com/oracle/oci-hdfs-connector
Ignite File System
https://github.com/apache/ignite/tree/master/modules/hadoop
サードパーティ製のFileSystem実装
© 2019 NTT DATA Corporation 9
FileSystem APIをユーザ向けに整理する意図
意図通りに普及/移行していない...
FileContextのドキュ��ントがない
できることに(ほとんど)差はない
Hadoopのコード自体が両方使っている
FileSystem実装を作るときにケアする必要あり
FileContext API(HADOOP-4952)
FileContext ctx = FileContext.getFileContext(uri);
FSDataInputStream is = ctx.create(path, ...);
ctx.setWorkingDir(path);
FSDataInputStream os = ctx.open(path, ...);
© 2019 NTT DATA Corporation 10
.jarにclasspathを通す
Configurationでschemeとクラス名を対応づけ
fs.foobar.impl => foobar://
see https://hadoop.apache.org/docs/r3.2.0/hadoop-project-dist/hadoop-common/core-default.xml
FileSystemモジュールを使うためには
<property>
<name>fs.foobar.impl</name>
<value>org.example.FooBarFileSystem</value>
</property>
$ tail META-INF/services/org.apache.hadoop.fs.FileSystem
org.example.FooBarFileSystem
もしくはjava.util.ServiceLoaderを使う
# 使ってなくてもロードされるのが難点
© 2019 NTT DATA Corporation 11
FileContext APIのためのもの
FileSystem実装を作るためのベースクラス、ではない
AbstractFileSystem?
<property>
<name>fs.s3a.impl</name>
<value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>
</property>
<property>
<name>fs.AbstractFileSystem.s3a.impl</name>
<value>org.apache.hadoop.fs.s3a.S3A</value>
</property>
© 2019 NTT DATA Corporation 12
FileSystem APIをユーザ向けに整理する意図
意図通りに普及/移行しなかった...
ドキュメントがない
できることが(ほとんど)変わらない
Hadoopのコード自体が両方使っている
FileSystem実装を作るときにケアする必要あり
FileSystem実装をwrapするのが定番
see o.a.h.fs.DelegateToFileSystem
FileContext API (HADOOP-4952)
FileContext ctx = FileContext.getFileContext(uri);
FSDataInputStream is = ctx.create(path, ...);
ctx.setWorkingDir(path);
FSDataInputStream os = ctx.open(path, ...);
© 2019 NTT DATA Corporation 13
ファイル操作と抽象化
© 2019 NTT DATA Corporation 14
Linuxのコマンドと似たような雰囲気
CLI(FsShell)はJava APIを利用して作られたもの
CLIによるファイル操作
$ hadoop fs -mkdir -p /foo/bar
$ hadoop fs -chmod g+w /foo/bar
$ hadoop fs -ls -R /
drwxr-xr-x - iwasakims supergroup 0 2019-08-31 15:11 /foo
drwxrwxr-x - iwasakims supergroup 0 2019-08-31 15:11 /foo/bar
$ echo baz > baz.txt
$ hadoop fs -put baz.txt /foo/bar/
$ hadoop fs -head /foo/bar/baz.txt
baz
$ hadoop fs -rm -r /foo
© 2019 NTT DATA Corporation 15
URIに対応するインスタンスを取得
設定上のデフォルトFSなら明示的な指定は不要
FileSystemインスタンスの取得
scala> import org.apache.hadoop.conf.Configuration
scala> import org.apache.hadoop.fs.FileSystem
scala> import org.apache.hadoop.fs.Path
scala> val conf = new Configuration()
scala> conf.get("fs.defaultFS")
res0: String = hdfs://localhost:8020/
scala> val fs = FileSystem.get(conf)
scala> val path = new Path("hdfs://localhost:8020/")
scala> val fs = p.getFileSystem(conf)
scala> val fs = FileSystem.get(path.toUri(), conf)
© 2019 NTT DATA Corporation 16
ディレクトリの作成
基本的に親がなければ作成 (mkdir -p)
mkdirs
scala> val path = new Path("/foo/bar")
scala> fs.mkdirs(path)
res1: Boolean = true
scala> fs.exists(new Path("/foo"))
res2: Boolean = true
© 2019 NTT DATA Corporation 17
ファイル情報(FileStatus)の取得
listStatus
scala> val listing = fs.listStatus(new Path("/foo/bar"))
scala> val f = listing(0)
f: org.apache.hadoop.fs.FileStatus =
HdfsNamedFileStatus{path=hdfs://localhost:8020/foo/bar/baz.
txt; isDirectory=false; length=4; replication=1;
blocksize=134217728; modification_time=1567217427000;
access_time=1567152892141; owner=iwasakims; group=docker;
permission=rw-r--r--; isSymlink=false; hasAcl=false;
isEncrypted=false; isErasureCoded=false}
scala> fs.getFileBlockLocations(f.getPath(), 0, f.getLen())
res23: Array[org.apache.hadoop.fs.BlockLocation] =
Array(0,4,localhost)
© 2019 NTT DATA Corporation 18
ファイルの新規作成&書き込みオープン
得られたOutputStreamにバイト列を書き込む
先頭からシーケンシャルに
create
scala> import java.nio.charset.Charset
scala> val os = fs.create(new Path("/foo/bar/baz.txt"))
scala> val buf ="baz".getBytes(Charset.forName("UTF-8"))
buf: Array[Byte] = Array(98, 97, 122)
scala> os.write(buf, 0, buf.length)
scala> os.close()
© 2019 NTT DATA Corporation 19
ファイルの読み込みオープン
open
scala> import java.nio.ByteBuffer
scala> val is = fs.open(new Path("/foo/bar/baz.txt"))
scala> val buf = ByteBuffer.allocate(3).array
scala> is.read(buf, 0, buf.length)
res1: Int = 3
scala> new String(buf.array(), Charset.forName("UTF-8"))
res2: String = baz
scala> val bb = ByteBuffer.allocate(2)
scala> is.read(1, bb)
res3: Int = 2
scala> new String(bb.array(), Charset.forName("UTF-8"))
res4: String = az
© 2019 NTT DATA Corporation 20
入力ファイルを分割してタスクに対応づける
タスクごとにデータを処理する
作業用ディレクトリを作る
タスクの出力ファイルを作る
入力ファイルからレコードを読み出す
データを処理する
出力ファイルにレコードを書き込む
出力ファイルを出力先に移動する
フレームワークが入出力を抽象化
Hadoopジョブによるデータ処理の流れ
© 2019 NTT DATA Corporation 21
データ入力を抽象化するもの
入力をInputSplitに分割
レコードを読み出す
InputFormat
public abstract class InputFormat<K, V> {
public abstract
List<InputSplit> getSplits(JobContext context
) throws ...
public abstract
RecordReader<K,V> createRecordReader(InputSplit split,
TaskAttemptContext context
) throws ...
}
© 2019 NTT DATA Corporation 22
ファイルからのデータを読み出しを抽象化
レコードを読み出すロジックは派生クラスが提供
TextInputFormat
SequenceFileInputFormat
AvroInputFormat
ParquetInputFormat
ユーザはパ��メータを指定するだけ
どのクラスを使うか
処理対象ファイルのpath
...
FileInputFormat
© 2019 NTT DATA Corporation 23
データ出力を抽象化するもの
レコードを書き出す
出力できるかを確認する
出力を確定する
OutputFormat
public abstract class OutputFormat<K, V> {
public abstract RecordWriter<K, V>
getRecordWriter(TaskAttemptContext context
) throws ...
public abstract void checkOutputSpecs(JobContext context
) throws ...
public abstract
OutputCommitter getOutputCommitter(TaskAttemptContext context
) throws ...
}
© 2019 NTT DATA Corporation 24
クラウドストレージの活用
© 2019 NTT DATA Corporation 25
マスターノード(NameNode)がボトルネック
1. NameNodeのヒープサイズ(<100GBくらい?)
2. 管理可能なスレーブノード数(<10000くらい?)
3. 処理可能なリクエスト数(<10万tpsくらい?)
ざっくりした目安
100万データブロックあたりヒープ1GB
ヒープサイズはGC的に100GB程度まで
1億ブロックで12.8PB (ブロックサイズ128MBで)
HDFSのスケーラビリティ
© 2019 NTT DATA Corporation 26
データをファイルという単位で管理
ファイルはデータ(バイト列)の入れ物
ディレクトリはファイルやディレクトリの入れ物
階層的なディレクトリ構造でファイルを整理
ファイル/ディレクトリはメタデータを持つ
所有者に応じたアクセス制御
...
ファイルシステム?
© 2019 NTT DATA Corporation 27
データをオブジェクトという単位で管理
オブジェクトはデータ(バイト列)の入れ物
バケットはオブジェクトの入れ物
フラットなキー空間でオブジェクトを管理
オブジェクトはメタデータを持つ
...
スケールアウトしやすい
REST APIによるアクセス(がメイン)
クラウドのマネージドサービスが利用可能
オブジェクトストレージ
© 2019 NTT DATA Corporation 28
S3AFileSystem
s3a://mybucket/path
ユーザが多く継続的に改良されている
Amazon S3
export HADOOP_OPTIONAL_TOOLS=hadoop-aws
classpathを通すのに設定が必要:
<property>
<name>fs.s3.impl</name>
<value>com.amazon.ws.emr.hadoop.fs.EmrFileSystem</value>
</property>
Amazon EMRでは利用されていない:
© 2019 NTT DATA Corporation 29
NativeAzureFileSystem
wasb://mycontainer@myaccount.blob.core.windows.net/path
Azureのオブジェクトストレージ
後継的なサービス(後述)が存在
Azure Blob Storage
export HADOOP_OPTIONAL_TOOLS=hadoop-azure
classpathを通すのに設定が必要:
<property>
<name>fs.defaultFS</name>
<value>wasb://mycluster@myaccount.blob.core.windows.net</value>
<final>true</final>
</property>
HDInsightでdefualtFSとして利用される:
© 2019 NTT DATA Corporation 30
AdlFileSystem
adl://mycontainer.azuredatalakestore.net/path
Azureのファイルシステム的ストレージサービス
後継的なサービス(後述)が存在
WebHDFS互換なREST APIを提供
Azure Data Lake Storage (Gen1)
export HADOOP_OPTIONAL_TOOLS=hadoop-azure-datalake
classpathを通すのに設定が必要:
© 2019 NTT DATA Corporation 31
AzureBlobFileSystem
abfs://mycontainer@myaccount.dfs.core.windows.net/path
Azureのストレージサービス
階層的名前空間(optional)を提供
Azure Data Lake Storage Gen2
export HADOOP_OPTIONAL_TOOLS=hadoop-azure
classpathを通すのに設定が必要:
<property>
<name>fs.defaultFS</name>
<value>abfs://mycluster@myaccount.dfs.core.windows.net</value>
<final>true</final>
</property>
HDInsightでdefualtFSとして利用される:
© 2019 NTT DATA Corporation 32
GoogleHadoopFileSystem
gs://container/path
サードパーティ実装
https://github.com/GoogleCloudPlatform/bigdata-interop
最近v2.0.0がリリース
https://cloud.google.com/blog/products/data-analytics/new-release-of-cloud-storage-connector-for-
hadoop-improving-performance-throughput-and-more
ランダムreadの性能向上(fadvise)
ディレクトリ操作のロック(cooperative locking)
Google Cloud Storage
© 2019 NTT DATA Corporation 33
気をつけるポイント
© 2019 NTT DATA Corporation 34
FileSystem APIはHDFSの機能を抽象化したもの
全機能を備えているのはHDFS(DistributedFileSystem)
HDFSにない機能は定義されていない
例えばファイルのランダムupdate
オブジェクトストレージはファイルシステムではない
APIをwrapしてそれっぽく見せかける
あらまし
© 2019 NTT DATA Corporation 35
オブジェクトストレージには存在しない
末尾が/の空オブジェクトで模擬(されることが多い)
/dir1/
/dir1/file1.txt
/dir1/file2.txt
ディレクトリ削除: prefixが同じオブジェクトを全削除
abfs:
hierarchical namespace機能あり(optional)
有効化するとrenameとdeleteがO(1)
https://docs.microsoft.com/en-us/azure/storage/blobs/data-lake-storage-namespace
ディレクトリ
© 2019 NTT DATA Corporation 36
キーの更新ができないデータストレージでは高価
新しい名前のオブジェクトにデータコピー
古い名前のオブジェクトを削除
ディレクトリのrenameが高価
prefixが同じオブジェクトをすべてrename
s3a: 実データのコピー&削除
abfs: メタデータのみの操作?
gs: メタ���ータのみコピー&削除
rename
© 2019 NTT DATA Corporation 37
s3a:
作成直後にリストに出ない可能性あり
削除直後にアクセスできる可能性あり
https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyModel
abfs:
hierarchical namespace enabledなら問題なし
gs: 問題なし?
https://cloud.google.com/storage/docsv/consistency
Eventual Consistency
© 2019 NTT DATA Corporation 38
S3AFileSystemの一貫性を改善するアドオン
補助的なメタデータ置き場としてDynamoDBを利用
create: S3にオブジェクト作成後DynamoDBに記録
listStatus: S3とDynamoDBの両方を見る
S3Guard (HADOOP-13345)
https://issues.apache.org/jira/secure/attachment/12821464/S3GuardImprovedConsistencyforS3AV2.pdf
© 2019 NTT DATA Corporation 39
タスク/ジョブ完了時に出力を確定
ユーザ/後続処理にすべての出力が見える
タスク/ジョブが失敗(中止)したら後片付け
ユーザ/後続処理にゴミが見えない
OutputCommitter
public abstract class OutputCommitter {
public abstract void setupJob(JobContext jobContext)
public void cleanupJob(JobContext jobContext)
public void commitJob(JobContext jobContext)
public void abortJob(JobContext jobContext, JobStatus.State state)
public abstract void setupTask(TaskAttemptContext taskContext)
public abstract boolean needsTaskCommit(TaskAttemptContext taskContext)
public abstract void commitTask(TaskAttemptContext taskContext)
public abstract void abortTask(TaskAttemptContext taskContext)
public boolean isRecoverySupported()
public boolean isCommitJobRepeatable(JobContext jobContext)
public boolean isRecoverySupported(JobContext jobContext)
public void recoverTask(TaskAttemptContext taskContext)
}
© 2019 NTT DATA Corporation 40
デフォルトのOutputCommitter
タスク成功時: 出力ファイルを出力ディレクトリに移動
ジョブ成功時: _SUCCESSという空ファイルを作成
失敗時: 出力ディレクトリをdelete
ファイルシステムを暗黙に想定
renameとdeleteが軽量でatomicでconsistent
see also ParquetOutputCommitter
https://github.com/apache/parquet-mr/blob/master/parquet-
hadoop/src/main/java/org/apache/parquet/hadoop/ParquetOutputCommitter.java
FileOutputCommitter
© 2019 NTT DATA Corporation 41
https://hadoop.apache.org/docs/r3.2.0/hadoop-aws/tools/hadoop-aws/committers.html
magic committer:
マルチパートアップロードを利用
各出力ファイルをジョブ完了時にまとめてcomplete
要S3Guard
S3A committers (HADOOP-13786)
<property>
<name>mapreduce.outputcommitter.factory.scheme.s3a</name>
<value>org.apache.hadoop.fs.s3a.commit.S3ACommitterFactory</value>
</property>
<property>
<name>fs.s3a.committer.name</name>
<value>magic</value>
</property>
<property>
<name>fs.s3a.committer.magic.enabled</name>
<value>true</value>
</property>
© 2019 NTT DATA Corporation 42
一度書き込みcloseしたファイルへの追記
s3a, wasb: 不可
adl, abfs, gs: 可
append
scala> val p = new Path("s3a://mybucket/foo.txt")
scala> val fs = p.getFileSystem(conf)
scala> fs.append(p)
java.lang.UnsupportedOperationException: Append is not supported by S3AFileSystem
at org.apache.hadoop.fs.s3a.S3AFileSystem.append(S3AFileSystem.java:1139)
at org.apache.hadoop.fs.FileSystem.append(FileSystem.java:1397)
... 49 elided
© 2019 NTT DATA Corporation 43
未サポートのFSはsetPermissionがnoop
FileStatus#getPermissionはデフォルト値を返す
ファイルなら666
ディレクトリなら777
UnsupportedOperationExceptionは出ない
setOwner/getOwnerも同様
パーミッション
© 2019 NTT DATA Corporation 44
書き込み中のファイルの読み込み
hdfs, abfs: 可
open以後にwriteされたデータを読むには再open
gs: 可(オプショナル)
hsync/syncを呼ぶ必要あり
fs.gs.outputstream.type=SYNCABLE_COMPOSITE
s3a: 不可
Tailing
© 2019 NTT DATA Corporation 45
FSDataOutputStreamの機能
hflushとhsyncが使えるか
(fsyncに相当するAPI)
未サポートだとflushが呼ばれる
Syncable
@Override // Syncable
public void hflush() throws IOException {
if (wrappedStream instanceof Syncable) {
((Syncable)wrappedStream).hflush();
} else {
wrappedStream.flush();
}
}
© 2019 NTT DATA Corporation 46
FSDataInputStream#read(ByteByffer buf)の有無
see also ByteBufferPositionedReadable
ByteBufferReadable
scala> val p = new Path("s3a://mybucket/foo.txt")
scala> val fs = p.getFileSystem(new Configuration())
scala> val is = fs.open(p)
scala> val buf = ByteBuffer.allocate(3).array
scala> is.read(buf, 0, buf.length)
res13: Int = 3
scala> new String(buf, Charset.forName("UTF-8"))
res14: String = foo
scala> is.seek(0)
scala> val bb = ByteBuffer.allocateDirect(3)
scala> is.read(bb)
java.lang.UnsupportedOperationException: Byte-buffer read unsupported by input stream
at org.apache.hadoop.fs.FSDataInputStream.read(FSDataInputStream.java:152)
... 49 elided
© 2019 NTT DATA Corporation 47
hasCapabilityメソッドで���認できる
まだ未対応の実装も多いはず..
StreamCapabilities
public boolean hasCapability(String capability) {
switch (StringUtils.toLowerCase(capability)) {
case StreamCapabilities.READAHEAD:
case StreamCapabilities.DROPBEHIND:
case StreamCapabilities.UNBUFFER:
case StreamCapabilities.READBYTEBUFFER:
case StreamCapabilities.PREADBYTEBUFFER:
return true;
default:
return false;
}
}
© 2019 NTT DATA Corporation 48
Ozone
© 2019 NTT DATA Corporation 49
新作オブジェクトストレージ
/volume/bucket/keyという名前空間
S3互換の機能も提供
REST APIと認証(Signature Version 4)
Hadoopのサブモジュール
設定、RPC、認証、暗号化などの共通パーツを利用
depending on hadoop-common
リリースはHadoop本体とは独立
Ozoneのbinary tarballはOzone専用
Hadoopのbinary tarballにはOzoneは含まれない
Ozoneの概要
© 2019 NTT DATA Corporation 50
Hadoop Commonのモジュール利用している
HDFSのモジュールを(ほとんど)利用していない
大部分は新規コード
OzoneとHDFSとの関係
$ ozone --daemon start datanode
<property>
<name>dfs.datanode.plugins</name>
<value>org.apache.hadoop.ozone.HddsDatanodeService</value>
</property>
DataNode内で追加のモジュールとしても起動可能
Ozone専用のプロセスを起動する場合:
© 2019 NTT DATA Corporation 51
hadoop-hddsとhadoop-ozone
HDDS: 分散KVS (using RocksDB and Ratis)
Ozone: 名前空間管理 (using RocksDB and Ratis)
HDDS? Ozone?
https://github.com/apache/hadoop/blob/trunk/hadoop-hdds/docs/static/OzoneOverview.svg
© 2019 NTT DATA Corporation 52
FSNameSystem:
ファイルシステムメタデータ
ファイルとブロックの対応を管理
全体をメモリ上に置く必要あり
BlockManager:
ブロックとDataNodeとの対応を管理
FSNameSystemと密結合
分離したいができていない
NameNodeのFSNameSystemとBlockManager
© 2019 NTT DATA Corporation 53
OzoneにアクセスするためのFileSystem実装
o3fs://bucket.volume.omhost:9862/path
RPCでOzoneにアクセス
Eventually Consistentではない
ディレクトリは空オブジェクト(末尾/)で表現
renameはOzoneManagerのメタデータのみの更新
ディレクトリのrename/deleteは非atomic
append不可
OzoneFileSystem
© 2019 NTT DATA Corporation 54
まとめ
© 2019 NTT DATA Corporation 55
Hadoopはデータストアへの透過的なアクセスを提供
とはいえデータストアごとの違いに注意
実装ごとのチューニングノブも多数
その辺すこし気にしつつご活用ください
まとめ
© 2019 NTT DATA Corporation本資料に記載されている会社名、商品名、又はサービス名は、各社の登録商標又は商標です。

More Related Content

データインターフェースとしてのHadoop ~HDFSとクラウドストレージと私~ (NTTデータ テクノロジーカンファレンス 2019 講演資料、2019/09/05)

  • 1. © 2019 NTT DATA Corporation 1 © 2019 NTT DATA Corporation NTTデータ テクノロジーカンファレンス 2019 データインターフェースとしてのHadoop 2019年9月5日 株式会社NTTデータ 岩崎正剛
  • 2. © 2019 NTT DATA Corporation 2 クラウドサービスの発展により、大量のデータを格納して処理す るための選択肢は多くなりました。 Hadoopは、分散ファイルシステム(HDFS)と各種クラウドスト レージ等を透過的に併用して、データ処理を行うための機能を提 供しており、Sparkに代表される分散処理フレームワークのユー ザは、あまり意識せずにこれを活用しています。 本セッションでは、Hadoopが提供するデータアクセスのための インターフェースやモジュールに焦点をあて、データストアの併 用や使い分けの勘所などを解説します。 概要
  • 3. © 2019 NTT DATA Corporation 3 Hadoopのファイルシステム
  • 4. © 2019 NTT DATA Corporation 4 Hadoop Distributed File System Hadoop = 分散FS + 分散処理FW Hadoopアプリケーション = HDFS上のデータを処理するもの HDFS https://hadoop.apache.org/docs/r3.2.0/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
  • 5. © 2019 NTT DATA Corporation 5 core-site.xmlに書く基本的な設定項目 HDFSのアクセス先を書く ネームサービス名 またはNameNodeのホスト名:port hdfs://以外もある? defaultではないFSもある? fs.defaultFS <property> <name>fs.defaultFS</name> <value>hdfs://mycluster/</value> </property>
  • 6. © 2019 NTT DATA Corporation 6 ファイルのpathをURI形式で指定 schemeに応じてよしなにデータを読み書き データストア間のデータコピー/移動にも便利 distcpなどでも同じ要領 schemeなしのただのpathだとfs.defaultFS Hadoopのデータストア抽象化 $ hadoop fs -cp file:///a/b hdfs://ns1/a/ $ hadoop fs -cp hdfs://ns1/a/b hdfs://nn1:8020/a/ $ hadoop fs -cp webhdfs://nn1:9870/a/b s3a://bc/a/
  • 7. © 2019 NTT DATA Corporation 7 Hadoop Compatible File Systems Hadoop FileSystem API Hadoop Application HDFS Local FS Amazon S3 Azure Data Lake Storage gen2 ... Distributed FileSystem Local FileSystem S3A FileSystem AzureBlob FileSystem SparkMapReduce Spark Application MapReduce Application WebHdfs FileSystem Ozone Ozone FileSystem
  • 8. © 2019 NTT DATA Corporation 8 Google Cloud Storage https://github.com/GoogleCloudPlatform/bigdata-interop/tree/master/gcs Oracle Cloud Infrastructure https://github.com/oracle/oci-hdfs-connector Ignite File System https://github.com/apache/ignite/tree/master/modules/hadoop サードパーティ製のFileSystem実装
  • 9. © 2019 NTT DATA Corporation 9 FileSystem APIをユーザ向けに整理する意図 意図通りに普及/移行していない... FileContextのドキュメントがない できることに(ほとんど)差はない Hadoopのコード自体が両方使っている FileSystem実装を作るときにケアする必要あり FileContext API(HADOOP-4952) FileContext ctx = FileContext.getFileContext(uri); FSDataInputStream is = ctx.create(path, ...); ctx.setWorkingDir(path); FSDataInputStream os = ctx.open(path, ...);
  • 10. © 2019 NTT DATA Corporation 10 .jarにclasspathを通す Configurationでschemeとクラス名を対応づけ fs.foobar.impl => foobar:// see https://hadoop.apache.org/docs/r3.2.0/hadoop-project-dist/hadoop-common/core-default.xml FileSystemモジュールを使うためには <property> <name>fs.foobar.impl</name> <value>org.example.FooBarFileSystem</value> </property> $ tail META-INF/services/org.apache.hadoop.fs.FileSystem org.example.FooBarFileSystem もしくはjava.util.ServiceLoaderを使う # 使ってなくてもロードされるのが難点
  • 11. © 2019 NTT DATA Corporation 11 FileContext APIのためのもの FileSystem実装を作るためのベースクラス、ではない AbstractFileSystem? <property> <name>fs.s3a.impl</name> <value>org.apache.hadoop.fs.s3a.S3AFileSystem</value> </property> <property> <name>fs.AbstractFileSystem.s3a.impl</name> <value>org.apache.hadoop.fs.s3a.S3A</value> </property>
  • 12. © 2019 NTT DATA Corporation 12 FileSystem APIをユーザ向けに整理する意図 意図通りに普及/移行しなかった... ドキュメントがない できることが(ほとんど)変わらない Hadoopのコード自体が両方使っている FileSystem実装を作るときにケアする必要あり FileSystem実装をwrapするのが定番 see o.a.h.fs.DelegateToFileSystem FileContext API (HADOOP-4952) FileContext ctx = FileContext.getFileContext(uri); FSDataInputStream is = ctx.create(path, ...); ctx.setWorkingDir(path); FSDataInputStream os = ctx.open(path, ...);
  • 13. © 2019 NTT DATA Corporation 13 ファイル操作と抽象化
  • 14. © 2019 NTT DATA Corporation 14 Linuxのコマンドと似たような雰囲気 CLI(FsShell)はJava APIを利用して作られたもの CLIによるファイル操作 $ hadoop fs -mkdir -p /foo/bar $ hadoop fs -chmod g+w /foo/bar $ hadoop fs -ls -R / drwxr-xr-x - iwasakims supergroup 0 2019-08-31 15:11 /foo drwxrwxr-x - iwasakims supergroup 0 2019-08-31 15:11 /foo/bar $ echo baz > baz.txt $ hadoop fs -put baz.txt /foo/bar/ $ hadoop fs -head /foo/bar/baz.txt baz $ hadoop fs -rm -r /foo
  • 15. © 2019 NTT DATA Corporation 15 URIに対応するインスタンスを取得 設定上のデフォルトFSなら明示的な指定は不要 FileSystemインスタンスの取得 scala> import org.apache.hadoop.conf.Configuration scala> import org.apache.hadoop.fs.FileSystem scala> import org.apache.hadoop.fs.Path scala> val conf = new Configuration() scala> conf.get("fs.defaultFS") res0: String = hdfs://localhost:8020/ scala> val fs = FileSystem.get(conf) scala> val path = new Path("hdfs://localhost:8020/") scala> val fs = p.getFileSystem(conf) scala> val fs = FileSystem.get(path.toUri(), conf)
  • 16. © 2019 NTT DATA Corporation 16 ディレクトリの作成 基本的に親がなければ作成 (mkdir -p) mkdirs scala> val path = new Path("/foo/bar") scala> fs.mkdirs(path) res1: Boolean = true scala> fs.exists(new Path("/foo")) res2: Boolean = true
  • 17. © 2019 NTT DATA Corporation 17 ファイル情報(FileStatus)の取得 listStatus scala> val listing = fs.listStatus(new Path("/foo/bar")) scala> val f = listing(0) f: org.apache.hadoop.fs.FileStatus = HdfsNamedFileStatus{path=hdfs://localhost:8020/foo/bar/baz. txt; isDirectory=false; length=4; replication=1; blocksize=134217728; modification_time=1567217427000; access_time=1567152892141; owner=iwasakims; group=docker; permission=rw-r--r--; isSymlink=false; hasAcl=false; isEncrypted=false; isErasureCoded=false} scala> fs.getFileBlockLocations(f.getPath(), 0, f.getLen()) res23: Array[org.apache.hadoop.fs.BlockLocation] = Array(0,4,localhost)
  • 18. © 2019 NTT DATA Corporation 18 ファイルの新規作成&書き込みオープン 得られたOutputStreamにバイト列を書き込む 先頭からシーケンシャルに create scala> import java.nio.charset.Charset scala> val os = fs.create(new Path("/foo/bar/baz.txt")) scala> val buf ="baz".getBytes(Charset.forName("UTF-8")) buf: Array[Byte] = Array(98, 97, 122) scala> os.write(buf, 0, buf.length) scala> os.close()
  • 19. © 2019 NTT DATA Corporation 19 ファイルの読み込みオープン open scala> import java.nio.ByteBuffer scala> val is = fs.open(new Path("/foo/bar/baz.txt")) scala> val buf = ByteBuffer.allocate(3).array scala> is.read(buf, 0, buf.length) res1: Int = 3 scala> new String(buf.array(), Charset.forName("UTF-8")) res2: String = baz scala> val bb = ByteBuffer.allocate(2) scala> is.read(1, bb) res3: Int = 2 scala> new String(bb.array(), Charset.forName("UTF-8")) res4: String = az
  • 20. © 2019 NTT DATA Corporation 20 入力ファイルを分割してタスクに対応づける タスクごとにデータを処理する 作業用ディレクトリを作る タスクの出力ファイルを作る 入力ファイルからレコードを読み出す データを処理する 出力ファイルにレコードを書き込む 出力ファイルを出力先に移動する フレームワークが入出力を抽象化 Hadoopジョブによるデータ処理の流れ
  • 21. © 2019 NTT DATA Corporation 21 データ入力を抽象化するもの 入力をInputSplitに分割 レコードを読み出す InputFormat public abstract class InputFormat<K, V> { public abstract List<InputSplit> getSplits(JobContext context ) throws ... public abstract RecordReader<K,V> createRecordReader(InputSplit split, TaskAttemptContext context ) throws ... }
  • 22. © 2019 NTT DATA Corporation 22 ファイルからのデータを読み出しを抽象化 レコードを読み出すロジックは派生クラスが提供 TextInputFormat SequenceFileInputFormat AvroInputFormat ParquetInputFormat ユーザはパラメータを指定するだけ どのクラスを使うか 処理対象ファイルのpath ... FileInputFormat
  • 23. © 2019 NTT DATA Corporation 23 データ出力を抽象化するもの レコードを書き出す 出力できるかを確認する 出力を確定する OutputFormat public abstract class OutputFormat<K, V> { public abstract RecordWriter<K, V> getRecordWriter(TaskAttemptContext context ) throws ... public abstract void checkOutputSpecs(JobContext context ) throws ... public abstract OutputCommitter getOutputCommitter(TaskAttemptContext context ) throws ... }
  • 24. © 2019 NTT DATA Corporation 24 クラウドストレージの活用
  • 25. © 2019 NTT DATA Corporation 25 マスターノード(NameNode)がボトルネック 1. NameNodeのヒープサイズ(<100GBくらい?) 2. 管理可能なスレーブノード数(<10000くらい?) 3. 処理可能なリクエスト数(<10万tpsくらい?) ざっくりした目安 100万データブロックあたりヒープ1GB ヒープサイズはGC的に100GB程度まで 1億ブロックで12.8PB (ブロックサイズ128MBで) HDFSのスケーラビリティ
  • 26. © 2019 NTT DATA Corporation 26 データをファイルという単位で管理 ファイルはデータ(バイト列)の入れ物 ディレクトリはファイルやディレクトリの入れ物 階層的なディレクトリ構造でファイルを整理 ファイル/ディレクトリはメタデータを持つ 所有者に応じたアクセス制御 ... ファイルシステム?
  • 27. © 2019 NTT DATA Corporation 27 データをオブジェクトという単位で管理 オブジェクトはデータ(バイト列)の入れ物 バケットはオブジェクトの入れ物 フラットなキー空間でオブジェクトを管理 オブジェクトはメタデータを持つ ... スケールアウトしやすい REST APIによるアクセス(がメイン) クラウドのマネージドサービスが利用可能 オブジェクトストレージ
  • 28. © 2019 NTT DATA Corporation 28 S3AFileSystem s3a://mybucket/path ユーザが多く継続的に��良されている Amazon S3 export HADOOP_OPTIONAL_TOOLS=hadoop-aws classpathを通すのに設定が必要: <property> <name>fs.s3.impl</name> <value>com.amazon.ws.emr.hadoop.fs.EmrFileSystem</value> </property> Amazon EMRでは利用されていない:
  • 29. © 2019 NTT DATA Corporation 29 NativeAzureFileSystem wasb://mycontainer@myaccount.blob.core.windows.net/path Azureのオブジェクトストレージ 後継的なサービス(後述)が存在 Azure Blob Storage export HADOOP_OPTIONAL_TOOLS=hadoop-azure classpathを通すのに設定が必要: <property> <name>fs.defaultFS</name> <value>wasb://mycluster@myaccount.blob.core.windows.net</value> <final>true</final> </property> HDInsightでdefualtFSとして利用される:
  • 30. © 2019 NTT DATA Corporation 30 AdlFileSystem adl://mycontainer.azuredatalakestore.net/path Azureのファイルシステム的ストレージサービス 後継的なサービス(後述)が存在 WebHDFS互換なREST APIを提供 Azure Data Lake Storage (Gen1) export HADOOP_OPTIONAL_TOOLS=hadoop-azure-datalake classpathを通すのに設定が必要:
  • 31. © 2019 NTT DATA Corporation 31 AzureBlobFileSystem abfs://mycontainer@myaccount.dfs.core.windows.net/path Azureのストレージサービス 階層的名前空間(optional)を提供 Azure Data Lake Storage Gen2 export HADOOP_OPTIONAL_TOOLS=hadoop-azure classpathを通すのに設定が必要: <property> <name>fs.defaultFS</name> <value>abfs://mycluster@myaccount.dfs.core.windows.net</value> <final>true</final> </property> HDInsightでdefualtFSとして利用される:
  • 32. © 2019 NTT DATA Corporation 32 GoogleHadoopFileSystem gs://container/path サードパーティ実装 https://github.com/GoogleCloudPlatform/bigdata-interop 最近v2.0.0がリリース https://cloud.google.com/blog/products/data-analytics/new-release-of-cloud-storage-connector-for- hadoop-improving-performance-throughput-and-more ランダムreadの性能向上(fadvise) ディレクトリ操作のロック(cooperative locking) Google Cloud Storage
  • 33. © 2019 NTT DATA Corporation 33 気をつけるポイント
  • 34. © 2019 NTT DATA Corporation 34 FileSystem APIはHDFSの機能を抽象化したもの 全機能を備えているのはHDFS(DistributedFileSystem) HDFSにない機能は定義されていない 例えばファイルのランダムupdate オブジェクトストレージはファイルシステムではない APIをwrapしてそれっぽく見せかける あらまし
  • 35. © 2019 NTT DATA Corporation 35 オブジェクトストレージには存在しない 末尾が/の空オブジェクトで模擬(されることが多い) /dir1/ /dir1/file1.txt /dir1/file2.txt ディレクトリ削除: prefixが同じオブジェクトを全削除 abfs: hierarchical namespace機能あり(optional) 有効化するとrenameとdeleteがO(1) https://docs.microsoft.com/en-us/azure/storage/blobs/data-lake-storage-namespace ディレクトリ
  • 36. © 2019 NTT DATA Corporation 36 キーの更新ができないデータストレージでは高価 新しい名前のオブジェクトにデータコピー 古い名前のオブジェクトを削除 ディレクトリのrenameが高価 prefixが同じオブジェクトをすべてrename s3a: 実データのコピー&削除 abfs: メタデータのみの操作? gs: メタデータのみコピー&削除 rename
  • 37. © 2019 NTT DATA Corporation 37 s3a: 作成直後にリストに出ない可能性あり 削除直後にアクセスできる可能性あり https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html#ConsistencyModel abfs: hierarchical namespace enabledなら問題なし gs: 問題なし? https://cloud.google.com/storage/docsv/consistency Eventual Consistency
  • 38. © 2019 NTT DATA Corporation 38 S3AFileSystemの一貫性を改善するアドオン 補助的なメタデータ置き場としてDynamoDBを利用 create: S3にオブジェクト作成後DynamoDBに記録 listStatus: S3とDynamoDBの両方を見る S3Guard (HADOOP-13345) https://issues.apache.org/jira/secure/attachment/12821464/S3GuardImprovedConsistencyforS3AV2.pdf
  • 39. © 2019 NTT DATA Corporation 39 タスク/ジョブ完了時に出力を確定 ユーザ/後続処理にすべての出力が見える タスク/ジョブが失敗(中止)したら後片付け ユーザ/後続処理にゴミが見えない OutputCommitter public abstract class OutputCommitter { public abstract void setupJob(JobContext jobContext) public void cleanupJob(JobContext jobContext) public void commitJob(JobContext jobContext) public void abortJob(JobContext jobContext, JobStatus.State state) public abstract void setupTask(TaskAttemptContext taskContext) public abstract boolean needsTaskCommit(TaskAttemptContext taskContext) public abstract void commitTask(TaskAttemptContext taskContext) public abstract void abortTask(TaskAttemptContext taskContext) public boolean isRecoverySupported() public boolean isCommitJobRepeatable(JobContext jobContext) public boolean isRecoverySupported(JobContext jobContext) public void recoverTask(TaskAttemptContext taskContext) }
  • 40. © 2019 NTT DATA Corporation 40 デフォルトのOutputCommitter タスク成功時: 出力ファイルを出力ディレクトリに移動 ジョブ成功時: _SUCCESSという空ファイルを作成 失敗時: 出力ディレクトリをdelete ファイルシステムを暗黙に想定 renameとdeleteが軽量でatomicでconsistent see also ParquetOutputCommitter https://github.com/apache/parquet-mr/blob/master/parquet- hadoop/src/main/java/org/apache/parquet/hadoop/ParquetOutputCommitter.java FileOutputCommitter
  • 41. © 2019 NTT DATA Corporation 41 https://hadoop.apache.org/docs/r3.2.0/hadoop-aws/tools/hadoop-aws/committers.html magic committer: マルチパートアップロードを利用 各出力ファイルをジョブ完了時にまとめてcomplete 要S3Guard S3A committers (HADOOP-13786) <property> <name>mapreduce.outputcommitter.factory.scheme.s3a</name> <value>org.apache.hadoop.fs.s3a.commit.S3ACommitterFactory</value> </property> <property> <name>fs.s3a.committer.name</name> <value>magic</value> </property> <property> <name>fs.s3a.committer.magic.enabled</name> <value>true</value> </property>
  • 42. © 2019 NTT DATA Corporation 42 一度書き込みcloseしたファイルへの追記 s3a, wasb: 不可 adl, abfs, gs: 可 append scala> val p = new Path("s3a://mybucket/foo.txt") scala> val fs = p.getFileSystem(conf) scala> fs.append(p) java.lang.UnsupportedOperationException: Append is not supported by S3AFileSystem at org.apache.hadoop.fs.s3a.S3AFileSystem.append(S3AFileSystem.java:1139) at org.apache.hadoop.fs.FileSystem.append(FileSystem.java:1397) ... 49 elided
  • 43. © 2019 NTT DATA Corporation 43 未サポートのFSはsetPermissionがnoop FileStatus#getPermissionはデフォルト値を返す ファイルなら666 ディレクトリなら777 UnsupportedOperationExceptionは出ない setOwner/getOwnerも同様 パーミッション
  • 44. © 2019 NTT DATA Corporation 44 書き込み中のファイルの読み込み hdfs, abfs: 可 open以後にwriteされたデータを読むには再open gs: 可(オプショナル) hsync/syncを呼ぶ必要あり fs.gs.outputstream.type=SYNCABLE_COMPOSITE s3a: 不可 Tailing
  • 45. © 2019 NTT DATA Corporation 45 FSDataOutputStreamの機能 hflushとhsyncが使えるか (fsyncに相当するAPI) 未サポートだとflushが呼ばれる Syncable @Override // Syncable public void hflush() throws IOException { if (wrappedStream instanceof Syncable) { ((Syncable)wrappedStream).hflush(); } else { wrappedStream.flush(); } }
  • 46. © 2019 NTT DATA Corporation 46 FSDataInputStream#read(ByteByffer buf)の有無 see also ByteBufferPositionedReadable ByteBufferReadable scala> val p = new Path("s3a://mybucket/foo.txt") scala> val fs = p.getFileSystem(new Configuration()) scala> val is = fs.open(p) scala> val buf = ByteBuffer.allocate(3).array scala> is.read(buf, 0, buf.length) res13: Int = 3 scala> new String(buf, Charset.forName("UTF-8")) res14: String = foo scala> is.seek(0) scala> val bb = ByteBuffer.allocateDirect(3) scala> is.read(bb) java.lang.UnsupportedOperationException: Byte-buffer read unsupported by input stream at org.apache.hadoop.fs.FSDataInputStream.read(FSDataInputStream.java:152) ... 49 elided
  • 47. © 2019 NTT DATA Corporation 47 hasCapabilityメソッドで確認できる まだ未対応の実装も多いはず.. StreamCapabilities public boolean hasCapability(String capability) { switch (StringUtils.toLowerCase(capability)) { case StreamCapabilities.READAHEAD: case StreamCapabilities.DROPBEHIND: case StreamCapabilities.UNBUFFER: case StreamCapabilities.READBYTEBUFFER: case StreamCapabilities.PREADBYTEBUFFER: return true; default: return false; } }
  • 48. © 2019 NTT DATA Corporation 48 Ozone
  • 49. © 2019 NTT DATA Corporation 49 新作オブジェクトストレージ /volume/bucket/keyという名前空間 S3互換の機能も提供 REST APIと認証(Signature Version 4) Hadoopのサブモジュール 設定、RPC、認証、暗号化などの共通パーツを利用 depending on hadoop-common リリースはHadoop本体とは独立 Ozoneのbinary tarballはOzone専用 Hadoopのbinary tarballにはOzoneは含まれない Ozoneの概要
  • 50. © 2019 NTT DATA Corporation 50 Hadoop Commonのモジュール利用している HDFSのモジュールを(ほとんど)利用していない 大部分は新規コード OzoneとHDFSとの関係 $ ozone --daemon start datanode <property> <name>dfs.datanode.plugins</name> <value>org.apache.hadoop.ozone.HddsDatanodeService</value> </property> DataNode内で追加のモジュールとしても起動可能 Ozone専用のプロセスを起動する場合:
  • 51. © 2019 NTT DATA Corporation 51 hadoop-hddsとhadoop-ozone HDDS: 分散KVS (using RocksDB and Ratis) Ozone: 名前空間管理 (using RocksDB and Ratis) HDDS? Ozone? https://github.com/apache/hadoop/blob/trunk/hadoop-hdds/docs/static/OzoneOverview.svg
  • 52. © 2019 NTT DATA Corporation 52 FSNameSystem: ファイルシステムメタデータ ファイルとブロックの対応を管理 全体をメモリ上に置く必要あり BlockManager: ブロックとDataNodeとの対応を管理 FSNameSystemと密結合 分離したいができていない NameNodeのFSNameSystemとBlockManager
  • 53. © 2019 NTT DATA Corporation 53 OzoneにアクセスするためのFileSystem実装 o3fs://bucket.volume.omhost:9862/path RPCでOzoneにアクセス Eventually Consistentではない ディレクトリは空オブジェクト(末尾/)で表現 renameはOzoneManagerのメタデータのみの更新 ディレクトリのrename/deleteは非atomic append不可 OzoneFileSystem
  • 54. © 2019 NTT DATA Corporation 54 まとめ
  • 55. © 2019 NTT DATA Corporation 55 Hadoopはデータストアへの透過的なアクセスを提供 とはいえデータストアごとの違いに注意 実装ごとのチューニングノブも多数 その辺すこし気にしつつご活用ください まとめ
  • 56. © 2019 NTT DATA Corporation本資料に記載されている会社名、商品名、又はサービス名は、各社の登録商標又は商標です。