Hadoop を Pseudo-Distributed Mode で試してみた
大量データの分析で威力を発揮するものの、癖が強すぎて適応分野の選定が難しいと一部で話題になっているらしい Hadoop ですが、これまでなかなか触る機会がなかったため、少し触ってみることにしました。実際にうまく使いこなせている人達は企業でさえも 10 〜 20 社あるかないかとか言われてるらしいですが、個人的に何も知見がないため、何を言われていても「へ〜そうなんだ〜」と返すしかありません。エンジニアとしてそんな状況もどうなんだと思ったので、ちょっと遊んでみることにしました。ちなみに、Hadoop には大きく分けて Standalone Mode、Pseudo-Distributed Mode、Fully-Distributed Mode の 3 つの構築方法があるそうで、公式ドキュメントにちょっと遊んでみるなら単一ノード構成がいいよと書かれていたので Pseudo-Distributed Mode で遊んでみることにしました。ざっとドキュメントを読んだ感じ、Standalone Mode は分析用のロジックを動かしてみるだけで、Hadoop の特徴らしい HDFS なんかにまったく触らず終わりそうだったので、やめておきます。分析ロジックが jar なので、自分で書いた jar を試しに動かしてみるには Standalone Mode が良さそうです。
なお、今回はらしいらしいという記述が多くなっていますが、分散処理は奥が深そうで、現時点では断言できることが少ないためです。ブログにはなるべくしっかりとした情報を書きたいのですが、これに関しては仕方ないかなと思います。ご了承ください。
Hadoop を Pseudo-Distributed Mode で構築する
公式ドキュメントを参考に、例によって Linux 環境で構築していきます。hadoop ユーザーを作成する
ユーザーは誰でもいいですが、ここでは Hadoop 専用ユーザーを作っておきます。# useradd hadoop # passwd hadoop移行はこの hadoop ユーザーで作業をしていきます。
必要なソフトウェアを用意する
以下の 3 つが必要になります。Java 7 以上
環境変数 JAVA_HOME を設定し、コマンドラインから java が使えるようにパスを通しておきます。sshd
Hadoop が ssh で通信をするので、接続先ノードには sshd を動かしておく必要があります。普通はノードを複数並べた構成にするはずですが、今日は遊んでみるだけなので単一ノード構成になります。ということは、接続先もローカルマシンになるのでローカルで sshd を動かしておきましょう、ということですね。
ssh
単一ノード構成では接続元もローカルマシンになるので、ローカルマシンで ssh が使える必要があります。パスワード無しで localhsot に ssh できるように設定します。
$ ssh localhost
パスワードを聞かれた場合は、聞かれなくて済むように証明書を設定しておきます。ssh 先がローカルマシンになるので、ローカルマシンに SSL 公開鍵を登録します。
$ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys $ chmod 0600 ~/.ssh/authorized_keys
Hadoop をインストールする
公式サイトからアーカイブをとってきます。ここではHadoop ver 2.8.0にします。$ wget http://ftp.jaist.ac.jp/pub/apache/hadoop/common/hadoop-2.8.0/hadoop-2.8.0.tar.gz
展開して配置します。
配置先はどこでもいいですが、私は /usr/local 下に置いておくことにします。
$ tar xvfz hadoop-2.8.0.tar.gz # mv hadoop-2.8.0 /usr/local/
bin ディレクトリ、sbin ディレクトリ下に Hadoop のコマンドがあるので、こちらを実行できるようパスを通します。
sbin ディレクトリ下のスクリプトに PATH を通すかは意見が別れるところですが、今回は面倒なので通します。
$ vi ~/.bashrc ---------------------------- ... HADOOP_HOME="/usr/local/hadoop-2.8.0" PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin ... ---------------------------- $ source ~/.bashrc
パスを通したら動作確認をしてみます。hadoop コマンドを実行できれば OK です。
$ 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 /usr/local/hadoop-2.8.0/share/hadoop/common/hadoop-common-2.8.0.jar
Pseudo-Distributed Mode で動かす
Hadoop を Pseudo-Distributed Mode で動かすために設定ファイルを編集します。Hadoop のインストールディレクトリ下の各ファイルを以下のように修正します。
etc/hadoop/core-site.xml を設定する
ファイルシステムに HDFS を利用します。HDFS は Hadoop Distributed File System の略で、Hadoop での分散処理用に設計されたものらしいです。1 つのファイルを複数に分割してディスクに配置することで、I/O 性能を上げる等の工夫がされているそうです。
<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <!-- 一時ファイルのファイル置き場 --> <property> <name>hadoop.tmp.dir</name> <value>file:/home/hadoop/tmp</value> </property> </configuration>hadoop.tmp.dir 値には Hadoop が利用する一時ファイルの配置場所を設定します。
etc/hadoop/hdfs-site.xml を設定する
HDFS の設定を行います。HDFS は NameNode と DataNode によって構成されます。
HDFS ではファイルはいくつかのブロックに分割されて配置されます。ブロックに分割されたファイルが実際に配置されるのが DataNode です。NameNode には実ファイルは配置されず、どのファイルがどの DataNode のどのブロックに配置されているかというメタ情報のみを扱います。NameNode はそのようなメタデータをメモリ上に展開するため、多量のメモリが必要になります。
dfs.replication 値には DataNode のレプリケーション数を指定します。今回は単一ノード構成なので 1 です。複数ノード構成では適切なレプリケーション数を指定することで、耐障害性を上げることができます。
dfs.namenode.data.dir 値には NameNode が、dfs.datanode.data.dir 値には DataNode が利用するディレクトリを設定します。
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <!-- NameNode のファイル置き場 --> <property> <name>dfs.namenode.data.dir</name> <value>file:/home/hadoop/hdfs/namenode</value> </property> <!-- DataNode のファイル置き場 --> <property> <name>dfs.datanode.data.dir</name> <value>file:/home/hadoop/hdfs/datanode</value> </property> </configuration>
HDFS ファイルシステムをフォーマットする
ファイルシステムをフォーマットする、と聞くと Linux パーティションがまるごとフォーマットされそうな気がしますが、その心配はありません。実際には hdfs-site.xml で設定したディレクトリに HDFS のデータが作られ、実ファイルが格納されることになります。$ hdfs namenode -format 17/03/29 10:31:08 INFO namenode.NameNode: STARTUP_MSG: /************************************************************ STARTUP_MSG: Starting NameNode STARTUP_MSG: user = hadoop ... 17/03/29 10:31:09 INFO common.Storage: Storage directory /home/hadoop/hdfs/namenode has been successfully formatted. ...
NameNode デーモンと DataNode デーモンを起動する
HDFS を動かすため、NameNode デーモンと DataNode デーモンを起動します。ファイルシステムを動かすというのも変な感じですが、Linux ファイルシステム上で擬似的に独自のファイルシステムを再現する設計になっているため、デーモンが必要になるようです。起動スクリプト start-dfs.sh を実行すればいいのですが、そのままでは java コマンドが探せずエラーになったので、etc/hadoop/hadoop-env.sh に JAVA_HOME を直接設定しておきます。
... # The java implementation to use. export JAVA_HOME=/usr/java/jre1.8.0_111 #export JAVA_HOME=${JAVA_HOME} ...
これで、start-dfs.sh を実行すれば HDFS を動かすことができます。
$ start-dfs.shHDFS の停止用には stop-dfs.sh があるので、停止する時はそちらを使います。
HDFS ファイルシステム内にディレクトリを作る
Hadoop に読ませるファイルを HDFS に配置したり、分析結果を出力させたりするために、あらかじめディレクトリを作っておきます。ここはいわば作業ディレクトリとなるようです。
$ hdfs dfs -mkdir /user $ hdfs dfs -mkdir /user/hadoop
Hadoop でテキストファイルを分析する
実際に Hadoop でテキストファイルを分析してみます。分析といっても今回は grep コマンド相当のことをしてみるだけです。Hadoop に読ませるサンプルのアクセスログファイルを用意し、アクセス元 IP を抽出してみます。
分析用のアクセスログを用意する
分析用に以下のようなテキストファイルを用意してみました。とりあえず 3 つ置いておきます。$ cat accesslog01.log 192.168.0.1 - - [21/Mar/2017:10:09:18 +0900] "GET /book/1 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 192.168.0.2 - - [21/Mar/2017:10:09:18 +0900] "GET /book/2 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 192.168.0.4 - - [21/Mar/2017:10:09:18 +0900] "GET /book/3 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" $ cat accesslog02.log 192.168.0.1 - - [21/Mar/2017:10:09:18 +0900] "GET /book/1 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 192.168.0.2 - - [21/Mar/2017:10:09:18 +0900] "GET /book/2 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 192.168.0.5 - - [21/Mar/2017:10:09:18 +0900] "GET /book/3 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" $ cat accesslog03.log 192.168.0.2 - - [21/Mar/2017:10:09:18 +0900] "GET /book/1 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 192.168.0.3 - - [21/Mar/2017:10:09:18 +0900] "GET /book/2 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko" 192.168.0.6 - - [21/Mar/2017:10:09:18 +0900] "GET /book/3 HTTP/1.1" 200 2078 "/" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
これらの分析用テキストファイルを HDFS ファイルシステム内に配置します。
input ディレクトリを作成し、その中に先ほどのテキストファイルを配置します。
$ hdfs dfs -mkdir input $ hdfs dfs -put accesslog*.log input
アクセスログからアクセス元 IP を抽出する
HDFS ファイルシステム内に配置したテキストファイルから単語を抽出し、その個数を数えてみます。Hadoop が分析で利用するロジックを jar ファイルとして hadoop コマンドの引数に指定することができます。例えば hadoop-mapreduce-examples-2.8.0.jar は引数に grep を渡すと Linux の grep コマンドのように動作します。
先頭の数値が抽出したい IP になるので、grep する単語を正規表現で '^[0-9.]+' と指定してみます。
$ hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.0.jar grep input output '^[0-9.]+'
分析結果が HDFS ファイルシステムの output 内に配置されるので、直接中身を確認してみます。
$ hdfs dfs -cat output/* 3 192.168.0.2 2 192.168.0.1 1 192.168.0.6 1 192.168.0.5 1 192.168.0.4 1 192.168.0.3アクセスログに記載された IP が個数順に集計されていますね。
分析結果を HDFS ファイルシステムからとりだすには、以下のコマンドを実行します。
$ hdfs dfs -get output outputとりだした分析結果をローカルマシンのファイルシステム上で cat してみると、先程 HDFS ファイルシステム内で確認した内容と同じ内容が書かれているのがわかります。
$ cat output/* 3 192.168.0.2 2 192.168.0.1 1 192.168.0.6 1 192.168.0.5 1 192.168.0.4 1 192.168.0.3
なお、一度分析結果を出力した HDFS ファイルシステム内のディレクトリは再利用ができないので、不要になったタイミングで適宜消しておきましょう。
$ hadoop fs -rm -r outputこれは、逆に言えば、一度分析した結果は HDFS 内に永続化されるということになります。
一度分析した結果を 1 週間利用するというケースでは、分析処理は 1 度だけ行い、それ移行は既に分析済みの結果を読み込むのみでよいということですね。
おわり
ひとまず Hadoop で遊んでみようということで、Pseudo-Distributed Mode での動作を簡単に試してみました。今回試した grep 処理でも、データ量の割には結構な時間がかかっているのがわかると思います。少量のデータに対しては、Linux の grep なら遥かに高速ですが、これは逆に大量データを相手にする際には Hadoop に軍配が上がってくるのだと思います。また一回の分析処理にかかる時間を短縮する試みや、様々な面での使い勝手を改善するための周辺技術も多く存在しているので、その辺についてもまた折をみて試してみたいなと思います。
WEB 技術, Hadoop コメント (0) 2017/03/29 18:42:18