ラベル Hadoop の投稿を表示しています。 すべての投稿を表示
ラベル Hadoop の投稿を表示しています。 すべての投稿を表示

2011/01/31

Hadoopのslaveの増やし方、減らし方の続き


xperiaにskypeをインストールしたらバッテリーが1日持たなくなってしまいました。
今は充電ケーブルに行動範囲を縛られてるleonです。

今回は、前回の続き「ご紹介したHadoopサーバーの増やし方、減らし方」のハマり話です。

ハマったのはincludeファイル、excludeファイルの記述する方法です。

答え、先言っちゃいますね。
includeファイルと、excludeファイルにはサーバーのFQDNを記述しなきゃ行けなかったんです。

最初、slavesファイルと同じホスト名を記述したんですが、Hadoopサービスを再起動するとslaveサーバーが接続されないんです。

「hadoop-hadoop-jobtracker-hdmaster.log」を見ると以下のようなログが出てました。
2010-10-20 14:05:35,769 INFO org.apache.hadoop.ipc.Server: IPC Server handler 15 on 9001, call heartbeat(org.apache.hadoop.mapred.TaskTrackerStatus@51c293f8, true, true, -1) from 192.168.0.xxx:44035: error: org.apache.hadoop.mapred.DisallowedTaskTrackerException: Tasktracker denied communication with jobtracker: tracker_hdslave1.domain.jp:localhost.localdomain/127.0.0.1:44034
org.apache.hadoop.mapred.DisallowedTaskTrackerException: Tasktracker denied communication with jobtracker: tracker_hdslave1.domain.jp:localhost.localdomain/127.0.0.1:44034
    at org.apache.hadoop.mapred.JobTracker.heartbeat(JobTracker.java:1246)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:481)
    at org.apache.hadoop.ipc.Server$Handler.run(Server.java:890)
はて?jobtrackerからクラスタへの接続が許可されないとな?includeファイルが読めてない?
Haddop設定ファイルのdfs.hostsプロパティとmapred.hostsプロパティの値を見直しても間違ってませんよ。って感じであれこれ試行錯誤しました。

で、ホスト名をFQDNにしてみたらなんとslaveサーバーと接続されるじゃないですか。

前回説明した通りmasters/slavesファイルとinclude/excludeファイルは役割が異なる。役割が異なるから設定も異なるってことなんでしょうね。
masters/slavesファイルはHadoop起動制御のシェルスクリプトが参照し、include/excludeファイルはHadoopのjavaプロセスが参照しています。

これ、わかんないですよねー。

あと、masterサーバーとslaveサーバーのネットワーク接続状態を確認したところ、hadoopのjavaプロセスはIPv6でバインドされてました。

$ sudo netstat -plten | grep java
tcp        0      0 ::ffff:192.168.0.5:9000     :::*                        LISTEN      220        2464200901 8483/java
tcp        0      0 ::ffff:192.168.0.5:9001     :::*                        LISTEN      220        2464201589 8766/java
(省略)

弊社のネットワークはすべてIPv4で構築してます。なので、HadoopもIPv4でバインドするように変更しました。具体的には、hadoopコマンドのスクリプトを編集して明示的にIPv4で接続するしました。

258 HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.root.logger=${HADOOP_ROOT_LOGGER:-INFO,console}"
    259 if [ "x$JAVA_LIBRARY_PATH" != "x" ]; then
    260   HADOOP_OPTS="$HADOOP_OPTS -Djava.library.path=$JAVA_LIBRARY_PATH"
    261 fi
→  262 HADOOP_OPTS="$HADOOP_OPTS -Djava.net.preferIPv4Stack=true"
    263
    264 # run it
    265 exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS -classpath "$CLASSPATH" $CLASS "$@"
262行目の1行が編集した部分です。

hadoopを再起動して、ネットワーク接続状態を確認すると・・・

$ sudo netstat -plten | grep java
tcp        0      0 192.168.0.5:9000            0.0.0.0:*                   LISTEN      220        2480561580 29797/java
tcp        0      0 192.168.0.5:9001            0.0.0.0:*                   LISTEN      220        2480562441 30112/java
(省略)

IPv4でバインドされてますね。OKです。

ではでは

2011/01/25

Hadoopサーバーの増やし方、減らし方



すべての地雷を踏む男leonです。

Hadoopカスタマイズをネタにブログを始めさせていただきました。
前回、Hadoopセットアップ手順を紹介させていただいたので、今回からカスタマイズ話をしたいと思います。

Hadoopのincludeファイル、excludeファイルにハマった・・・。
今日はそのお話をさせていただきます。

話を始める前にincludeファイル、excludeファイルとはなんですかを説明しないとですね。
このincludeファイルとexcludeファイルはサーバーのslave接続を終了させる際に、ノードからデータを退避して脱退させる為に使用します。

includeファイルはデータノードへの接続を許可されるデータノード、JobTrackerに接続可能なTaskTrackerのホスト名を記述するファイルです。includeファイルはdfs.hostsプロパティとmapred.hostsプロパティで指定します。

excludeファイルはデータノードへの接続を許可しないデータノード、JobTrackerに接続させないTaskTrackerのホスト名を記述するファイルです。excludeファイルはdst.hosts.excludeプロパティとmapred.hosts.excludeプロパティでシステムで指定します。

includeファイルとexcludeファイルを指定した場合のノード接続のルールは以下の通りです。

includeファイルexcludeファイル扱い
含まれていない含まれていないノードへ接続不可
含まれていない含まれているノードへ接続不可
含まれている含まれていないノードへ接続可
含まれている含まれているノードは接続可で、脱退させられる

JobTracker接続へのルールは以下の通りです。

includeファイルexcludeファイル扱い
含まれていない含まれていないTrackerへ接続不可
含まれていない含まれているTrackerへ接続不可
含まれている含まれていないTrackerへ接続可
含まれている含まれているTrackerへ接続不可

Hadoopにはmastersファイル、slavesファイルという設定ファイルがあり、slavesファイルとincludeファイルには同じような内容となりますが役割が異なります。

slavesファイルは、Hadoopのデーモン起動終了といったオペレーションの制御スクリプトで参照されます。

includeファイルは、Hadoopデーモンがノード接続、Tracker接続の許可不許可を確認するために参照しています。

ここまで偉そうに説明しましたが、includeファイル、excludeファイルのことは最近まで知らずデータの退避はできないと思っていました。
というのは、Hadoopにslave接続させるサーバーを増やしていく過程ではexcludeファイルはもちろん、includeファイルも必要なかったのです。

というのも、dfs.hostsプロパティ、mapred.hostsプロパティでincludeファイルを明示的に指定していなければ、ノード、Trackerへの接続はデフォルト許可だったんです。

うぅ~ん、まぁ、いい訳ですね。

さてさて、では、includeファイル/excludeファイルを指定してslavesサーバーを正しく脱退させる手順をご紹介していくことにします。

サーバー追加手順

  1. includeファイルに追加するslaveサーバーのホスト名を追記し、Hadoopサーバー全台に設定を同期します。
    $ echo "hdslave3.domain" >> /home/hadoop/hadoop/conf/hosts.include
    $ /home/hadoop/rsync.sh
    
    ※includeファイルにはサーバーのFQDNを記述します。
    ※rsync.shは前回のHadoopセットアップ手順を参照してください。

  2. hadoopコマンドでネームノードを更新します。
    $ /home/hadoop/hadoop/bin/hadoop dfsadmin -refreshNodes
    
  3. slavesファイルに追加するslaveサーバーのホスト名を追記し、設定を同期します。
    $ echo "hdslave3" >> /home/hadoop/hadoop/conf/slaves
    $ /home/hadoop/rsync.sh
    
  4. Hadoopシステムを再起動します。
    $ /home/hadoop/hadoop/bin/stop-all.sh
    $ /home/hadoop/hadoop/bin/start-all.sh
    

サーバー脱退手順

  1. excludeファイルにslave接続から脱退させるサーバーのホスト名を追記し、設定を同期します。
    $ echo "hdslave3.domain" >> /home/hadoop/hadoop/conf/hosts.exclude
    $ /home/hadoop/rsync.sh
    
  2. HadoopシステムのMapReduceクラスタを再起動します。
    $ /home/hadoop/hadoop/bin/stop-mapred.sh
    $ /home/hadoop/hadoop/bin/start-mapred.sh
    
  3. hadoopコマンドでネームノードを更新します。
    $ /home/hadoop/hadoop/bin/hadoop dfsadmin -refreshNodes
    
  4. 脱退中のデータノードからデータが退避されるのをひたすら(弊社の規模では1?2日)待ちます。
    データが退避できたかどうかは、以下のコマンドで確認します。
    $ hadoop/home/hadoop/bin/hadoop dfsadmin -refreshNodes
    
    StateがDecommission in progress から Decommissionedへ変更したらデータ退避完了です。

  5. includeファイル、excludeファイル、slavesファイルから脱退させるサーバーのホスト名を削除し、設定を同期します。
    $ grep -v "hdslave3" /home/hadoop/hadoop/conf/hosts.include >> /home/hadoop/hadoop/conf/hosts.include
    $ grep -v "hdslave3" /home/hadoop/hadoop/conf/hosts.exclude >> /home/hadoop/hadoop/conf/hosts.exclude
    $ grep -v "hdslave3" /home/hadoop/hadoop/conf/slaves >> /home/hadoop/hadoop/conf/slaves
    $ /home/hadoop/rsync.sh
    
  6. Hadoopシステムを再起動します。
    $ /home/hadoop/hadoop/bin/stop-all.sh
    $ /home/hadoop/hadoop/bin/start-all.sh
    
  7. hadoopコマンドでネームノードを更新します。
    $ /home/hadoop/hadoop/bin/hadoop dfsadmin -refreshNodes
    

なんと、手順説明までで時間切れになってしまいました。
ハマったお話は次回とさせていただきます。
ではでは

2011/01/17

Hadoopのカスタマイズ



はじめましてミツバチワークスleonです。
TECHチームの一員としてブログ連載をさせていただく機会をいただきました。

テーマはHadoopカスタマイズにします。

Hadoopは既にstoneさんが現在の運用で使っているテクニックを記事にされています。
そこで私はHadoopのカスタマイズについてご紹介させていいただくことにします。
私自身がHadoop初心者ということもあり、私は初歩からスタートさせていただこうと思います。お付き合いのほどよろしくお願いします。

Hadoopとは?から始めるのが礼儀かと思いますが、そこはwikipediaなどにお任せします。ご容赦ください。

何はともあれ立ち上がっているサービスがないと話を始められません。
そこで、最初の最初はHADOOPのセットアップ手順のご紹介です。

実際、弊社ではHadoopに十数台のサーバーを投入していますが、わかりやすいように最小限のサービス構成で紹介します。
数十台で運用していても基本は同じです。
master : hdmaster
slave : hdslave1, hdslave2
client : hdclient1

なお、いくつか前提条件があります。
・ユーザーはhadoopを用意(homedirは/home/hadoop)
・ホスト間のhadoopユーザーはパスフレーズでsshログインできる
・サーバーのスペック差分なし
・サーバーにはJava 1.4.2のJREがインストールされている

Hadoopはversion 0.18.3を使用しています。現在の最新versionは0.21.0でいまから紹介するにはずいぶん古いですね。
弊社がHadoopを導入した当時でも最新は0.18ではなかったんですが、あえて0.18.3を採用したんだそうです。
これにはちょっとした経緯があります。
stoneさんがHadoop記事でも触れてますが、我々はhadoop-streamingを利用してPerlで作成したmapperとreducerを使用しています。hadoop-streamingは当時、0.18.3が一番安定していたんだそうです。
なんでも新しいものがよいという訳ではないんですね。勉強になります。

では、Hadoopセットアップ手順です。どうぞ

1. HADOOPセットアップ

1.1 マスターセットアップ

$ ssh hdmaster
$ sudo su - hadoop
$ umask 002
$ wget http://ftp.kddilabs.jp/infosystems/apache/hadoop/core/stable/hadoop-0.18.3.tar.gz
$ tar xzf hadoop-0.18.3.tar.gz
$ mv hadoop-0.18.3 hadoop
これで/home/hadoop/hadoopに展開されています。

$ mkdir -p /home/hadoop/tmp
$ mkdir -p /home/hadoop/filesystem/dfs
$ mkdir -p /home/hadoop/logs
Hadoopで使用するディレクトの作成です。

$ vi /home/hadoop/hadoop/conf/masters
hdmaster
マスターのhostです。

$ vi /home/hadoop/hadoop/conf/slaves
hdslave1
hdslave2
スレーブのhost一覧です。スレーブを追加したらこのファイルにhostを追加していきます。

$ vi hadoop/conf/hadoop-env.sh
>>>
export JAVA_HOME=/usr/java/latest

export HADOOP_LOG_DIR=/home/hadoop/logs
>>>
Hadoop環境にJavaのパスを設定します。

$ vi hadoop/conf/hadoop-default.sh
>>>

<property></property>
  <name>hadoop.tmp.dir</name>
  <value>/home/hadoop/tmp/hadoop-${user.name}</value>
<description>A base for other temporary directories.</description>

>>>
Hadoopの一時ディレクトリのパスを設定します。

$ vi hadoop/conf/hadoop-site.sh
>>>
<configuration>

<property>
  <name>fs.default.name</name>
  <value>hdfs://hdmaster:9000</value>
</property>

<property>
  <name>mapred.job.tracker</name>
  <value>hdmaster:9001</value>
</property>

<property>
  <name>dfs.name.dir</name>
  <value>/home/hadoop/filesystem/dfs/name</value>
</property>

<property>
  <name>dfs.data.dir</name>
  <value>/home/hadoop/filesystem/dfs/data</value>
</property>

<property>
  <name>mapred.system.dir</name>
  <value>/home/hadoop/filesystem/mapred/system</value>
</property>

<property>
  <name>mapred.local.dir</name>
  <value>/home/hadoop/filesystem/mapred/local</value>
</property>

<property>
  <name>dfs.replication</name>
  <value>1</value>
</property>

<property>
  <name>mapred.job.tracker.handler.count</name>
  <value>20</value>
</property>

<property>
  <name>mapred.map.tasks</name>
  <value>50</value>
</property>

<property>
  <name>mapred.reduce.tasks</name>
  <value>10</value>
</property>

</configuration>
>>>
サービス起動に必要な最低限の設定です。

1.2スレーブ&クライアントのセットアップ

$ ssh hdmaster
$ sudo su - hadoop
$ rsync --delete --progress -av hadoop hdslave1:
$ rsync --delete --progress -av hadoop hdslave2:
$ rsync --delete --progress -av hadoop hdclient:
基本的にスレーブとクライアントは同じ設定です。今後、スレーブやクライアントを増えていくことが予想される場合、以下のようにスクリプト準備しておくと便利です。
$ vi /home/hadoop/rsync.sh
#!/bin/sh

HOST_LIST="hdslave1, hdslave2, hdclient1"

cd /home/hadoop
for host in $HOST_LIST ; do
  rsync --delete --progress -av hadoop $host:
done
$ chmod 755 /home/hadoop/rsync.sh

1.3 サービスの起動

$ ssh hdmaster
$ sudo su - hadoop
$ /home/hadoop/hadoop/bin/hadoop namenode -format
$ /home/hadoop/hadoop/bin/bin/start-all.sh
namenodeのフォーマットはセットアップの際に1度行っておけばOKです。

1.4 動作確認

$ ssh hdclient
$ sudo su - hadoop
$ /home/hadoop/hadoop/bin/hadoop jar hadoop-0.18.3-examples.jar sudoku src/examples/org/apache/hadoop/examples/dancing/puzzle1.dta

これで準備OKです。
なお、ここまでは先人の叡智です。
私には既に動いているHadoopが与えられました。

私がHadoopに初めて触ったのはスレーブ追加作業からでして、そこで初ハマりがありました。
HADOOPスレーブの追加はスレーブのセットアップ後、マスターでサービスを再起動するだけだと教えていただいたのですが、サービス停止のスクリプトを実行してもスレーブで立ち上がっているjavaプロセスが終了しなかったんです。
そのときは、javaプロセスを直接killしちゃったんです。もう時効ですよね。

このあともHadoopのカスタマイズで四苦八苦を繰り返しています。
次回からは実体験のカスタマイズでハマったこと、気がついたことをご紹介していきたいと思います。

それでは

2010/10/13

HadoopによるApacheのログ解析の実際


こんにちは、ミツバチワークス stoneです。

今日は、DECOLOGで行われている、Apacheのログ解析について、
ご紹介してみようかと思います。

現在、DECOLOGでは、リバースプロキシが8台あって、
その8台の1日のApacheのログは、全部で、200Gバイト以上になっています。
これを、13台のHadoopのスレーブノードで解析を行っています。

全体の流れとしては、
1) リバースプロキシからHDFSにログを転送
2) 解析用のサーバーで、HDFSにログの転送が終わるのを監視
3) ログの転送が終わったら、Hadoopを起動、解析
4) Hadoopの解析結果をデータベースに保存

以下では、各々のステップを個別に見て行くことにしますね。

1. リバースプロキシからHDFSにログを転送


当初、Hadoopのプロセスが立ち上がっていないと、HDFSにはアクセスできない、
と思い込んでいたので、解析用のサーバーが、
1) 各リバースプロキシからのログをscpで収集
2) 収集したログをHDFSへ
というステップを踏んでいたのですが、
このステップだけで、昼過ぎまでかかるようになったため、
現在では、リバースプロキシから直接HDFSへ展開するように変更しました。

Apacheのログは、cron.dailyで起動されるlogroateでローテーションがかかるので、
そのcron.dailyの最後にHDFSへの展開するスクリプトが起動するようになっています。

/etc/cron.daily/zz_log_to_hdfs.sh
#! /bin/sh

HOST=`hostname -s`
TODAY=`date +%Y%m%d`

HADOOP='/path/to/hadoop/bin/hadoop dfs'

HDFS_BASE='hdfs://hadoop-master:9000/accesslog'
LOG_FILE="$HDFS_BASE/$TODAY/$HOST"
SIGNUP_FILE="$HDFS_BASE/signup/$HOST"

LOCAL_SRC="/path/to/log/access_log.1"

$HADOOP -copyFromLocal $LOCAL_SRC $LOG_FILE
$HADOOP -chmod 777 $LOG_FILE
$HADOOP -touchz $SIGNUP_FILE
$HADOOP -chmod 777 $SIGNUP_FILE


2. 解析用サーバーで、HDFSにログの転送が終わるのを監視


各リバースプロキシから直接HDFSへ展開することで、時間は大幅に短縮できたのですが、
1点、問題がありました。
それは、
リバースプロキシからのログの転送がいつ終わったのか、わからない
という問題です。

そのため、ちょっと、工夫をして、HDFS上にsignupディレクトリというモノを作りました。
各リバースプロキシは、ログの転送が終わったタイミングで、
このsignupディレクトリにホスト名のファイルをtouchします。
前出のスクリプトの下から2行目でそのtouchをしています。
$HADOOP -touchz $SIGNUP_FILE
解析用のサーバーは、適当な時間に起動した後、このsignupディレクトリを監視して、
すべてのサーバーが出そろった段階で、次のHadoopでの解析のステップに進みます。
(signupディレクトリは、転送が始まる前に事前に空にしています。)


3. Hadoopでの解析


3.1 Key-Valueのマッピング


ご存知のように、Hadoopでは、mapのステップで、
あるデータを「key => value」の形にマッピングして、
reduceのステップで、同一のkeyの値を取りまとめてくれます。

DECOLOGでは、以下のような切り口でログ解析をしています
・1時間ごとのHit数、PV数
・ページグループ毎のPV数
・携帯キャリアごとのHit数

具体的なキーは、こんな感じです。
YYMMDD-hourly_hit-HH (時間ごとのヒット数)
YYMMDD-hourly_page-HH (時間ごとのPV数)
YYMMDD-page_group-PAGE (ページグループごとのHit数)
YYMMDD-page_group_pv-PAGE (ページグループごとのPV数)
YYMMDD-user_agent-CARRIER (携帯キャリアごとのHit数)

アクセスされたURLから、
・ページビューなのか画像なのか?
・ページビューの場合、HTTPステータスが200か?
・どのページグループに属するURLか?
を判別しています。

なので、例えば、記事ページへのアクセスがあった場合、
aaa.bbb.ccc.ddd - - [13/Oct/2010:11:05:09 +0900] "GET /en/00000/00000 HTTP/1.1" 200 999 "-" "DoCoMo/2.0 D905i(c100;TB;W24H17)" "-"
という感じのログになるのですが、これをmapperに通すと、
20101013-hourly_hit-11    1
20101013-hourly_page-11    1
20101013-page_group-Entry    1
20101013-page_group_pv-Entry    1
20101013-user_agent-docomo    1
という結果が出力されることになります。

3.2 mapper/reducerの記述


実際のmapperとreducerは、Javaではなく、Perlで記述しています。
Perlで記述したスクリプトをhadoop-streamingを利用して、
mapper/reducerとして、稼働させています。

hadoop-streamingは、データの入出力に標準入力/標準出力を使用するため、
動作の検証は、実際のログデータをmapperに標準入力から流し込んだときに、
想定通りの出力が得られるか?で、行うことが出来ます。
(reducerは、そのmapperの出力を流しこめばOKです。)


4.解析結果をDBに保存


Hadoopの解析結果は、「part-0001」みたいなファイル名でHDFS上に出力されているのですが、
これをうまい具合に取り出す方法がよくわかっていなくて、
現状、以下のようにして、ローカルに取り出しています。
$HADOOP dfs -cat "$OUTPUT_DIR/part-*" | grep $YESTERDAY > $TMP_FILE

保存するデータベースは、以下のようなテーブル定義になってます。
(データベースは、MySQLです)
カラム名データタイプ
log_datechar(8)
sectionenum("hourly_hit", "houly_page" ...)
section_keyvarchar(255)
log_countint unsigned

解析結果は、上記のmapperの出力に準じた形になっているので、
20101013-hourly_hit-11    999
と出力されていて、これを、
log_date: 20101013
section: houry_hit
section_key: 11
log_count: 999
と保存しています。


ごく初期の段階では、webalizerを使って、サーバー1台で解析をしていたですが、
解析が夜までかかるようになったため、Hadoopによる解析に切り替えました。
当時(2009年4月ごろ)は、サーバーの負荷対策で、ほぼ毎日のようにサーバーの増設や、役割の変更、
それに伴うプログラムの修正を行っていました。
その際、その対策の根拠となるのが、前日のZABBIXのグラフとアクセス解析の結果でした。
なので、アクセス解析が夜までかかると、負荷対策の立案にとても困った記憶があります。
参考までに、切り替えを行ったタイミングでは、1日あたり、だいたい4700万PV/2億6000万Hitでした。
(ログのサイズは、データが残っていませんでした。)
現在は、1日あたり、だいたい2億PV/15億Hitなのですが、お昼までには解析が終了しています。


蛇足ですが。。。。。。。。。。。。。
ウチでは、このシステムを「HAL」と読んでます。
「Hadoop for Apache Log」の頭文字をとって「HAL」としていますが、
本当は、HAL9000からパクって、「Hadoop for なんちゃら」は、
後からこじつけました。
が、他の年下のエンジニアからは、「何スか?HAL9000って?」と言われ、
ジェネレーションギャップを感じました。
(え?知らない方が多数派ですかね、これって?)

では、また次回に♪