Hadoop Cluster Setup(CDHではなく本家)

Hadoopの事を理解するために、CDHなどのディストリビューションではなく、本家のHadoopでClusterをセットアップしてみます。
また、データ保存先にHDFSではなくIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)を設定してみます。(hadoop Clusterも[IDCFクラウド](http://www.idcf.jp/cloud/)上に構築します。)
なお、今回のセットアップはNon-Secure Modeとよばれる、認証や暗号化なしのセットアップとなります。(参考: [Secure Mode](http://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-common/SecureMode.html)、ケルベロス認証を使うようななので、敷居高いですね。。。私もまだ試せていません。)

私もまだよくわかっていない部分も多く、間違っている部分もあるかもしれませんので、あしからず。(基本的には自分のためのメモです。)

参考URL: [Hadoop Cluster Setup](http://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-common/ClusterSetup.html)
参考URL: [Integration with Amazon Web Services](http://hadoop.apache.org/docs/r2.7.2/hadoop-aws/tools/hadoop-aws/index.html)

# 環境準備
* CentOS 7.2.1511
* java-1.7.0-openjdk-1.7.0.99-2.6.5.0.el7_2.x86_64 (CentOS付属)
* [hadoop-2.7.2](http://www.apache.org/dyn/closer.cgi/hadoop/common/hadoop-2.7.2/hadoop-2.7.2.tar.gz)

javaは[Hadoop Java Versions](http://wiki.apache.org/hadoop/HadoopJavaVersions)にて、Testedバージョンが公開されています。今回は、まぁ、動けばいいや、なのでCentOS付属のOpenJDKで動かしています。

## デーモンとサーバ台数
* HDFSのデーモン
    * NameNode : ファイルシステムのmetadataと実際にデータを格納するDataNodeの管理を行います。
    * SecondaryNameNode : NameNodeのMetaDataのメンテナンスを行います。
    * DataNode : 実際のデータを保持します。データは一定のサイズに分割され、複数のDataNodeへ保存されます。
* YARNのデーモン
    * ResourceManager : クライアントから受けとったジョブを管理して各NodeManage上で実行します。
    * NodeManager : 実際にジョブを実行する計算ノードです。
    * WebAppProxy : 各WebUIの為のproxy?(あまりちゃんと調べてない。)
* MapReduceのデーモン
    * MapReduce Job History Server : MapReduceのジョブのヒストリーを管理します。

すべてのデーモンを別々のサーバで動かすこともできます。
今回は、次のようにしようと思います。

* Server1 : NameNode, SecondaryNameNode
* Server2 : ResourceManager, WebAppProxy, MapReduce Job History Server
* Server3 : DataNode1, NodeManager1
* Server4 : DataNode2, NodeManager2
* Server5 : DataNode3, NodeManager3
* Server6 : Client

本来はNameNode, SecondaryNameNodeは分けるべきですが、今回はこれで。
せっかくなので、NodeManagerは3台用意しています。余裕のある方は、全部を個別のサーバにインストールしてみても良いかもしれません。

ところで、NameNodeとDataNodeはIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)を代わりに使うので不要そうですが、現状ではMapReduce Job History Serverがhdfsにしか対応していないようで、NameNodeとDataNodeを完全になくすことはできないようです。どうやら、このバージョンのs3aはAbstractFileSystemとして実装されていないから、という事のようです。(良くわかっていないので、嘘かもしれないが。)[こちら](https://github.com/Aloisius/hadoop-s3a/blob/master/README.md)に差し替えることで行けるっぽいけど、試していません。

# サーバの準備
すべてのサーバで実行する形になります。

## CentOS 7.2サーバの準備
まずは、CentOS7.2のサーバを準備してください。IDCF Cloudであれば、テンプレートから5台起動し、yum updateしてください。

## JAVAのインストール
次に、Javaのインストールを行います。jpsコマンドを使いたいので、Develも入れましょう。

```:openjdkインストール
# yum install java-1.7.0-openjdk java-1.7.0-openjdk-devel
```

##  hadoopの展開
`/usr/local`へ展開しておきます。また、`/usr/loca/hadoop`へリンクを張っておきます。

```
# cd /usr/local
# tar zxvf ~/hadoop-2.7.2.tar.gz
# ln -s hadoop-2.7.2 hadoop
```

## s3a関連jarファイルのコピー
HDFSの代わりにIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)を利用するためにはs3a(AWS S3)のモジュールを利用します。これらをhdfsのlibディレクトリへコピーしておきます。

```
# cd /usr/local/hadoop-2.7.2/share/hadoop/tools/lib/
# cp -a hadoop-aws-2.7.2.jar aws-java-sdk-1.7.4.jar jackson-core-2.2.3.jar jackson-databind-2.2.3.jar jackson-annotations-2.2.3.jar /usr/local/hadoop-2.7.2/share/hadoop/hdfs/lib/
```

## 環境変数の設定
JAVA_HOME, HADOOP_CONF_DIR, HADOOP_CLASSPATHを設定します。

```bash:環境変数の設定
# vi /usr/local/hadoop/etc/hadoop/hadoop-env.sh

# The java implementation to use.
#export JAVA_HOME=${JAVA_HOME}
export JAVA_HOME=/usr/lib/jvm/jre

# The jsvc implementation to use. Jsvc is required to run secure datanodes
# that bind to privileged ports to provide authentication of data transfer
# protocol.  Jsvc is not required if SASL is configured for authentication of
# data transfer protocol using non-privileged ports.
#export JSVC_HOME=${JSVC_HOME}

#export HADOOP_CONF_DIR=${HADOOP_CONF_DIR:-"/etc/hadoop"}
export HADOOP_CONF_DIR=/usr/local/hadoop/etc/hadoop
```

## sshの設定
localhostへパスフレーズ無しでsshログイン出きるように、パスフレーズ無しのKeyを作り、authorized_keysへ登録します。

```:パスフレーズ無しのKeyを作り、authorized_keysへ登録する。
# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/tmp/id_rsa.
Your public key has been saved in /var/tmp/id_rsa.pub.
The key fingerprint is:
c4:38:39:76:d4:c3:5f:d1:29:60:84:a0:32:1f:ea:dc root@hdp1.csdidcfcloud.internal
The key's randomart image is:
+--[ RSA 2048]----+
|       .ooo+. .o.|
|      .= .=  ....|
|   o o* +  o ..  |
|    =..=    .    |
|   . .  S        |
|  o .            |
|   o E           |
|                 |
|                 |
+-----------------+

# cat .ssh/id_rsa.pub >> .ssh/authorized_keys

# ssh localhost    <= パスフレーズ聞かれずにloginできればOK
```

## 確認
```bash:確認
# hadoop version
Hadoop 2.7.2
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r b165c4fe8a74265c792ce23f546c64604acf0e41
Compiled by jenkins on 2016-01-26T00:08Z
Compiled with protoc 2.5.0
From source with checksum d0fda26633fa762bff87ec759ebe689c
This command was run using /usr/local/hadoop-2.7.2/share/hadoop/common/hadoop-common-2.7.2.jar
```

## Hadoopの設定
基本的には、すべてのサーバで同様の設定を行います。ここでは、クライアントとなるServer6でも同様の設定をしてください。

* `core-site.xml` : 全体的な設定
* `hdfs-site.xml` : NameNode, DataNodeの設定
* `yarn-site.xml` : ResourceManager, NodeManagerの設定
* `mapred-site.xml` : MapReduce Applications, MapReduce JobHistory Serverの設定

これらは、`/usr/local/hadoop/etc/hadoop/`配下に配置してください。(デフォルトで、空の設定ファイルが存在するはずです。)

### core-site.xml
```xml:core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://hdp1.csdidcfcloud.internal:9000</value>
    </property>
    <property>
      <name>fs.s3a.access.key</name>
      <value>XXXXXXXXXXXXXXXXXXXX</value>
    </property>
    <property>
      <name>fs.s3a.secret.key</name>
      <value>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</value>
    </property>
    <property>
      <name>fs.s3a.endpoint</name>
      <value>ds.jp-east.idcfcloud.com</value>
    </property>
</configuration>
```

* `fs.defaultFS`にはデフォルトのFSを指定します。NameNodeを指定することになります。IDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)を指定したいところですが、前述の通り、MapReduce Job History Serverが起動できなくなります。また、NameNodeやDataNodeもhdfsを指定する必要があります。(これは当たり前かもしれないが。)
* `fs.s3a.access.key`,`fs.s3a.secret.key`にはIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)のAPI Keyを指定します。
* `fs.s3a.endpoint`にはIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)のエンドポイントを指定します。

### hdfs-site.xml
```xml:hdfs-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:///var/lib/dfs/name</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:///var/lib/dfs/data</value>
    </property>
</configuration>
```

* `dfs.namenode.name.dir`にはNameNodeのデータが格納されるディレクトリを指定します。NameNode上で指定したディレクトリにファイルが作成されることになります。
* `dfs.datanode.data.dir`にはDataNodeのデータが格納されるディレクトリを指定します。DataNode上で指定したディレクトリにファイルが作成されることになります。

### yarn-site.xml
```xml:yarn-site.xml
<?xml version="1.0"?>

<configuration>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>hdp2.csdidcfcloud.internal</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.web-proxy.address</name>
        <value>hdp2.csdidcfcloud.internal:9046</value>
    </property>
</configuration>
```

* `yarn.resourcemanager.hostname`にはResourceManagerのアドレスを記載します。DataNodeが接続しに行く先になります。
* `yarn.nodemanager.aux-services`には、まだ良くわかっていないのですが、Map ReduceアプリケーションにはShuffle serviceなるものが必要らしい。その設定。
* `yarn.web-proxy.address`はWebAppProxyの為の設定で、アドレスを記載します。(すいません、WebAppProxyも良くわかってない。)

### mapred-site.xml
```xml:mapred-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>
```

* `mapreduce.framework.name`はMapReduceの処理にYARNを使うという設定(だと思う。)

## Hadoop Clusterの起動
### hdfsのフォーマット
Server1で実行。(フォーマットはNameNodeの処理なので。)
hdfsは最初にフォーマットする必要があります。

```
# hdfs namenode -format
16/04/11 11:35:55 INFO namenode.NameNode: STARTUP_MSG:
/************************************************************
STARTUP_MSG: Starting NameNode
STARTUP_MSG:   host = hdp1.csdidcfcloud.internal/10.13.0.104

<<snip>>

16/04/11 11:35:57 INFO namenode.NameNode: SHUTDOWN_MSG:
/************************************************************
SHUTDOWN_MSG: Shutting down NameNode at hdp1.csdidcfcloud.internal/10.13.0.104
************************************************************/
```

### NameNode, SecondaryNameNodeの起動
Server1で実行。
次にNameNode, SecondaryNameNodeを起動します。

```
# hadoop-daemon.sh  start namenode
starting namenode, logging to /usr/local/hadoop-2.7.2/logs/hadoop-root-namenode-hdp1.csdidcfcloud.internal.out

# hadoop-daemon.sh  start secondarynamenode
starting secondarynamenode, logging to /usr/local/hadoop-2.7.2/logs/hadoop-root-secondarynamenode-hdp1.csdidcfcloud.internal.out

# jps
16813 SecondaryNameNode
16723 NameNode
```

### DataNodeの起動
Server3,4,5,で実行。
次に、DataNodeを起動します。

```
# hadoop-daemon.sh start datanode
starting datanode, logging to /usr/local/hadoop-2.7.2/logs/hadoop-root-datanode-hdp3.csdidcfcloud.internal.out

# jps
9349 DataNode
```

これで、HDFSのデーモンであるNameNodeとDataNodeが起動できたので、HDFSとしては利用可能です。

### ResourceManagerの起動
Server2で実行。
次にResourceManagerを起動します。

```
# yarn-daemon.sh start resourcemanager
starting resourcemanager, logging to /usr/local/hadoop-2.7.2/logs/yarn-root-resourcemanager-hdp2.csdidcfcloud.internal.out

# jps
20402 ResourceManager
```

### NodeManagerの起動
Server3,4,5,で実行。
次にNodeManagerを起動します。

```
# yarn-daemon.sh start nodemanager
starting nodemanager, logging to /usr/local/hadoop-2.7.2/logs/yarn-root-nodemanager-hdp3.csdidcfcloud.internal.out

# jps
9349 DataNode
9477 NodeManager
```

### WebAppProxyの起動
Server2で実行。
次にWebAppProxyを起動します。

```
# yarn-daemon.sh start proxyserver
starting proxyserver, logging to /usr/local/hadoop-2.7.2/logs/yarn-root-proxyserver-hdp2.csdidcfcloud.internal.out

# jps
20681 WebAppProxyServer
20402 ResourceManager
```

### MapReduce JobHistory Serverの起動
Server2で実行。
次にMapReduce JobHistory Serverを起動します。

```
# mr-jobhistory-daemon.sh start historyserver
starting historyserver, logging to /usr/local/hadoop-2.7.2/logs/mapred-root-historyserver-hdp2.csdidcfcloud.internal.out

# jps
20681 WebAppProxyServer
20756 JobHistoryServer
20402 ResourceManager
```

### 全デーモン
これで、全てのデーモンをすべてのサーバで起動しました。Hadoop Clusterとして動いているはずです。

```:Server1
# jps
16813 SecondaryNameNode
16723 NameNode
```

```:Server2
# jps
20402 ResourceManager
20681 WebAppProxyServer
20756 JobHistoryServer
```

```:Server3
# jps
9349 DataNode
9477 NodeManager
```

```:Server4
# jps
19123 DataNode
19243 NodeManager
```

```:Server5
# jps
8726 DataNode
8843 NodeManager
```

### ユーザディレクトリの作成
空のバケットの場合アクセスに失敗しまうようなので、`dummy.txt`などなんでも良いのでファイルを一つ置いておきます。(重要)

どうやら、相対パスを指定した場合、`/user/<username>`がデフォルトで使用されるようですので、あらかじめ作成しておきます。(ただ、今回はs3a://を使うので、これ以降の説明では絶対パスを指定しています。)

```
# hdfs dfs -ls /
Found 2 items
-rw-rw-rw-   1          6 2016-04-11 13:34 /dummy.txt

# hdfs dfs -mkdir /user

# hdfs dfs -mkdir /user/root

# hdfs dfs -ls /user/root

# hdfs dfs -ls /
Found 2 items
-rw-rw-rw-   1          6 2016-04-11 13:34 /dummy.txt
drwxrwxrwx   -          0 1970-01-01 09:00 /user
```

## 動かしてみよう
Server6のClientでサンプルのMapReduceアプリケーションを動かしてみましょう。
このアプリケーションは、指定したディレクトリのファイルのワードをカウントするアプリケーションです。

### サンプルの実行
それでは、ようやくサンプルのワードをカウントのアプリケーションを実行してみましょう。

#### インプットファイルの準備
まだでした。ワードカウントするファイル群をIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)へアップロードします。

```
# hdfs dfs -put /usr/local/hadoop s3a://hadoop-idcf1/user/root/input

# hdfs dfs -ls s3a://hadoop-idcf1/user/root/input/
Found 29 items
-rw-rw-rw-   1       4436 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/capacity-scheduler.xml
-rw-rw-rw-   1       1335 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/configuration.xsl
-rw-rw-rw-   1        318 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/container-executor.cfg
-rw-rw-rw-   1        774 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/core-site.xml
-rw-rw-rw-   1       3670 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/hadoop-env.cmd
-rw-rw-rw-   1       4224 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/hadoop-env.sh
-rw-rw-rw-   1       2490 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/hadoop-metrics.properties
-rw-rw-rw-   1       2598 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/hadoop-metrics2.properties
-rw-rw-rw-   1       9683 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/hadoop-policy.xml
-rw-rw-rw-   1        775 2016-04-11 14:26 s3a://hadoop-idcf1/user/root/input/hdfs-site.xml
-rw-rw-rw-   1       1449 2016-04-11 14:25 s3a://hadoop-idcf1/user/root/input/httpfs-env.sh
-rw-rw-rw-   1       1657 2016-04-11 14:25 s3a://hadoop-idcf1/user/root/input/httpfs-log4j.properties
-rw-rw-rw-   1         21 2016-04-11 14:25 s3a://hadoop-idcf1/user/root/input/httpfs-signature.secret
-rw-rw-rw-   1        620 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/httpfs-site.xml
-rw-rw-rw-   1       3518 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/kms-acls.xml
-rw-rw-rw-   1       1527 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/kms-env.sh
-rw-rw-rw-   1       1631 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/kms-log4j.properties
-rw-rw-rw-   1       5511 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/kms-site.xml
-rw-rw-rw-   1      11237 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/log4j.properties
-rw-rw-rw-   1        951 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/mapred-env.cmd
-rw-rw-rw-   1       1383 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/mapred-env.sh
-rw-rw-rw-   1       4113 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/mapred-queues.xml.template
-rw-rw-rw-   1        758 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/mapred-site.xml.template
-rw-rw-rw-   1         10 2016-04-11 14:24 s3a://hadoop-idcf1/user/root/input/slaves
-rw-rw-rw-   1       2316 2016-04-11 14:23 s3a://hadoop-idcf1/user/root/input/ssl-client.xml.example
-rw-rw-rw-   1       2268 2016-04-11 14:23 s3a://hadoop-idcf1/user/root/input/ssl-server.xml.example
-rw-rw-rw-   1       2250 2016-04-11 14:23 s3a://hadoop-idcf1/user/root/input/yarn-env.cmd
-rw-rw-rw-   1       4567 2016-04-11 14:23 s3a://hadoop-idcf1/user/root/input/yarn-env.sh
-rw-rw-rw-   1        690 2016-04-11 14:23 s3a://hadoop-idcf1/user/root/input/yarn-site.xml
```

#### やっと実行

```
# hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar grep s3a://hadoop-idcf1/user/root/input s3a://hadoop-idcf1/user/root/output 'dfs[a-z.]
16/04/11 14:34:52 INFO client.RMProxy: Connecting to ResourceManager at hdp2.csdidcfcloud.internal/10.13.0.5:8032
16/04/11 14:35:03 INFO input.FileInputFormat: Total input paths to process : 29
16/04/11 14:35:03 INFO mapreduce.JobSubmitter: number of splits:29
16/04/11 14:35:03 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1460342739299_0002
16/04/11 14:35:03 INFO impl.YarnClientImpl: Submitted application application_1460342739299_0002
16/04/11 14:35:03 INFO mapreduce.Job: The url to track the job: http://hdp2.csdidcfcloud.internal:9046/proxy/application_1460342739299_0002/
16/04/11 14:35:03 INFO mapreduce.Job: Running job: job_1460342739299_0002
16/04/11 14:35:14 INFO mapreduce.Job: Job job_1460342739299_0002 running in uber mode : false
16/04/11 14:35:14 INFO mapreduce.Job:  map 0% reduce 0%
16/04/11 14:35:30 INFO mapreduce.Job:  map 7% reduce 0%
16/04/11 14:35:50 INFO mapreduce.Job:  map 10% reduce 0%

<< snip >>
```

```
# hdfs dfs -cat s3a://hadoop-idcf1/user/root/output/part-r-00000
6       dfs.audit.logger
4       dfs.class
3       dfs.server.namenode.
2       dfs.period
2       dfs.audit.log.maxfilesize
2       dfs.audit.log.maxbackupindex
1       dfsmetrics.log
1       dfsadmin
1       dfs.servers
1       dfs.file
```

# 雑感とメモ
* とりあえず、動いたって感じ。Hadoop難しい。
* MapReduce Job History Serverを起動する為にはHDFSが必要。
* MapReduce Applicationを動かす時にテンポラリの書き込み？で、HDFSが必要っぽい。
* これらは、2.8.0で解消されそうなので2.8.0ではNameNodeとDataNodeを停止できるかもしれない。(HDFS使わずにIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)つかうなら、是非止めたい。)
* s3a経由でIDCFクラウドの[オブジェクトストレージ](http://www.idcf.jp/cloud/storage/)を利用すると、かなり遅い気がする。設定で隠蔽できるのかは、まだわからない。
* そもそもHDFSの代わりにS3ってどうなんでしょう？HDFSで大量のサーバ束ねるのよりも、遅くなってしまうのではないのかな？