読者です 読者をやめる 読者になる 読者になる

hadoop アドベントカレンダー 2011 3日目 疑似分散モードについて

hadoopアドベントカレンダー2011 3日目の12/3を担当する@marblejenkaです。


今日は、スタンドアロンモードと義人分散モード、完全分散モードの違いを調べます。hadopoを使ったアプリケーションを開発していると、だいたいどのモードも試してみたことはあると思いますが、それぞれのモードの挙動の違い、とくに疑似分散モードと完全分散モードの違いはあまり正確には把握していないなあということで。


とりあえず、インストラクション(一台構成複数台構成)で見かける説明を抜粋すると、

・stdalone
By default, Hadoop is configured to run in a non-distributed mode, as a single Java process. This is useful for debugging.

・pseudo
Hadoop can also be run on a single-node in a pseudo-distributed mode where each Hadoop daemon runs in a separate Java process.

・fully distributed
特に記述はないけど、まあ普通にマスター1以上、スレーブ2以上な感じの構成のことを指すものかなあと。

という感じです。


hadoopの設定ファイルをつくる上では、

・stdalone
fs.default.name = file:///
dfs.replication = 指定しない

・pseudo
fs.default.name = hdfs:///
dfs.replication = 1

・fully distributed
fs.default.name = hdfs:///
dfs.replication = 2以上?

という感じで説明されることが多いと思います。



今回はいろいろ裏をとりたいので、

・クライアントからのファイルシステムに対するアクセス
・fs.default.nameの設定毎によるデーモンの挙動の違い
・dfs.replicationの設定毎によるデーモンの挙動の違い

このへんの観点で簡単にソースを洗ってみることにしました。


・クライアントからのファイルシステムに対するアクセス
適当にhadoop fs -lsくらいから追っかけていきます。bin/hadoopあたりをみると、org.apache.hadoop.fs.FsShellに飛んでいることがわかります。
そこからは適当に所与の文字列からPathを生成してファイルシステムを取得していることがわかるので、
standaloneであればローカルファイルシステムをみて、疑似分散・完全分散だとHDFSを見に行っているという感じです。Pathを生成するときのプロトコルに(指定がなければ)fs.default.nameが使用されるので、まあ各モードでそんなにおもしろい違いはないという感じです。



・fs.default.nameの設定毎によるデーモンの挙動の違い
org.apache.hadoop.fs.FileSystem

public static URI getDefaultUri(Configuration conf) {
return URI.create(fixName(conf.get(FS_DEFAULT_NAME_KEY, "file:///")));
}

とかこんな感じのところで使われていますが(FS_DEFAULT_NAME_KEYは文字列の"fs.default.name")、URIで返してるのでファイルシステムをとるところが変わるだけかなあという感じ。これもそんなにおもしろい違いはないなあという感じです。強いて言うと、org.apache.hadoop.fs.CommonConfigurationKeys.DFSConfigKeysがいろいろ泣けてくる感じです。



・dfs.replicationの設定毎によるデーモンの挙動の違い
使われているところは、

org.apache.hadoop.hdfs.server.namenode.FSNamesystem

this.defaultReplication = conf.getInt("dfs.replication", 3);

org.apache.hadoop.hdfs.server.namenode.NamenodeFsck.Result

this.replication = (short)conf.getInt("dfs.replication", 3);

org.apache.hadoop.hdfs.DFSClient

defaultReplication = (short) conf.getInt("dfs.replication", 3);

とかです。


FSNamesystemのdefaultReplicationはorg.apache.hadoop.hdfs.server.namenode.FSImageで使われているようなのですが、

// read file info
short replication = FSNamesystem.getFSNamesystem().getDefaultReplication();

LOG.info("Number of files = " + numFiles);

String path;
String parentPath = "";
INodeDirectory parentINode = fsDir.rootDir;
for (long i = 0; i < numFiles; i++) {
long modificationTime = 0;
long atime = 0;
long blockSize = 0;
path = readString(in);
replication = in.readShort();
replication = FSEditLog.adjustReplication(replication);

こんな感じでFSImageの値で上書きして無視するので、僕も無視します(inはfsimageのファイルのDataInputStream)。

NamenodeFsck.Resultは表示用で情報をとってるだけなので無視します。

DFSClientはファイルを作成する際に、NameNodeにいくつレプリカを複製するか情報を伝えるために使います。あとは、レプリカ数をこいつからとれるような口があって、ジョブの履歴にそのときのレプリケーション数を出したりとか、FileSystemにレプリカ数を伝えたりしているぽいです。

と、FileSystemに

public abstract FSDataOutputStream create(Path f,
FsPermission permission,
boolean overwrite,
int bufferSize,
short replication,
long blockSize,
Progressable progress) throws IOException;

こんな感じのレプリケーション数を指定するインターフェイスがありますが、ローカルファイルでは華麗に無視します。

/** {@inheritDoc} */
public FSDataOutputStream create(Path f, boolean overwrite, int bufferSize,
short replication, long blockSize, Progressable progress)
throws IOException {
if (exists(f) && !overwrite) {
throw new IOException("File already exists:"+f);
}
Path parent = f.getParent();
if (parent != null && !mkdirs(parent)) {
throw new IOException("Mkdirs failed to create " + parent.toString());
}
return new FSDataOutputStream(new BufferedOutputStream(
new LocalFSFileOutputStream(f, false), bufferSize), statistics);
}

また、スタンドアロンモードではレプリカ数をチェックするNameNodeのプロセスも起動させないので、ふつうにふつうのローカルファイルシステムを参照するものとして使えているようです。


と、余談ですが、dfs.replicationって0でも動くかなあと思って試してみました。だめでした。レプリカ数というか、同じブロックをトータルでいくつ持つか数的な感じですかね。

java.io.IOException: failed to create file /user/asakusa/setup.sh on client 127.0.0.1.
Requested replication 0 is less than the required minimum 1
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFileInternal(FSNamesystem.java:1177)
at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFile(FSNamesystem.java:1120)
at org.apache.hadoop.hdfs.server.namenode.NameNode.create(NameNode.java:585)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:557)
at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:1434)
at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:1430)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:396)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1127)
at org.apache.hadoop.ipc.Server$Handler.run(Server.java:1428)
2


ということで、疑似分散モードと完全分散モードには大きな違いはないようで、スタンドアロンモードとは前者のふたつはFileSystemのレイヤで差異を吸収している、という感じでした。思ったよりおもしろくない感じでした。おもむろに、replication < 3とか出てきてもいいなあとか思ったのですが。

明日は @taro_x さんです。