Hadoop 3.2.1 を完全分散モードで動かすためのメモ

# はじめに
会社で使っているHadoop環境はサブスクによるもので，値段的にコストが高いので，自社でできたら嬉しくない？という話が出たそうで，自分が構築することになったのでメモとして残す．結論としては，できそうではあるけど，まあまあめんどくさいので，サブスクを使ったほうが良さそうではある．

構築に際して，導入が楽ちんで無料の[clouderaの公式docker_CDH5.8](https://hub.docker.com/r/cloudera/quickstart/)もあったが．対応OSがubuntu14.04と古く，後々セキュリティの問題が騒がれそうなのでやめた．あまり良く調べていないが，おそらくCDH6以降はサブスクになっていると思われ，それを使ってしまうと本松転倒なので，素のhadoopを入れることにした．
なるべくコマンドを打っていくだけでできるようにしたつもりだが，何か問題があればググって解決してほしい．EC2上の話で書いているが，他の環境でも同じような作業になるかと．


# 設置する環境
将来的にはオンプレでやるにしても，性能検証等々のためにAWSのEC2上で構築することにした．構成は簡単にmaster, slave1, slave2, slave3, slave4の５台とした．ノードタイプはt2.largeのメモリは8GBくらい．同じVPC内，同じサブネット内に，以下のようにサーバを立てた（合計３台でもうまくいっていた）．

| Private IP | NAME |
| ----- | --- |
| 10.0.1.120 | master |
| 10.0.1.23 | slave1 | 
| 10.0.1.51 | slave2 |
| 10.0.1.57 | slave3 |
| 10.0.1.8 | slave4 |

hadoopを実行するためには，これらは双方向でパスワード無しのsshができる必要がある．ただ，一方向ごとに鍵を生成すると面倒なので，masterで生成した秘密鍵と公開鍵を全てで使い回す（後述）．場合によってはセキュリティ的にアレな可能性があるので，自己責任でどうぞ．
ちなみに最近はあんまりmaster/slaveとは言わずleader/followerとかいうらしいですね．

# 各サーバで行うこと

基本的にmaster, slaveでやることはほとんど変わらない．重要なことは，各マシンに「hadoop」というユーザを作ることである．新しくユーザを作らずに，デフォルトの「ec2-user」で押し切ることもできると思うが，本投稿ではhadoopユーザの.ssh以下のファイルをそこそこ乱暴（？）な方法で編集するので，今回はhadoopユーザを推奨しておく．

## /etc/hostsの編集
はじめに，とりあえず全てのマシンで/etc/hostsを編集し，以下のように保存しておく．sudo vi /etc/hostsとかで．

~~~python:/etc/hosts
10.0.1.120 master
10.0.1.23  slave1
10.0.1.51  slave2
10.0.1.57  slave3 
10.0.1.8 slave4 
~~~
自分自身もいれる．Don't think, feel.


## hadoopユーザの作成と設定
各マシンのグローバルIPなどからsshでec2-userにログインし，

~~~
sudo useradd hadoop
sudo passwd hadoop # やっといたほうがあとあと便利
~~~

適当にパスワードを入力したのち，以下のコマンドで開いたエディタで

~~~
sudo visudo
~~~

hadoop ALL=(ALL) NOPASSWD: ALL

を適当な行に追加する（面倒なのでルート権限）．

また．

~~~
sudo vi /etc/ssh/sshd_config
~~~

で開いたファイルに

PubkeyAuthentication yes

を追加する（同内容でNoがある場合はコメントアウトする）．

hadoopユーザにsudo実行権限を与え，リブートする．

~~~
sudo usermod -aG wheel hadoop # ubuntu系は別のやつ
sudo reboot # いらないかも
~~~

もう一度sshを繋ぎ，hadoopユーザにログインする．

~~~
sudo su - hadoop
~~~



## hadoopユーザでの作業
まずmasterで鍵を生成し，他のマシンでコピーする．先述のように，キーペアは一個しか作らない．
sshが入っていなければ適当なコマンドで入れる（省略）．~/.sshがなければ作成する（省略）．

### 鍵の生成（masterのみ）

~~~
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
~~~

現状でauthorized_keysは

~~~shell:~/.ssh/authorized_keys
ssh-rsa AAAAB_TOTEMO_NAGAI_MOJIRETSU hadoop@ip-10-0-1-120.ap-northeast-1.compute.internal
~~~

のようになっていると思われるが，viで以下のように編集する．

~~~shell:~/.ssh/authorized_keys
from="*" ssh-rsa AAAAB_TOTEMO_NAGAI_MOJIRETSU
~~~

こうすることで，どのようなホストからssh要求が来ても，AAAAB_TOTEMO_NAGAI_MOJIRETSUの鍵で認証するので使い回しができる．
hadoop@hoge~の部分はコメントだとsshdのmanに書いてあったためいらない．

同様に，~/.ssh/configを作成し，次のように記述する．

~~~python:~/.ssh/config
HOST *
    User hadoop
    IdentityFile    ~/.ssh/id_rsa
    StrictHostKeyChecking no
~~~
こうすることで，どのホストにつなげるときも，id_rsaを使うので，使い回しができる．
仕上げに以下を実行しておく．

~~~
chmod go-w ~
chmod go-w ~/.ssh
chmod go-w ~/.ssh/authorized_keys
chmod go-w ~/.ssh/config
chmod 600 ~/.ssh/id_rsa
~~~

### slaveでの鍵関連の操作

各slaveでは，~/.ssh/　にmasterと同じものをおけばよい．
つまり，コピペするなどして id_rsa, id_rsa.pub, authorized_keys, configを作ればよい．
そのあと以下を実行しておく．

~~~
chmod go-w ~
chmod go-w ~/.ssh
chmod go-w ~/.ssh/authorized_keys
chmod go-w ~/.ssh/config
chmod 600 ~/.ssh/id_rsa
~~~

## hadoopのダウンロード

各マシンのhadoopユーザで下記を実行

~~~
cd
wget https://downloads.apache.org/hadoop/common/stable/hadoop-3.2.1.tar.gz
tar -zxvf hadoop-3.2.1.tar.gz hadoop-3.2.1
~~~

とりあえず実体だけ落とした状態．あとで使う環境変数を.bashrcに書く．

~~~shell:~/.bashrc
export HADOOP_HOME=/home/hadoop/hadoop-3.2.1
export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
~~~

一応これも．

~~~
source ~/.bashrc
~~~

### Javaのインストール
Javaがないと動かないのでインストールする．なお3.2.1ではJDK1.8.0でないと動かないらしい（私調べ）．

~~~
sudo yum install java-1.8.0-openjdk
sudo yum install java-1.8.0-openjdk-devel
~~~

パスを．hadoop-3.2.1/etc/hadoop/hadoop-env.shに記述する（多分.bashrcでもいいとは思うが，公式はこっちに書けとのこと）

~~~shell:~/hadoop-3.2.1/etc/hadoop/hadoop-env.sh
vi ~/hadoop-3.2.1/etc/hadoop/hadoop-env.sh
~~~

開いたファイルに以下を記述（参考までに3.2.1では54行目くらい）．

~~~shell
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk
~~~


## hadoopの設定

各マシンのhadoopユーザで~/hadoop-3.2.1/etc/hadoop/以下にある設定ファイルをいじっていく（core-site.xml, hdfs-site.xml, mapred-site.xml, yarn-site.xml)．
一応[公式](https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/ClusterSetup.html)に説明がある．結構動作が不安定だったので，とりあえず動いた実績のある設定を載せておく．

~~~xml:core-site.xml
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://master:9000</value>
    </property>
</configuration>
~~~

~~~xml:hdfs-site.xml
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>2</value>
    </property>
    <property>
        <name>dfs.datanode.max.xcievers</name>
        <value>4096</value>
    </property>
</configuration>
~~~

~~~xml:mapred-site.xml
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <name>mapreduce.application.classpath</name>
        <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
    </property>
</configuration>
~~~

~~~xml:yarn-site.xml
<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
            <name>yarn.nodemanager.aux-services.mapreduce_shuffle.class</name>
            <value>org.apache.hadoop.mapred.ShuffleHandler</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_MAPRED_HOME</value>
    </property>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>master</value>
    </property>
</configuration>
~~~

### masterだけはworkerファイルもいじる
~/hadoop-3.2.1/etc/hadoop/workersに以下を記述

~~~
slave1
slave2
slave3
slave4
~~~

以上で準備完了

# hadoopクラスタを動かしてみる

上から順に実行．クラスタ名として，cluster_0とした．

~~~
hdfs namenode -format cluster_0
start-dfs.sh
start-yarn.sh
~~~

ブラウザでhttp://XX.XX.XX.XX:9870 に接続するとHadoopコンソールが見れる．EC2の場合はインバウンドルールの編集を忘れずに．
以下のようなものが見れたりします（この画像ではslave4がいないが気にしないでほしい）．

![スクリーンショット 2020-09-29 17.13.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170560/9b4d7a0a-55ac-e824-cccd-1618ce8760ef.png)

## ワードカウントをしてみる

公式ドキュメントはあまりにも雑で「Do mapreduce job」的なことしか書いていないので，
[こちらの記事](https://qiita.com/Esfahan/items/bdd254dccf29da22cc7c)を参考にWord Countをしてみる．適当にでかいファイルを作って，ワードカウントする．
                                      
~~~
cd ~/hadoop-3.2.1
for i in `seq 10000`; do echo -n 'the company I belong to is the best '>> sample.txt; done
hadoop fs -mkdir /wc_test
hadoop fs -put sample.txt /wc_test
hadoop fs -ls /wc_test # HDFSにsample.txtがあるか確認
hadoop jar ~/hadoop-3.2.1/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.2.1.jar wordcount /wc_test /output_wc_test
hadoop fs -ls /output_wc_test
hadoop fs -cat /output_wc_test/part-r-00000
~~~

結果は

~~~
2020-09-30 02:59:49,426 INFO sasl.SaslDataTransferClient: SASL encryption trust check: localHostTrusted = false, remoteHostTrusted = false
I	10000
belong	10000
best	10000
company	10000
is	10000
the	20000
to	10000
~~~      

ちゃんとカウントできている．
なお，jobの状態はhttp://XX.XX.XX.XX:8088 で確認可能．

![スクリーンショット 2020-09-30 10.17.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170560/2cb2a61c-1f49-e382-da96-027b8bc99cb3.png)

終了は以下のコマンド．

~~~
stop-all.sh
~~~


# TIPS
なんか動かなくなったらとりあえず

~~~
sudo rm -rf /tmp/*
~~~
を全てのマシンで実行するとよいことがあるかも．
あとは以下も有効な場合あり．

~~~
hadoop fs -ls /
>>> /hoge
hadoop fs -rf -r /hoge
~~~

セーフモードがなんとかと言われた場合，復帰には

~~~
hadoop dfsadmin -safemode leave
~~~

を使えばよい．

# おわりに
公式ドキュメントには例を書いて欲しいなあと思った．あと，全てのマシンに書かなきゃいけない処理とかは，なんらかのスクリプトで自動化したいと思った．サーバを増やすと，ちゃんと計算時間が早くなるかどうかを検証中．