CUBE SUGAR CONTAINER

技術系のこと書きます。

CentOS7 で Apache Hadoop の完全分散モードを使ってみる

以前、このブログでは OSS 版の Apache Hadoop を疑似分散モードでセットアップする方法を試した。 疑似分散モードというのは、一つのホスト上に必要なデーモンを全て立ち上げる方法を指す。 このモードを使うと HDFS が使えるような、なるべく本番に近い環境が手軽に作れる。

blog.amedama.jp

ただ、疑似分散モードでは本当にちゃんと動作するのかが確認しづらい箇所もある。 それは、主にホストを分割してネットワーク越しにやり取りをする部分で、例えばファイアウォールの設定など。

そこで、今回は Apache Hadoop を完全分散モードでセットアップしてみることにした。 完全分散モードというのは本番運用されるのと同じ環境で、それぞれのデーモンを異なるホストで動かすやり方。 完全分散モードのセットアップ方法については次のドキュメントを参照する。

Apache Hadoop 2.7.3 –

構成について

今回は、なるべく少ないホストで作りたいので三台のホストを使ってセットアップする。 マスターノード一台とスレーブノード二台という構成になる。 それぞれのホストには、次のように IP アドレスを振った。

  • マスターノード
    • 192.168.33.10
  • スレーブノード1
    • 192.168.33.11
  • スレーブノード2
    • 192.168.33.12

ここからは、それぞれのホストでどういった機能が動作するかを説明する。 まずは、Apache Hadoop で動作させるサービス (デーモン) について説明しておく。

  • HDFS 関連

    • NameNode
      • どのノードに目当てのファイルがあるかといったメタデータを管理するサービス
    • DataNode
      • 細かく分割したファイルを実際に保持して必要に応じて操作できるようにするサービス
    • Secondary NameNode
      • メタデータなどの情報をメモリからディスクに永続化するためのサービス
  • YARN 関連

    • ResourceManager
      • 今どのノードで、どういったタスクが実行されているか管理するサービス
    • NodeManager
      • タスクを実行するためのサービス
  • MRv2 関連

    • MapReduce Job History Server
      • MapReduce ジョブの履歴を管理するサービス

上記のサービスを、それぞれ次のように割り当てる。

  • マスターノード

    • NameNode
    • ResourceManager
    • Secondary NameNode
    • MapReduce Job History Server
  • スレーブノード

    • DataNode
    • NodeManager

使った環境については次の通り。 疑似分散モードのときと同じように CentOS7 上に OSS 版の Apache Hadoop をセットアップする。

$ cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)
$ uname -r
3.10.0-514.21.1.el7.x86_64

全ホスト共通

まずは全ホスト共通の作業から実施する。

hostname

ホスト名を設定しておく。 これはやらなくても大丈夫だけど、操作対象のホストを間違えないようにやっておいた方が良いかも。 それぞれのホストで実施する。

マスターノードには “master” というホスト名をつけておく。

master $ sudo hostname master
master $ echo "master" | sudo tee /etc/hostname > /dev/null

一つ目のスレーブノードには “node1” というホスト名をつけておく。

node1 $ sudo hostname node1
node1 $ echo "node1" | sudo tee /etc/hostname > /dev/null

二つ目のスレーブノードには “node2” というホスト名をつけておく。

node2 $ sudo hostname node2
node2 $ echo "node2" | sudo tee /etc/hostname > /dev/null

/etc/hosts

次に /etc/hosts ファイルを編集する。 これは IP アドレスの代わりにホスト名を使って設定ファイルを書きたいので。

$ cat << 'EOF' | sudo tee -a /etc/hosts > /dev/null
192.168.33.10 master
192.168.33.11 node1
192.168.33.12 node2
EOF

ちゃんと設定ファイルが書き換わったことを確認しておく。

$ tail -n 3 /etc/hosts
192.168.33.10 master
192.168.33.11 node1
192.168.33.12 node2

依存パッケージ

次に必要なパッケージをインストールする。 epel-releasesshpass はスレーブノードには必要ないんだけど、まあ入れておいても問題はない。

$ sudo yum -y install epel-release
$ sudo yum -y install openssh-clients rsync wget java-1.8.0-openjdk-devel sshpass

Hadoop

次に Apache Hadoop をダウンロードする。

$ wget http://ftp.riken.jp/net/apache/hadoop/common/hadoop-2.8.0/hadoop-2.8.0.tar.gz
$ tar xf hadoop-2.8.0.tar.gz

シェルの設定ファイルに、いくつかの環境変数を指定しておこう。

$ cat << 'EOF' >> ~/.bashrc
export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk
export HADOOP_HOME=~/hadoop-2.8.0
export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$JAVA_HOME/bin:$PATH
EOF
$ source ~/.bashrc

これで hadoop コマンドが動作することを確認しておく。

$ hadoop version
Hadoop 2.8.0
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r 91f2b7a13d1e97be65db92ddabc627cc29ac0009
Compiled by jdu on 2017-03-17T04:12Z
Compiled with protoc 2.5.0
From source with checksum 60125541c2b3e266cbf3becc5bda666
This command was run using /home/vagrant/hadoop-2.8.0/share/hadoop/common/hadoop-common-2.8.0.jar

マスターノード

ここからはマスターノードで作業をする。

SSH

Apache Hadoop のセットアップでは、マスターノードから各ホストに SSH でパスフレーズを使わずにログインできる必要がある。 そこで、まずはパスフレーズを空にした公開鍵ペアを用意しておく。

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

作成した公開鍵を各ホストに設置する。 これでマスターノードから各ホストにログインできるようになる。

$ for node in master node1 node2; do sshpass -p "vagrant" ssh-copy-id -i ~/.ssh/id_rsa.pub -o "StrictHostKeyChecking no" $node; done;

設定ファイルを編集する

ここからは Apache Hadoop の動作に必要な設定ファイルを編集していく。

etc/hadoop/slaves

まずはスレーブノードがどのホストなのかを示す設定ファイルを編集する。

$ cat << 'EOF' > $HADOOP_HOME/etc/hadoop/slaves
node1
node2
EOF

今回使うスレーブノードは node1node2 の二台になる。

$ cat $HADOOP_HOME/etc/hadoop/slaves
node1
node2
etc/hadoop/core-site.xml

次は HDFS の設定ファイルを編集する。 HDFS の接続エンドポイントがマスターノードであることを指定しておく。

$ cat << 'EOF' > /tmp/core-site.xml.property
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://192.168.33.10:9000</value>
  </property>
EOF
$ sed -i -e '
  /^<configuration>$/r /tmp/core-site.xml.property
  /^$/d
' $HADOOP_HOME/etc/hadoop/core-site.xml

こんな感じになる。

$ tail -n 6 $HADOOP_HOME/etc/hadoop/core-site.xml
<configuration>
  <property>
    <name>fs.defaultFS</name>
    <value>hdfs://192.168.33.10:9000</value>
  </property>
</configuration>
etc/hadoop/hdfs-site.xml

同様に HDFS のレプリケーション数 (ファイルのコピーをいくつ作るか) やセカンダリネームノードのエンドポイントを指定していく。

$ cat << 'EOF' > /tmp/hdfs-site.xml.property
  <property>
    <name>dfs.replication</name>
    <value>2</value>
  </property>
  <property>
    <name>dfs.namenode.secondary.http-address</name>
    <value>192.168.33.10:50090</value>
  </property>
EOF
$ sed -i -e '
  /^<configuration>$/r /tmp/hdfs-site.xml.property
  /^$/d
' $HADOOP_HOME/etc/hadoop/hdfs-site.xml

こんな感じになる。

$ tail -n 6 $HADOOP_HOME/etc/hadoop/hdfs-site.xml
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>2</value>
  </property>
</configuration>
etc/hadoop/mapred-site.xml

次は分散処理フレームワークの YARN を設定していく。

分散処理フレームワークとして YARN を使うことを指定する。

$ cp $HADOOP_HOME/etc/hadoop/mapred-site.xml{.template,}
$ cat << 'EOF' > /tmp/mapred-site.xml.property
  <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
EOF
$ sed -i -e '
  /^<configuration>$/r /tmp/mapred-site.xml.property
  /^$/d
' $HADOOP_HOME/etc/hadoop/mapred-site.xml

こんな感じになる。

$ tail -n 6 $HADOOP_HOME/etc/hadoop/mapred-site.xml
<configuration>
  <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
  </property>
</configuration>
etc/hadoop/yarn-site.xml

同様に YARN が動作するための設定を行う。

$ cat << 'EOF' > /tmp/yarn-site.xml.property
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
  <property>
    <name>yarn.resourcemanager.hostname</name>
    <value>master</value>
  </property>
EOF
$ sed -i -e '
  /^<configuration>$/r /tmp/yarn-site.xml.property
  /^$/d
' $HADOOP_HOME/etc/hadoop/yarn-site.xml

こんあ感じになる。

$ tail -n 11 $HADOOP_HOME/etc/hadoop/yarn-site.xml
<configuration>
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
  <property>
    <name>yarn.resourcemanager.hostname</name>
    <value>master</value>
  </property>
<!-- Site specific YARN configuration properties -->
</configuration>
設定ファイルをスレーブノードにコピーする

マスターノードで作った設定ファイルをスレーブノードにも設置する。 ここは SCP を使って一気にバラまくことにした。

$ for node in node1 node2; do scp $HADOOP_HOME/etc/hadoop/* $node:$HADOOP_HOME/etc/hadoop/; done;

クラスタを構築する

ここまでで必要な設定ファイルは用意できた。 あとはクラスタを構築していくだけ。 以下の操作もマスターノードのみで実施する。

まずは HDFS をフォーマットする。

$ $HADOOP_HOME/bin/hdfs namenode -format

続いて HDFS 関連のデーモンを起動する。

$ $HADOOP_HOME/sbin/start-dfs.sh

マスターノードでは、次のようにネームノードとセカンダリネームノードのサービスが起動する。

$ jps
9062 Jps
8951 SecondaryNameNode
8473 NameNode

続いて YARN 関連のデーモンを起動する。

$ $HADOOP_HOME/sbin/start-yarn.sh

マスターノードでは、次のようにリソースマネージャのサービスが起動する。

$ jps
9648 Jps
8951 SecondaryNameNode
8473 NameNode
9165 ResourceManager

続いて MapReduce のジョブ履歴を管理するサービスを起動する。

$ $HADOOP_HOME/sbin/mr-jobhistory-daemon.sh --config $HADOOP_CONF_DIR start historyserver

マスターノードでは、次のようにジョブヒストリサーバが起動する。

$ jps
9648 Jps
8951 SecondaryNameNode
8473 NameNode
9610 JobHistoryServer
9165 ResourceManager

ここまで実行するとスレーブノードでもネームノードとデータノードのサービスが立ち上がっている。

$ jps
4560 NodeManager
5090 Jps
4456 DataNode

使ってみよう

これで Apache Hadoop のクラスタを完全分散モードで組むことができた。 実際に軽く使ってみることにしよう。

まずはサンプルに入っているプログラムを使って円周率を計算させてみる。

$ $HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar pi 10 10000
Number of Maps  = 10
Samples per Map = 10000
Wrote input for Map #0
Wrote input for Map #1
Wrote input for Map #2
Wrote input for Map #3
Wrote input for Map #4
Wrote input for Map #5
Wrote input for Map #6
Wrote input for Map #7
Wrote input for Map #8
Wrote input for Map #9
Starting Job
...(snip)...
Job Finished in 52.727 seconds
Estimated value of Pi is 3.14120000000000000000

上手く実行できた。

次は、たくさんのファイルの中から特定の文言が何回出現するかを調べる grep も動かしてみよう。 先ほどの円周率は単なる数値計算だったけど、こちらのプログラムは HDFS との連携も必要になる。

まずは検索対象のファイルを置くために HDFS のホームディレクトリを作っておく。

$ $HADOOP_HOME/bin/hdfs dfs -mkdir -p .
$ $HADOOP_HOME/bin/hdfs dfs -ls -R /user
drwxr-xr-x   - vagrant supergroup          0 2017-06-05 19:58 /user/vagrant

grep の検索対象は Apache Hadoop の設定ファイル群を使ってみよう。 設定ファイルの入ったディレクトリを input という名前で HDFS にコピーする。

$ $HADOOP_HOME/bin/hdfs dfs -put $HADOOP_HOME/etc/hadoop input

こんな感じでコピーされるはず。

$ $HADOOP_HOME/bin/hdfs dfs -ls input
Found 30 items
-rw-r--r--   2 vagrant supergroup       4942 2017-06-05 19:59 input/capacity-scheduler.xml
-rw-r--r--   2 vagrant supergroup       1335 2017-06-05 19:59 input/configuration.xsl
-rw-r--r--   2 vagrant supergroup        318 2017-06-05 19:59 input/container-executor.cfg
...(snip)...
-rw-r--r--   2 vagrant supergroup       2250 2017-06-05 19:59 input/yarn-env.cmd
-rw-r--r--   2 vagrant supergroup       4567 2017-06-05 19:59 input/yarn-env.sh
-rw-r--r--   2 vagrant supergroup        897 2017-06-05 19:59 input/yarn-site.xml

ちなみに、ファイルの実体はデータノードのサービスが動いているスレーブノードに分散して設置されている。

/tmp/hadoop-$(whoami)/dfs/data/current

サンプルにある grep のプログラムを動かしてみよう。 設定ファイルの中から dfs で始まる言葉を探している。

$ $HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar grep input output 'dfs[a-z.]+'
17/06/05 20:00:35 INFO client.RMProxy: Connecting to ResourceManager at master/192.168.33.10:8032
17/06/05 20:00:36 INFO input.FileInputFormat: Total input files to process : 30
17/06/05 20:00:36 INFO mapreduce.JobSubmitter: number of splits:30
17/06/05 20:00:37 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1496645818704_0002
17/06/05 20:00:37 INFO impl.YarnClientImpl: Submitted application application_1496645818704_0002
17/06/05 20:00:37 INFO mapreduce.Job: The url to track the job: http://master:8088/proxy/application_1496645818704_0002/
17/06/05 20:00:37 INFO mapreduce.Job: Running job: job_1496645818704_0002
17/06/05 20:00:46 INFO mapreduce.Job: Job job_1496645818704_0002 running in uber mode : false
17/06/05 20:00:46 INFO mapreduce.Job:  map 0% reduce 0%
17/06/05 20:01:14 INFO mapreduce.Job:  map 13% reduce 0%
17/06/05 20:01:15 INFO mapreduce.Job:  map 20% reduce 0%
...(snip)

実行が終わったら HDFS の output ディレクトリに結果が出力される。

$ $HADOOP_HOME/bin/hdfs dfs -cat output/*
6  dfs.audit.logger
4  dfs.class
3  dfs.logger
3  dfs.server.namenode.
2  dfs.audit.log.maxbackupindex
2  dfs.period
2  dfs.audit.log.maxfilesize
1  dfs.log
1  dfs.file
1  dfs.servers
1  dfsadmin
1  dfsmetrics.log
1  dfs.replication

ばっちりだね。

Vagrantfile

上記を手作業で毎回やるのは大変なので Vagrantfile を書いて自動化してみた。

Vagrantfile for Hadoop Cluster with CentOS 7 and Hadoop 2.8.0 (3 hosts) · GitHub

Vagrant と Git をインストールした Unix 系のシステムなら、次のようにして自動でクラスタを構築できる。 デフォルトで、それぞれのホストに 2GB のメモリを割り当てるので最低でも 8GB できれば 16GB 以上のメモリを積んだマシンで実行した方が良い。

$ git clone https://gist.github.com/0ff814ce4c3aa659723c6b5b0fc85557.git hadoop-cluster
$ cd hadoop-cluster
$ vagrant provision node1 node2 master

あとはそれぞれのホストに入って色々使ってみるだけ。

$ vagrant ssh master

いじょう。

まとめ

今回は Apache Hadoop のクラスタを完全分散モードで構築してみた。 完全分散モードであればマスターとスレーブがネットワークで分かれているので、よりプロダクション環境に近い状況で検証ができる。

Apache Hadoop については次の本がとても分かりやすい。

Hadoop徹底入門 第2版 オープンソース分散処理環境の構築

Hadoop徹底入門 第2版 オープンソース分散処理環境の構築