Debian 11によるHadoop完全分散モード構築

# 概要
Debian 11でCluster ModeのHadoop環境を構築した際のメモです。

# 環境
Debian 11 bullseye

# ノード構成
3台で構成しますが、1台はMaster兼Workerとして利用します。
|Hostname|IP address|NameNode|DataNode|ResourceManager|NodeManager|
|:-------|:-------|:-------|:-------|:-------|:-------|
|kyoto|192.168.10.111|Yes|Yes|Yes|Yes|
|tokyo|192.168.10.112|No|Yes|No|Yes|
|osaka|192.168.10.113|No|Yes|No|Yes|

# Single Clusterによる構築

オフィシャルドキュメントに従って、まずはSingle Clusterで動作を確認します。

https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/SingleCluster.html

実際の環境構築は、オフィシャルドキュメントに加えて以下のサイトを参考にしました。そのため、オフィシャルの奨励から多少ずれてはいますが、動いているので結果OKとしたいと思います。

https://www.rosehosting.com/blog/how-to-install-hadoop-on-debian-11/

## パッケージインストール

まずはJAVAをインストールします。
 
```bash
root@kyoto:~# apt install default-jdk default-jre
```

Hadoopを実行するユーザーhadoopを作成します。オフィシャルドキュメントではhdfsとyarnで別ユーザーを勧められていますが、今回は簡略のために同一ユーザーにします。

```bash
root@kyoto:~# useradd -r hadoop -m -d /opt/hadoop --shell /bin/bash
```

以降はhadoopユーザーで行います。
まず、Hadoopが通信のために利用するsshのために鍵を生成します。

```bash
root@kyoto:~# su - hadoop
hadoop@kyoto:~$ ssh-keygen -t rsa
hadoop@kyoto:~$ cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
```

Hadoopのパッケージを展開します。オフィシャルサイトから最新のバージョンをダウンロードし解凍します。

```bash
hadoop@kyoto:~$ wget https://dlcdn.apache.org/hadoop/common/hadoop-3.4.0/hadoop-3.4.0.tar.gz
hadoop@kyoto:~$ tar -zxvf hadoop-3.4.0.tar.gz -C /opt/hadoop --strip-components=1
```

環境変数を設定します。どこまで本当に必要なのかわかりませんが、参考サイトに従って設定しておきます。

```:/opt/hadoop/.bashrc
export JAVA_HOME=/usr/lib/jvm/java-1.11.0-openjdk-amd64
export HADOOP_HOME=/opt/hadoop
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"
```

```:/opt/hadoop/etc/hadoop/hadoop-env.sh
...
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
```
最後に、NameNodeとDataNodeの保存先を作成しておきます。

```bash
hadoop@kyoto:~$ mkdir -p /opt/hadoop/hadoop_tmp/hdfs/namenode
hadoop@kyoto:~$ mkdir -p /opt/hadoop/hadoop_tmp/hdfs/datanode
```

## サイトの設定

ここからが大変な設定ファイルの編集です。基本的にはオフィシャルドキュメントに沿って設定しますが、そのままでは起動しなかったので、試行錯誤で必要な項目を追加しています。おそらく環境変数等がうまく設定できていないので、正しい解決策はあると思いますが、とりあえず動いているのでこのままにしています。

```XML:/opt/hadoop/etc/hadoop/core-site.xml
<configuration>
  <property>
    <!-- The name of the default file system. -->
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:8020</value>
  </property>
</configuration>
```

```XML:/opt/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
  <property>
    <!-- Default block replication. -->
    <name>dfs.replication</name>
    <value>1</value>
  </property>
  <property>
    <!-- Determines where on the local filesystem the DFS name node should store the name table(fsimage). -->
    <name>dfs.namenode.name.dir</name>
    <value>file:///opt/hadoop/hadoop_tmp/hdfs/namenode</value>
  </property>
  <property>
    <!-- Determines where on the local filesystem an DFS data node should store its blocks. -->
    <name>dfs.datanode.data.dir</name>
    <value>file:///opt/hadoop/hadoop_tmp/hdfs/datanode</value>
  </property>
</configuration>
```

```XML:/opt/hadoop/etc/hadoop/yarn-site.xml
<configuration>
  <property>
    <!-- A comma separated list of services -->
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
</configuration>
```

```XML:/opt/hadoop/etc/hadoop/mapred-site.xml
<configuration>
  <property>
    <!-- The runtime framework for executing MapReduce jobs. -->
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
  <property>
    <!-- CLASSPATH for MR applications. -->
    <name>mapreduce.application.classpath</name>
    <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
  </property>
  <!-- 起動時に以下の設定がないとエラーになったので追加 -->
  <property>
    <name>yarn.app.mapreduce.am.env</name>
    <value>HADOOP_MAPRED_HOME=/opt/hadoop</value>
  </property>
  <property>
    <name>mapreduce.map.env</name>
    <value>HADOOP_MAPRED_HOME=/opt/hadoop</value>
  </property>
  <property>
    <name>mapreduce.reduce.env</name>
    <value>HADOOP_MAPRED_HOME=/opt/hadoop</value>
  </property>
</configuration>
```

## 初期化と起動

```bash
hadoop@kyoto:~$ hdfs namenode -format
```
起動スクリプトを実行します。

```bash
hadoop@kyoto:~$ start-all.sh
```

起動確認は、
- http://192.168.10.111:9870/
- http://192.168.10.111:8088/

を開くことで行います。

## サンプルの実行

オフィシャルドキュメントに書かれているMapReduceのサンプルを実行してみます。

```bash
hadoop@kyoto:~$ hdfs dfs -mkdir -p /user/hadoop
hadoop@kyoto:~$ hdfs dfs -mkdir input
hadoop@kyoto:~$ hdfs dfs -put etc/hadoop/*.xml input
hadoop@kyoto:~$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.0.jar grep input output 'dfs[a-z.]+'
hadoop@kyoto:~$ hdfs dfs -cat output/*
```

エラーなく実行が完了し、outputが表示できれば成功です。

# Cluster Setup (完全分散モード)

Single Clusterでの動作が確認されたら、3台ノードによるCluster Setupに移ります。

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

既にkyotoにはパッケージをインストールしてあるため、残りの2台(tokyo, osaka)にJAVAのインストール、Hadoopパッケージの展開および環境変数の設定を行っておきます。

SSHの鍵についてはkyotoで作成したものを使うため、再生成は不要です。kyotoで作成した~/.ssh/id_rsa.pubをtokyoとosakaの~/.ssh/authorized_keysに追加しておきます。

SSHで接続できることを確認しておきます。

```bash
hadoop@kyoto:~$ ssh tokyo
```

```bash
hadoop@kyoto:~$ ssh osaka
```

いずれの場合でもパスワード入力無しで接続できることを確認します。

## サイトの設定

完全分散モードの設定にするため、再度サイト設定を変更します。本当は個別に設定すべきものもあると思いますが、今回は全てのホスト(kyoto, tokyo, osaka)で同じ設定を行います。

```XML:/opt/hadoop/etc/hadoop/core-site.xml
<configuration>
  <property>
    <!-- The name of the default file system. -->
    <name>fs.defaultFS</name>
    <value>hdfs://kyoto/</value>
  </property>
</configuration>
```

```XML:/opt/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
  <property>
    <!-- Default block replication. -->
    <name>dfs.replication</name>
    <value>3</value>
  </property>
  <property>
    <!-- Determines where on the local filesystem the DFS name node should store the name table(fsimage). -->
    <name>dfs.namenode.name.dir</name>
    <value>file:///opt/hadoop/hadoop_tmp/hdfs/namenode</value>
  </property>
  <property>
    <!-- Determines where on the local filesystem an DFS data node should store its blocks. -->
    <name>dfs.datanode.data.dir</name>
    <value>file:///opt/hadoop/hadoop_tmp/hdfs/datanode</value>
  </property>
  <property>
    <!-- If true (the default), then the namenode requires that a connecting datanode's address must be resolved to a hostname. -->
    <name>dfs.namenode.datanode.registration.ip-hostname-check</name>
    <value>false</value>
  </property>
  <property>
    <!-- The actual address the RPC server will bind to. -->
    <!-- これがないとworkerからmasterへ接続できず、DataNodeが認識されなかった -->
    <name>dfs.namenode.rpc-bind-host</name>
    <value>0.0.0.0</value>
  </property>
</configuration>
```

```XML:/opt/hadoop/etc/hadoop/yarn-site.xml
<configuration>
  <property>
    <!-- The hostname of the RM. -->
    <name>yarn.resourcemanager.hostname</name>
    <value>kyoto</value>
  </property>
  <property>
    <!-- A comma separated list of services -->
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
  <property>
    <!-- The actual address the server will bind to. -->
    <!-- これがないとworkerからmasterへ接続できず、NodeManagerが認識されなかった -->
    <name>yarn.resourcemanager.bind-host</name>
    <value>0.0.0.0</value>
  </property>
</configuration>
```

```XML:/opt/hadoop/etc/hadoop/mapred-site.xml
<configuration>
  <property>
    <!-- The runtime framework for executing MapReduce jobs. -->
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
  <property>
    <!-- MapReduce JobHistory Server IPC host:port -->
    <name>mapreduce.jobhistory.address</name>
    <value>kyoto:10020</value>
  </property>
  <property>
    <!-- CLASSPATH for MR applications. -->
    <name>mapreduce.application.classpath</name>
    <value>$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/*:$HADOOP_MAPRED_HOME/share/hadoop/mapreduce/lib/*</value>
  </property>
  <property>
    <name>yarn.app.mapreduce.am.env</name>
    <value>HADOOP_MAPRED_HOME=/opt/hadoop</value>
  </property>
  <property>
    <name>mapreduce.map.env</name>
    <value>HADOOP_MAPRED_HOME=/opt/hadoop</value>
  </property>
  <property>
    <name>mapreduce.reduce.env</name>
    <value>HADOOP_MAPRED_HOME=/opt/hadoop</value>
  </property>
</configuration>
```

## 初期化と起動

Single ClusterのときにDataNodeを使用してしまったので、DataNodeの中身を削除しておきます。

```bash
hadoop@kyoto:~$ rm -rf hadoop_tmp/hdfs/datanode/*
```

NameNodeの初期化と起動を行います。

```bash
hadoop@kyoto:~$ hdfs namenode -format
hadoop@kyoto:~$ start-all.sh
```

## サンプルの実行

Single Clusterと同じMapReduceのサンプルを実行してみます。

```bash
hadoop@kyoto:~$ hdfs dfs -mkdir -p /user/hadoop
hadoop@kyoto:~$ hdfs dfs -mkdir input
hadoop@kyoto:~$ hdfs dfs -put etc/hadoop/*.xml input
hadoop@kyoto:~$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.4.0.jar grep input output 'dfs[a-z.]+'
hadoop@kyoto:~$ hdfs dfs -cat output/*
```