Hadoopでクラスタを作ってみたときの備忘録

ローカル環境でHadoopのクラスタを作ってみた時の備忘録。

# 目的
いまどき、ローカルでHadoop環境を構築するというケースは少ないと思う。今回は、Amazon EMRなどのクラウドのビックデータ系のマネージドサービスのありがたみを肌で感じたくて、あえてローカルに環境を作ってみた。

# 環境構築の進め方
基本的は本家のチュートリアルを参照しながら進める。

https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/ClusterSetup.html


他のチュートリアルもそうだが、ストレージの分散と処理の分散の仕組みがごちゃ混ぜになって手順が進むので分かりにくくなっていると感じる。今回は明確にステップを分けて構築した。



# 1. ノードの準備
ローカルにある３台のPCをノードにする。1台をマスターノード、残り2台をワーカーノードとする。

## 1.1. 全ノード共通の手順
全ノードに対して共通で下記を行う。これによりHadoopの実行とホスト名を使ったノード間通信が可能になる。

### [共通]Javaのインストール
OpenJDK 1.8をインストールする。

下記のように環境変数を設定する。

- 環境変数JAVA_HOMEにOpenJDK 1.0のインストールディレクトリを設定する

### [共通] Hadoopのインストール
Hadoopの本家サイトからバイナリをダウンロードして、任意のディレクトに展開する。

https://hadoop.apache.org/releases.html

下記のように環境変数を設定する。これでhadoopの各種コマンド、スクリプトがパス指定なしで実行できるようになる。

- 環境変数HADOOP_HOMEにHadoopの展開先のディレクトリを設定する
- 環境変数HADOOP_HOMEに`$HADOOP_HOME/etc/hadoop`を設定する
- 環境変数PATHに`$HADOOP_HOME/bin`と`$HADOOP_HOME/sbin`を追加する

### [共通] ネットワーク設定
お互いにホスト名で通信ができるように/etc/hostsの内容に全てのノードのホスト名とIPアドレスを追記する。

## 1.2. マスタノードの設定
マスターノードからワーカーノードに対してSSH接続するための設定を行う。
### [マスターノード] SSHキーの生成
マスターノードにhadoopを実行するユーザでログインし、下記のコマンドを実行して、SSH接続のための秘密鍵と公開鍵を生成する。
ここで作った鍵はマスターノードからワーカーノードにSSHログインするときに使う。

```
% ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
```

## 1.3. ワーカーノードの設定
マスターノードからワーカーノードに対してSSH接続するための設定を行う。
### [ワーカーノード] SSHキーの登録
ワーカーノードにhadoopを実行するユーザでログインし、マスターノードで生成した公開鍵(id_rsa.pub)を`~/.ssh/authorized_keys`に追記する。
authorized_keysのパーミッションは600以下にしておくこと。
これでマスターノードから秘密鍵を使ってアクセスが来た時に、パスワードなしでログインさせることができるようになる。

## 1.4. ノードの動作確認
### hadoopの実行
マスターノードとワーカーノードのそれぞれで下記のコマンドを実行して、hadoopのバージョンが表示されることを確認する。

```
% hadoop version
Hadoop 3.3.1
```

### SSHログイン
マスターノードからワーカーノードに対して`/etc/hosts`に追記したホスト名を指定して、パスワードなしでSSH接続できることを確認する。
パスワードが聞かれる場合は、大抵は公開鍵や秘密鍵ファイル及びそれを保存しているディレクトリのパーミッションが間違っているので見直す。

ここまでで、Hadoopのノードとして動く３台のPCのそれぞれでhadoopコマンドが実行できるようになり、マスターノードからワーカーノードへのSSH接続も可能となった。
次に、各ノードに対して分散ストレージ(HDFS)や、分散処理(yarn)の設定を追加していく。


# 2. HDFSによる分散ストレージ環境の構築
## 2.1. マスターノードの設定(namenode)
HDFSにおけるマスターノードの役割はNameNodeプロセスの実行である。

### [マスターノード] HDFSの設定(hdfs-site.xml)
NameNodeに関する設定は、`$HADOOP_HOME/etc/hadoop/hdfs-site.xml`で行う。

dfs.replicationにはデータのレプリケート数を設定する。（datanodeの数ではない）
dfs.namenode.name.dirにはnamenodeがHDFSのメタ情報を保存するためのディレクトリを設定する。
事前にhadoopユーザのが読み書きできるディレクトリを生成しておく。

```xml
<configuration>
	<property>
		<name>dfs.replication</name>
		<value>2</value>
	</property>
	<property>
		<name>dfs.namenode.name.dir</name>
		<value>file:///Users/hadoop/namenode</value>
	</property>
</configuration>
```

### [マスターノード] Hadoopデフォルトファイルシステムの設定(core-site.xml)
`$HADOOP_HOME/etc/hadoop/core-site.xml`で利用するファイルシステムのタイプを設定しておく。
<マスターノード>の部分は、マスターノードとして動かすPCのホスト名を設定する。

```xml
<configuration>
	<property>
		<name>fs.defaultFS</name>
		<value>hdfs://<マスターノード>:9000</value>
	</property>
</configuration>
```

### [マスターノード] HDFSのフォーマット
ファイルシステム(HDFS)をフォーマットする。実行後、dfs.namenode.name.dirに指定したディレクトリを見るとファイルが生成されているのが確認できる。

```
% hdfs namenode -format
```

### [マスターノード] HDFS namenodeの起動
namenodeを起動する。起動するとNameNodeプロセスが動作しているのが確認できる。

```
% hdfs --daemon start namenode
% jps
71200 Jps
70963 NameNode
```

また、`http://<マスターノードのIP>:9870`にアクセスすると、HDFSのWeb管理画面にアクセスできる。Datanodesタブでdatanodeのリストを確認できるが、まだ未登録のなので表示件数は0件。

## 2.2. ワーカーノードの設定(datanode)
HDFSにおけるワーカーノードの役割はDataNodeプロセスの実行である。

### [ワーカーノード] HDFSの設定(hdfs-site.xml)
DataNodeに関する設定は、$HADOOP_HOME/etc/hadoop/hdfs-site.xmlで行う。
dfs.replicationにはデータのレプリケート数を設定する。（datanodeの数ではない）
dfs.datanode.data.dirにはdatanodeがHDFSのデータを保存するためのディレクトリを設定する。
事前にhadoopユーザのが読み書きできるディレクトリを生成しておく。


```xml
<configuration>
	<property>
		<name>dfs.replication</name>
		<value>2</value>
	</property>
	<property>
		<name>dfs.datanode.data.dir</name>
		<value>file:///Users/hadoop/datanode</value>
	</property>
</configuration>
```

### [ワーカーノード] Hadoopのデフォルトファイルシステムの設定(core-site.xml)
core-site.xmlはマスターノードと同じ設定にしておく。
これでワーカーノード(datanode)はマスターノード(namenode)に接続先を認識できる。

### [ワーカーノード] HDFS datanodeの起動
datanodeを起動する。起動するとDataNodeプロセスが動作しているのが確認できる。
また、dfs.datanode.data.dirで指定したディレクトリにファイルが生成されているのも確認できる。

```
$ hdfs --daemon start datanode
$ jps
13142 DataNode
13161 Jps
```

## 2.3. HDFSの動作確認
### HDFSの状態確認
マスターノードで下記のコマンドを実行する。ワーカーノード(datanode)が認識されていることを確認する。

```
$ hdfs dfsadmin -report
```

`http://<マスタノードのIP>:9870`でも同様にワーカーノードが認識されていることを確認する。

### ディレクトリとファイルの作成
マスターノードでhdfsコマンドを使ってHDFSにディレクトリとファイルを作成してみる。

```
$ hdfs dfs -mkdir /user
$ hdfs dfs -mkdir /user/hadoop
$ hdfs dfs -mkdir input
$ echo 'hello' > hello.txt
$ hdfs dfs -put hello.txt input
$ hdfs dfs -ls input
$ hdfs dfs -cat input/hello.txt
```


### データレプリケーションの確認
dfs.replicationを2に設定したので、データは２箇所に冗長化されているはず。そのため、ワーカーノード(datanode)を一つ落としても、もう一つのワーカーノードからデータが取れるはず。

ワーカーノードのうちの1台でdatanodeを停止させる。

```
$ hdfs --daemon stop datanode
```

マスターノードでファイルを読んでみる。読める。

```
$ hdfs dfs -cat input/hello.txt
```

さらにもう一台のワーカーノードでもdatanodeを停止すると、ファイルが読めなくなる。

# 3. yarnによる分散処理環境の構築

## 3.1. マスターノードの設定(resourcemanager)
yarnにおけるマスターノードの役割はResourceManagerプロセスの実行である。

### [マスターノード] yarnの設定(yarn-site.xml)
yarnに関する設定は、`$HADOOP_HOME/etc/hadoop/yarn-site.xml`で行う。
yarn.resourcemanager.hostnameにマスターノードのホスト名を設定する。

```xml
<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_HOME,PATH,LANG,TZ,HADOOP_MAPRED_HOME</value>
    </property>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>マスターノードのホスト名</value>
    </property>
</configuration>
```

### [マスターノード] MapReduceの設定(mapped-site.xml)
yarnの上で動かすMapReduce処理の設定は`$HADOOP_HOME/etc/hadoop/mapred-site.xml`で行う。
mapreduce.application.classpathには、MapReduceの実装クラスが含まれるjarへのパスを設定する。

```xml
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <name>mapreduce.application.classpath</name>
        <value>/Users/hadoop/hadoop-3.3.1/share/hadoop/mapreduce/*</value>
    </property>
</configuration>
```

### [マスターノード] ResourceManagerの起動
ResourceManagerを起動する。起動すると、ResourceManagerプロセスが動作しているのが確認できる。

```
% yarn --daemon start resourcemanager
% jps
70963 NameNode
75716 Jps
74488 ResourceManager
```

ワーカーノードはまだない。

```
$ yarn node --list 
Total Nodes:0
         Node-Id	     Node-State	Node-Http-Address	Number-of-Running-Containers
```

また、`http://<マスターノードのIP>:8088`にアクセスすると、ResourceManagerのWeb管理画面を操作できる。

## 3.2. ワーカーノードの設定(nodemanager)
yarnにおけるマスターノードの役割はNodeManagerプロセスの実行である。

### [ワーカーノード] yarnとMapReduceの設定(yarn-site.xml, mapped-site.xml)
yarnとMapRecudeの設定はマスターノードと同じものを使う。

### [ワーカーノード] NodeManagerの起動
NodeManagerを起動する。起動すると、NodeManagerプロセスが動作しているのが確認できる

```
$ yarn --daemon start nodemanager
$ jps
56209 Jps
56178 NodeManager
53845 DataNode
```

## 3.3. yarnの動作確認
### ResourceManagerの状態確認

マスターノードで下記のコマンドを実行して、2つのノードが認識されていることを確認する。

```
% yarn node -list
Total Nodes:2
         Node-Id	     Node-State	Node-Http-Address	Number-of-Running-Containers
    worker1:50552	        RUNNING	      worker1:8042	                           0
    worker2:54175	        RUNNING	      worker2:8042	                           0
```

### MapReduceのジョブを実行する。
inputフォルダにある全てのファイルの単語の数をかぞえるジョブを実行する。

```
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.1.jar wordcount input output
```

結果を確認。

```
% hdfs dfs -cat output/part-r-00000
```

実行中のステータスは下記で確認。

```
% yarn node -list
Total Nodes:2
         Node-Id	     Node-State	Node-Http-Address	Number-of-Running-Containers
    worker1:50552	        RUNNING	      worker1:8042	                           8
    worker2:54175	        RUNNING	      worker2:8042	                           4
```

実行中のログは`$HADOOP_HOME/logs/`に出力される。

# 4. S3による分散ストレージ環境の構築
HDFSの代わりにS3を使ってみる。

## 4.1. 全ノード共通の設定 (core-site.xml)
core-site.xmlでfs.defaultFSにS3バケットのURLを指定する。プロトコルはs3a。
fs.s3a.access.keyとf2.s3a.secret.keyにS3へのアクセス権をもったIAMユーザのキー情報を設定する。

```xml
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>s3a://<s3バケット名>/value>
    </property>
    <property>
        <name>fs.s3a.access.key</name>
	<value></value>
    </property>
    <property>
       <name>fs.s3a.secret.key</name>
        <value></value>
    </property>
</configuration>
```

## HADOOP_CLASSPATHの設定
環境変数HADOOP_CLASSPATHにs3aにアクセスするためのjarを設定する。
これでhadoopコマンド実行時にこのjarの参照されるようになる。

```
$ export HADOOP_CLASSPATH=$HADOOP_HOME/share/hadoop/tools/lib/*
```

## s3への接続確認
hadoop fsコマンドを使ってs3にファイルシステムとしてアクセスできることを確認する。

```
$ hadoop fs -mkdir /user
$ hadoop fs -mkdir /user/hadoop
$ hadoop fs -mkdir /user/hadoop/input
$ hadoop fs -put ./hello.txt input
```

## yarnクラスパスの設定
yarn実行時のクラスパスにs3aにアクセスするためのjarを追加する。
yarn-site.xmlに追加してもうまくいかなかったので、泣く泣くyarnのライブラリが入っているディレクトリに下記の二つのjarをコピーした。

```
$ cp $HADOOP_HOME/share/hadoop/tools/lib/hadoop-aws-3.3.1.jar $HADOOP_HOME/share/hadoop/yarn/lib
$ cp $HADOOP_HOME/share/hadoop/tools/aws-java-sdk-bundle-1.11.901.jar $HADOOP_HOME/share/hadoop/yarn/lib
```

## 4.2. yarnの動作確認
HDFSの代わりにS3を使うので、namenode、datanodeは起動せず、yarnのResourceManagerとNodeManagerだけを起動すればOK。