CUBE SUGAR CONTAINER

技術系のこと書きます。

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

Apache Hadoop はビッグデータ処理基盤を構築するための超有名なオープンソースソフトウェア。 Google の発表した論文を元にして MapReduce アルゴリズムと Hadoop Distributed File System (HDFS) が実装されている。 この Hadoop/HDFS を中心として Apache HiveApache HBase などのミドルウェアが動作する一大エコシステムが形成されている。 今回は、そんな Apache Hadoop を CentOS7 で使ってみることにする。

尚、Apache Hadoop 関連の OSS をインストールするときは、いくつかの会社が出しているディストリビューションを利用することが多い。 例えば Cloudera CDH や Hortonworks HDP など。 しかし、今回はそれらのディストリビューションを使うのではなく Apache が出しているパッケージをそのまま使ってみる。 その方が内部でどんなことをやっているのかとか、どのように設定すれば良いのか理解が深まるだろうと考えたため。

ちなみに、Apache Hadoop は以下に示す三つの動作モードがある。

  • ローカルモード
    • 一つのホストで動作する
    • HDFS を使わない
  • 疑似分散モード
    • 一つのホストで動作する
    • HDFS を使う
  • 完全分散モード
    • 複数のホストで動作する
    • HDFS を使う

今回は、その中でも疑似分散モードを試すことにした。 実運用に乗せる環境なら確実に完全分散モードになるけど、手元での検証用なら疑似分散モードでも十分に使えそうなので。 ちなみに HDFS というのは複数のホストに細切れにしたファイルを配布した上で、それを各ホストで並列処理できる仕組みを備えたファイルシステムになっている。

疑似分散モード、というよりローカルモードを含むシングルホストで動かすときに参照するドキュメントは次の通り。

Apache Hadoop 2.8.0 – Hadoop: Setting up a Single Node Cluster.

もし完全分散モードなら、このドキュメントを参照する。

Apache Hadoop 2.8.0 – Hadoop Cluster Setup

使った環境は次の通り。

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

Java をインストールする

Hadoop 関連のソフトウェアは、その多くが JVM 言語で書かれている。 なので、まずは Java をインストールしておこう。 一応、動作確認が取れているバージョンはこのページにあった。

HadoopJavaVersions - Hadoop Wiki

OpenJDK だと 1.7 しか今のところ確認されていないっぽい。 とはいえ、まあ動かないなんてことはないだろうという考えで 1.8 を入れてみることにする。 jps コマンドが使いたいのでインストールするのを JDK にする。

$ sudo yum -y install java-1.8.0-openjdk-devel

java コマンドが使えることが確認する。

$ java -version
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)

Apache Hadoop をローカルモードで動かす

まずは Apache Hadoop をダウンロードした上でローカルモードで動かしてみる。 というより、何も設定しない状態ではローカルモードとして動作する。

ひとまず最低限必要なパッケージを一通り入れておく。

$ sudo yum -y install openssh-clients rsync wget

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

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

Apache Hadoop のミラーサイトは次の通り。 好きなところを選んでダウンロードしよう。

www.apache.org

ダウンロードできたら解凍する。

$ tar xf hadoop-2.8.0.tar.gz

解凍してできたディレクトリに移動しよう。

$ cd hadoop-2.8.0/

Hadoop を動かすには環境変数として JAVA_HOME が設定されている必要がある。 そのため、先ほどインストールした OpenJDK のディレクトリを指定する。

$ export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk

あるいは、起動時の環境変数を etc/hadoop/hadoop-env.sh で指定することもできる。 設定を永続化したいときは、こちらを使えば良さそう。

$ sed -i -e '
  s:^export JAVA_HOME=.*$:export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk:
' etc/hadoop/hadoop-env.sh

これで hadoop コマンドがエラーにならず実行できるようになる。

$ bin/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

いくつかサンプルプログラムを動かしてみよう。 これには hadoop コマンドに付属のサンプルプログラムの入った jar ファイルを指定する。 例えば、これは円周率を計算するもの。

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar pi 10 100000
Number of Maps  = 10
Samples per Map = 100000
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 1.526 seconds
Estimated value of Pi is 3.14155200000000000000

それ以外には、ディレクトリ内のファイルから特定の文字列を検索する grep とか。 ローカルモードでは HDFS を使わないので OS のファイルシステムを直接使うことになる。

検索対象として input ディレクトリの中に Hadoop の設定ファイルをコピーしておこう。

$ mkdir input
$ cp etc/hadoop/*.xml input

input ディレクトリの中にあるファイルから dfs から始まる言葉を探してみる。

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar grep input output 'dfs[a-z.]+'

結果は output ディレクトリに出力される。 この例では dfsadmin という言葉が一つだけ見つかった。

$ cat output/*
1  dfsadmin

同じ文字列を使って設定ファイルを egrep すると、たしかに一つだけ見つかる。

$ egrep 'dfs[a-z.]+' etc/hadoop/*.xml
etc/hadoop/hadoop-policy.xml:    dfsadmin and mradmin commands to refresh the security policy in-effect.

疑似分散モード

続いては疑似分散モードで動かしてみる。 疑似分散モードでは一つのホストで完結しているもののファイルシステムとして HDFS を使うことになる。 これによって、より実運用に近づけた状態で動作確認ができる。

SSH でログインできるようにする

Hadoop は SSH 経由でホストを操作するので、あらかじめログインできるようにしておかないといけない。 そこで、パスワードなしの公開鍵のペアを作ってローカルホストに仕込んでおく。

$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
$ ssh-copy-id -i ~/.ssh/id_rsa.pub localhost

ログインできることを確認しておこう。

$ ssh localhost

設定ファイル: etc/hadoop/core-site.xml

続いては設定ファイルを編集していく。

まずは etc/hadoop/core-site.xml に接続先ファイルシステム (HDFS) の設定を仕込む。

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

こんな感じ。 これでローカルホストの HDFS に接続するようになる。

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

設定ファイル: etc/hadoop/hdfs-site.xml

続いては etc/hadoop/hdfs-site.xml で HDFS の設定をする。 ここでは、ファイルのレプリケーション数を 1 に設定する。 HDFS では冗長性を担保するために、複数のホストで同じデータを重複して持つ。 これが HDFS のレプリケーション数と呼ばれるもので、デフォルトでは 3 になっている。 シングルホストで動く疑似分散モードでは、レプリケーション数は 1 にするしかない。

$ cat << 'EOF' > /tmp/hdfs-site.xml.property
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
EOF
$ sed -i -e '
  /^<configuration>$/r /tmp/hdfs-site.xml.property
  /^$/d
' etc/hadoop/hdfs-site.xml

こんな感じになる。

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

HDFS をセットアップする

ここまで設定できたら HDFS をフォーマットする。

$ bin/hdfs namenode -format

そして、必要なサービス (デーモン) を起動する。

$ sbin/start-dfs.sh

上記を実行したら、ログにエラーメッセージなどが出ていないことを確認しておこう。

$ grep -ir ERROR logs/*

また、次のように jps を確認してネームノード、データノード、セカンダリネームノードが動作していれば良い。 ネームノードは HDFS のメタデータなどを保持するマスターサーバのこと。 データノードは、実際のデータを格納しているスレーブサーバのようなもの。 セカンダリネームノードはその名の通りネームノードのセカンダリ。

$ jps
28720 NameNode
28849 DataNode
29012 SecondaryNameNode
29129 Jps

ここまでで hdfs コマンドが使えるようになる。 このコマンドを経由して HDFS の操作を行う。 使い方は一般的な POSIX のファイルシステムと基本的には似通っている。 ひとまず作業用にホームディレクトリを作ってみよう。

$ bin/hdfs dfs -mkdir -p /user/$(whoami)

すると、こんな感じでディレクトリが作られる。

$ bin/hdfs dfs -ls -R /
drwxr-xr-x   - vagrant supergroup          0 2017-05-15 21:16 /user
drwxr-xr-x   - vagrant supergroup          0 2017-05-15 21:16 /user/vagrant

ちなみに、HDFS で操作した実体はもちろん OS のファイルシステム上に存在している。 デフォルトでは /tmp/hadoop-<username> に作られる。

$ ls /tmp/hadoop-$(whoami)

先ほどと同じように grep のサンプルプログラムを動かすために input ディレクトリを作る。 その上でローカルのファイルシステムにある設定ファイルを HDFS のディレクトリ内にコピーしよう。

$ bin/hdfs dfs -mkdir input
$ bin/hdfs dfs -put etc/hadoop/*.xml input

上記ができたらサンプルプログラムを実行する。

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar grep input output 'dfs[a-z.]+'

実行が終わったら結果が出力されているディレクトリを HDFS からローカルのファイルシステムにコピーする。

$ bin/hdfs dfs -get output output2

結果を見ると、次のようになった。

$ cat output2/*
1  dfsadmin
1  dfs.replication

二つに増えているのは疑似分散モードで動かすための設定に dfs から始まる文字列が含まれていたため。

$ egrep 'dfs[a-z.]+' etc/hadoop/*.xml
etc/hadoop/hadoop-policy.xml:    dfsadmin and mradmin commands to refresh the security policy in-effect.
etc/hadoop/hdfs-site.xml:    <name>dfs.replication</name>

ちなみに、後片付けとして HDFS のサービス (デーモン) を停止するには、次のようにする。

$ sbin/stop-dfs.sh

YARN/MRv2 で動かす

実は Hadoop には一口に MapReduce といっても複数の実装がある。 それが、先ほど動かした MRv1 と、これから動かす YARN/MRv2 というもの。 まだ違いがよく分かっていないんだけど、YARN/MRv2 は MRv1 に存在した問題を解消するために作られたらしい。 主に、スケーラビリティや汎用性が改善されているっぽい。

設定ファイル: etc/hadoop/mapred-site.xml

ここからは、また設定ファイルを編集していく。

まずは設定ファイル etc/hadoop/mapred-site.xml のテンプレートをコピーしてくる。

$ cp etc/hadoop/mapred-site.xml{.template,}

その上で MapReduce のフレームワークとして YARN が使われるようにする。

$ 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
' etc/hadoop/mapred-site.xml

こんな感じ。

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

設定ファイル: etc/hadoop/yarn-site.xml

次は YARN 自体の設定を etc/hadoop/yarn-site.xml に書き込む。

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

こうなる。

$ tail -n 7 etc/hadoop/yarn-site.xml
<configuration>
  <property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
  </property>
<!-- Site specific YARN configuration properties -->
</configuration>

あとは YARN のサービス (デーモン) を起動する。

$ sbin/start-yarn.sh

先ほどと同じようにログにエラーメッセージが出ていないか確認しておく。

$ grep -ir ERROR logs/*

次のようにリソースマネージャとノードマネージャが起動していれば良い。

$ jps
28720 NameNode
28849 DataNode
29602 Jps
29012 SecondaryNameNode
29194 ResourceManager
29309 NodeManager

YARN/MRv2 でサンプルプログラムを動かしてみる

YARN/MRv2 は MapReduce の実行フレームワークが異なるだけで基本的には MRv1 と同じプログラムが使えるらしい。

先ほどと同じようにサンプルプログラムを動かしてみよう。 一旦、さっき使ったディレクトリを削除して、改めて設定ファイルをコピーしておく。 今度は XML 以外のファイルも対象に含めてみる。

$ bin/hdfs dfs -rm -r -f input output
$ bin/hdfs dfs -put etc/hadoop input

grep のサンプルプログラムを実行する。 理由は分からないけど、今回はやたらと時間がかかった。 ただファイルが増えただけが理由なのか YARN/MRv2 を使っているせいなのか。

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar grep input output 'dfs[a-z.]+'

終わったら実行結果を HDFS からローカルのファイルシステムにコピーする。

$ bin/hdfs dfs -get output output3

次のように dfs から始まる文字列が見つかった。

$ cat output3/*
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

後片付けするときは次のようにして YARN のサービス (デーモン) を停止する。

$ sbin/stop-yarn.sh

まとめ

今回は素の Apache Hadoop を CentOS7 の上で使ってみた。 動作モードとしてはシングルホスト上で HDFS と共に動作する疑似分散モードを選んだ。 実運用するなら完全分散モードになるけど、手元での動作検証用なら有用といえそうだ。 ビッグデータを扱う上で Apache Hadoop 関連のプロダクトは避けて通れないので、今後も知見を集めていきたい。

Apache Hadoop の概念や動作原理的な部分は、この本が分かりやすかった。 ただし、この本では発売時期的に MRv1 しか扱っておらず YARN/MRv2 については書かれていない。

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

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