Apache Hadoop と Dojo による経済的なビジネス・インテリジェンス: 第 1 回 既存のデータを Apache Hadoop を使って高速処理する

Web ベースのレポート作成アプリケーションに情報を供給する

自分のビジネスを理解することは常に重要なことです。企業に理想通りのアジリティー (俊敏性) があったとしても、進むべき正しい方向性を知らなければ、目を閉じたまま車を運転しているのと同じことです。ビジネス・インテリジェンス・ソリューションには法外なコストがかかるだけでなく、ソリューションのシステムで処理できるようにデータを改良しなければならないこともよくあります。けれどもオープンソースの技術が、独自のビジネス・インテリジェンス・レポートを今までになく簡単に作成できるようにします。2 回連載の第 1 回目となるこの記事では、Apache Hadoop を使って既存のデータを高速処理し、Web ベースのレポート作成アプリケーションで容易に利用できるデータに変換する方法を学びます。

Michael Galpin, Software architect, eBay

Michael_GalpinMichael Galpin は eBay のアーキテクトであり、developerWorks に頻繁に寄稿しています。彼は JavaOne、EclipseCon、AjaxWorld など、さまざまな技術カンファレンスで講演を行っています。彼が現在取り組んでいることを知るには、Twitter で @michaelg をフォローしてください。



2010年 8月 17日

前提条件

この記事では Apache Hadoop を使って大量の扱いにくいデータを処理します。記事で使用した Apache Hadoop は0.20 リリースです。Hadoop を使用するのに必要となる Java 開発キットには、JDK 1.6.0_20 を使用しました。Hadoop 自体にも、例えば SSH および RSYNC がインストールされていなければならないなど、独自の前提条件がありますので、Hadoop の資料ですべての前提条件を調べてください。記事で使用するツールへのリンクは、「参考文献」に記載されています。

Apache Hadoop とビジネス・インテリジェンス

どのような種類のビジネスに携わっていようとも、顧客を理解すること、そして顧客がビジネスのソフトウェアとどのように対話するのか理解することは、いくら強調しても強調し足りないほど重要です。設立されたばかりの企業や歴史の浅い企業の場合には、繰り返し迅速に顧客へ対応できるように、何が機能していて、何が機能していないのかを理解する必要があります。これは、ある程度成熟した企業にも言えることです。ただし、そのような会社にとっては、ビジネスを微調整することや、新しいアイデアをテストすることがより重要になります。いずれにしても、ユーザーの挙動を理解するためには、何らかの作業が必要になります。

まずはユーザーを理解するところから始めること

ユーザーを理解するのは、手に取るように簡単なことも、これ以上ないほど困難なこともあります。ユーザーを理解するということは、例えばユーザーが Web ページのどの部分をクリックしているのかといったユーザーの挙動を理解することです。あるいは、ページ上に同じタイプのデータがたくさんあったほうが、ユーザーは対応しやすいのか、またはその逆なのかを理解するという場合もあります。その場合のデータの量とは、ページ上の検索結果の件数や、1 つの項目に関する詳細情報の量が考えられます。けれども、ユーザーを理解する上で重要なことは、これだけではありません。例えば、ユーザーがどこにいるのかを知りたい場合もあります。この記事ではそのような例の 1 つとして、ユーザーが使用している Web ブラウザーの種類を調べる場合を検討します。この情報は少なくとも、エンジニアリング・チームが開発作業を最適化する上で非常に有益です。

必要なデータの出力

測定および分析する対象が明らかになったら、ユーザーの挙動または情報、あるいはその両方を数値化するために必要なデータを必ずアプリケーションが出力しているようにする必要があります。幸運にも、このデータはすでに出力されているかもしれません。例えば、必要なすべての情報が、システム内のある種のトランザクションの一部として出力され、データベースに記録されるようになっている場合が考えられます。あるいは、アプリケーションやシステム・ログに書き込まれている場合もあります。けれども、このように幸運なケースはめったにありません。そこで、必要な情報をどうにか記録するために、システム構成を変更するか、アプリケーションを変更するという作業が必要になります。

非常に対話性の高い Web アプリケーションの場合、この作業には JavaScript を作成するという作業も含まれることになるでしょう。JavaScript によって Ajax 呼び出しを行うか、動的にビーコン (通常、ある種のイベント情報を持つ 1x1 の画像で、URL のクエリー・ストリングに追加されます) を投じて、ユーザーが Web アプリケーションとどのように対話しているかを把握するためです。この記事の例では、Web アプリケーションにアクセスするために使用されているブラウザーのユーザー・エージェントの情報を取得する必要があります。この情報は既に収集済みかもしれませんが、そうでない場合でも、通常は簡単に収集することができます。例えば Apache Web サーバーを使用しているとしたら、httpd.conf ファイルにほんの数行を追加するだけの話です。リスト 1 に、その一例を記載します。

リスト 1. Apache Web サーバーでユーザー・エージェントをログに記録する場合
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" common
CustomLog /dev/logs/apache_access_log common

リスト 1 の構成は、Apache ではかなり典型的な構成です。この構成では、ユーザーの IP アドレス、リクエストされているリソース (ページ、画像など)、リファラー (ユーザーがリクエストを送信したときにアクセスしていたページ)、そしてもちろんブラウザーのユーザー・エージェントなど、多くの有益な情報がログに記録されます。ログに記録する情報は、LogFormat を調整して増やすことも減らすこともできます。

注: 必要な情報を出力するのは簡単なように思えますが、いくつかの落とし穴があります。例えば、ユーザーの挙動に関する情報を大量に収集する必要がある場合、ロギング用の文をアプリケーションに大量に追加することになると思いますが、それによって好ましくない副次的な影響がもたらされることがあります。それは、リクエストの処理時間が長くなることです。Web アプリケーションの場合、これはページが応答するまでに時間がかかることを意味するため、決して好ましいことではありません。さらに、同じ数のリクエストを処理するのに、より多くのアプリケーション・サーバーが必要となる可能性もあるため、明らかにコストがかかります。このような場合には、データがログに記録されるまでエンド・ユーザーが待機しなくても済むように、非ブロック方式のロギング・システムを使用することを検討してください。また、ランダムに選んだ一部のリクエストに関してだけ詳細を記録し、そこから推定するという方法も考えられます。ログに記録する情報量が増えると、その分、ストレージ・スペースもかなり多く必要になってくるため、ロギング専用のハードウェアに投資するのも一考です。明らかにコストがかかることですが、ビジネスをより深く理解すれば、その分大きな価値がもたらされます。

データの高速処理

必要なすべてのデータをどこか都合の良い場所に保存するのは、このプロセスの中では比較的容易なステップです。次に必要なのは、このデータを処理して、より有用なものに変換することです。従来からこの作業がいかに重要であったかを知るには、Web で「business intelligence」を検索するだけで十分です。多数のヒットがあるだけでなく、その多くは有料のソフトウェア・パッケージです。このステップ (そして最後のレポート作成のステップ) を支援するように設計された、非常に高価なソフトウェア・パッケージが世間には数多く出回っているのです。

このような高価なソフトウェア・スイートでさえも、通常は統合作業が必要となります。そしてその作業は、非常に複雑になりかねません (もちろん、ベンダーではこの作業を支援する専門のサービス部門を用意しているのが通常ですが、利用するには料金がかかります)。そこで登場するのが、大量データの高速処理に優れた Hadoop です。テラバイト規模のログ・ファイルと商用サーバーのクラスターがあれば、Hadoop はその威力を発揮します。そしてこれが、この記事で焦点とするステップです。この記事の例では、Hadoop の map/reduce ジョブを作成し、そのジョブによって Apache Web サーバーのアクセス・ログ・ファイルを Web アプリケーションが簡単に利用できる有益な情報からなる小さなデータ・セットに変換し、Web アプリケーションがインタラクティブなレポートを作成できるようにします。そしてこの作業が、プロセスの最後のステップにつながります。

レポートの作成

すべてのデータの処理が迅速に完了すると、非常に価値のある情報が簡潔な形式で残されることになるでしょう。そしてこれらの情報は、データベース内に置かれているか、あるいはどこかのファイラーに XML ファイルとして存在することになるはずです。開発者にとっては、これで問題ありませんが、このデータはおそらくビジネス・アナリストや企業の経営陣の手に渡さなければなりません。彼らが期待するのは、インタラクティブで視覚的に訴えかけるようなレポートです。そこで、エンド・ユーザーがさまざまな方法でデータを加工できるようなレポートを作成すれば、ユーザーにとってより有用なレポートになるだけでなく、開発者が同じデータに基づいて別のレポートを一から作成する手間も省けます。そして当然、誰でも見た目に魅力的なレポートを好むものです。この連載の第 2 回では、Dojo ツールキットを使用してレポートを魅力的に仕上げますが、とりあえず今は、Hadoop でデータを高速処理することに専念します。それでは早速、Apache ログの処理に取り掛かりましょう。


アクセス・ログの分析

Hadoop は、map/reduce パラダイムをベースとします。その概念は、手に負えないような大量のデータのセットを処理して関心のあるデータのみに絞り込み (map ステップ)、結果を集約する (reduce ステップ) というものです。この例では、Apache アクセス・ログを、各種のブラウザーから受け取ったリクエストの数だけが含まれるデータ・セットに変換します。したがって、このプロセスに対する入力はログ・ファイル (おそらく複数のログ・ファイル) です。リスト 2 に、ログ・ファイルに入力される内容の一例を記載します。

リスト 2. ログ・ファイルに入力される内容の例
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/pro_timeEdition.jpg 
HTTP/1.1" 304 - "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Windows; U;
Windows NT 6.1; it; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 ( .NET CLR 3.5.30729)"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/welogo.gif HTTP/1.1" 304 -
 "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Macintosh; U; Intel Mac 
OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/madeonamac.gif HTTP/1.1" 
304 - "http://localhost:8888/MAMP/?language=English" "Mozilla/4.0 (compatible; MSIE 
8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; 
MS-RTC LM 8)"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/bullet.gif HTTP/1.1" 304 -
 "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Macintosh; U; Intel 
 Mac OS X 10_6_3; en-us) AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 
 Safari/533.16"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/valid-xhtml10.png HTTP/1.1"
304 - "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Macintosh; U; PPC 
Mac OS X 10.5; en-US; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 GTB7.0"

上記に記載したような内容が 1 つの非常に大きなログ・ファイルに含まれる場合もあれば、複数のログ・ファイルとしてシステムに入力される場合もあります。Hadoop はいずれにしても、この一連のデータの塊で map の部分を実行します。その結果生成されるのは、map ステップの出力が含まれる中間ファイルのセットです。これらの中間ファイルは、通常はサイズが大幅に小さくなっています。リスト 3 に、出力の例を記載します。

リスト 3. map ステップによって生成された中間出力
FIREFOX 1
CHROME 1
IE 1
SAFARI 1
FIREFOX 1

リスト 3 の例を見ると明らかなように、ログ・ファイルの内容はかなり簡潔になり、検討対象のブラウザーが Microsoft Internet Explorer®、Mozilla Firefox®、Apple Safari®、Google Chrome® の 4 つに絞り込まれています。さらに、ブラウザーのバージョンさえも省かれています (ただしブラウザーのバージョンを省く習慣を付けるのは禁物です。これは特に、Internet Explorer の場合に言えることです)。これらのファイルで reduce ステップを実行すると、最終出力に結果が集約されます。最終出力はリスト4 のようになります。

リスト 4. reduce ステップによって生成された最終出力
{"IE":"8678891",
"FIREFOX":"4677201",
"CHROME":"2011558",
"SAFARI":"733549"}

リスト 4 に記載されているデータは、reduce ステップによって集約されているだけでなく、JSON にフォーマット設定されているので、Web ベースのレポート作成アプリケーションで簡単に使用することができます。もちろん必要であれば、フォーマット設定しないままのデータを出力し、サーバー・サイドのアプリケーションにアクセスさせて Web アプリケーションにデータを提供することも可能です。Hadoop と Dojo はいずれも、このステップに一切の制約または要件を設けていません。

ここまでの内容が単純で理解しやすいと思えたようであれば幸いですが、Hadoop は map/reduce パラダイムをカプセル化するフレームワークというだけではありません。Hadoop はこのパラダイムを分散方式で実装します。つまり、これらのログ・ファイルが非常に大きいものだとしても、その処理は、Hadoop クラスター内の複数のマシン (ノード) の間で分割されることになります。Hadoop クラスターは通常、水平スケーリングが行われるため、より多くのデータを処理したい場合、あるいはデータの処理速度を上げたい場合には、マシンを増やすようにします。クラスターを構成する方法、および構成を最適化する方法 (各種のマシンに送信できるようにデータを分割する方法など) は、それ自体が大きなトピックなので、この記事で詳細を探ることはしません。代わりにこの記事では、map、reduce、そして出力のフォーマット設定の部分に焦点を絞って説明します。まずは、map フェーズから始めます。

ステップ 1: map フェーズ

Hadoop ランタイムは処理対象のデータ (ログ・ファイル) を小さな塊に分割してからクラスター内の各ノードに配布します。これらのデータの塊には、map 関数が適用される必要があります。リスト 5 に、ログ・アナライザーの例で map 関数を指定する方法を示します。

リスト 5. サンプル・アクセス・ログの Mapper
public class LogMapper extends Mapper<Object, Text, Text, IntWritable> {
    private final static IntWritable one = new IntWritable(1);
      private static final Pattern regex = 
          Pattern.compile("(.*?)\"(.*?)\"(.*?)\"(.*?)\"(.*?)\"(.*?)\"");    
    @Override
    protected void map(Object key, Text value, Context context)
            throws IOException, InterruptedException {
        Matcher m = regex.matcher(value.toString());
        if (m.matches()){
            String ua = m.group(6).toLowerCase();
            Agents agent = IE; // default
            if (ua.contains("chrome")){
                agent = CHROME;
            } else if (ua.contains("safari")){
                agent = SAFARI;
            } else if (ua.contains("firefox")){
                agent = FIREFOX;
            }
            Text agentStr = new Text(agent.name());
            context.write(agentStr, one);
        }
    }
}

Hadoop は、タイプ・セーフな map 関数および reduce 関数を作成するために、Java™ の Generics をフルに活用します。リスト 5 は、その一例です。ここで継承しているのは、Hadoop フレームワークの org.apache.hadoop.mapreduce.Mapper クラスであることに注意してください。このクラスは (key,value) のペアを入力として取り、その入力を新しい (key,value) ペアにマッピングします。パラメーター化は、入力および出力の (key,value) ペアの型に基づいて行われます。つまり、この例では (Object, Text) 型の (key,value) ペアを受け取って、このペアを (Text,IntWritable) 型の (key,value) ペアにマッピングします。実装するメソッドは唯一、map しかありません (このメソッドは、入力された (key,value)' ペアの型に基づいてパラメーター化されます)。

リスト 5 の例では、ログ・ファイルの 1 行 (リスト 2 に記載されている行) が入力 Text オブジェクトとして map メソッドに渡されます。このオブジェクトに対して正規表現を実行し、ユーザー・エージェントのストリングを抽出します。次にストリング・マッチングを使用して、ユーザー・エージェントのストリングを Agents という名前の Enum にマッピングします。そして最後に、このデータが Context オブジェクトに書き込まれます (このオブジェクトの write メソッドは、map によって生成された (key,value) ペアの型に基づいてパラメーター化されます)。write 文が生成するのは、リスト 3 に記載したような単一の行です。これで、reduce フェーズに移る準備ができました。

ステップ 2: reduce フェーズ

Hadoop フレームワークは map フェーズの出力を取り、それを中間ファイルに書き込みます。reduce フェーズでは、この中間ファイルが入力の役割を果たします。データが集約されて、より有益なものに変換されるのは、このフェーズです。リスト 6 に、reduce 関数を指定する方法を示します。

リスト 6. サンプル・アクセス・ログの Reducer
public class AgentSumReducer extends 
    Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();
    @Override
    public void reduce(Text key, Iterable<IntWritable> values, 
          Context context) throws IOException, InterruptedException {
        int sum = 0;
        for (IntWritable val : values) {
            sum += val.get();
        }
        result.set(sum);
        context.write(key, result);
    }
}

今度は org.apache.hadoop.mapreduce.Reducer クラスを継承して、その reduce メソッドを実装しています。リスト 6 を見るとわかるように、ここでも Generics を利用して、このコードのすべてをタイプ・セーフにしています。ただし、Mapper によって生成された型と Reducer で使用される型が一致していることを確認してください。reduce メソッドは Iterable に渡され、その型は入力された (key, value) ペアの型になります。必要な作業は、これらの値を加算することだけです。その結果を、Mapper 内部で行ったように Context オブジェクトに書き込みます。次は、出力のフォーマットを設定します。

ステップ 3: 出力のフォーマット設定

このステップは、実際にはオプションで、Hadoop からの出力 (各行がスペースで区切られた名前と値で構成された出力) をそのまま使うこともできます。しかしご存知のとおり、このデータはレポートを作成する Web アプリケーションで使用できるようにしなければなりません。そこで、このデータをリスト 4 に記載したような JSON データにフォーマット設定します。出力のフォーマットを JSON フォーマットに指定する方法については、リスト 7 を見てください。

リスト 7. サンプル・アクセス・ログの OutputFormat
public class JsonOutputFormat extends TextOutputFormat<Text, IntWritable> {
    @Override
    public RecordWriter<Text, IntWritable> getRecordWriter(
            TaskAttemptContext context) throws IOException, 
                  InterruptedException {
        Configuration conf = context.getConfiguration();
        Path path = getOutputPath(context);
        FileSystem fs = path.getFileSystem(conf);
        FSDataOutputStream out = 
                fs.create(new Path(path,context.getJobName()));
        return new JsonRecordWriter(out);
    }

    private static class JsonRecordWriter extends 
          LineRecordWriter<Text,IntWritable>{
        boolean firstRecord = true;
        @Override
        public synchronized void close(TaskAttemptContext context)
                throws IOException {
            out.writeChar('{');
            super.close(null);
        }

        @Override
        public synchronized void write(Text key, IntWritable value)
                throws IOException {
            if (!firstRecord){
                out.writeChars(",\r\n");
                firstRecord = false;
            }
            out.writeChars("\"" + key.toString() + "\":\""+
                    value.toString()+"\"");
        }

        public JsonRecordWriter(DataOutputStream out) 
                throws IOException{
            super(out);
            out.writeChar('}');
        }
    }
}

リスト 7 のコードは多少複雑に見えるかもしれませんが、実際にはごく単純なものです。ここで継承している org.apache.hadoop.mapreduce.lib.output.TextOutputFormat クラスは、データをテキストとして出力するためのユーティリティー・クラスです (ここでも Generics が使用されていることに注意してください。つまり、生成される型が、Reducer クラスによって生成された (key,value) ペアの型と必ず一致するようにします)。ここで必要な作業は、(同じくパラメーター化された) org.apache.hadoop.mapreduce.RecordWriter インスタンスを返す getRecordWriter メソッドを実装することだけです。JsonRecordWriter のインスタンスを返すと、この内部クラスがリスト 3 のデータ行を取得して、リスト 4 のデータ行を生成します。その結果生成された JSON データは、レポート作成アプリケーションの Dojo ベースのコードでそのまま使用することができます。

今回 Hadoop によって生成されたインテリジェンス・データは、Dojo を使用して作成されたインタラクティブなレポートで使用されます。その作成方法については、この連載の第 2 回を読んでください。


まとめ

この記事では、大量のデータを Hadoop で高速処理する単純な例を紹介しました。ビジネスについての貴重な洞察を提供する大量のデータを処理するには、Hadoop を使用するのが簡単な方法となります。この記事では Hadoop の map/reduce 機能をそのまま使用しましたが、Hadoop をさまざまなケースで使用するようになると、Hadoop をベースに作成され、map/reduce ジョブを簡単に作成できるようにする高次のフレームワークを調べてみたくなるはずです。そのような優れたフレームワークとしては、オープンソースの Pig と Hive の 2 つが挙げられます。この 2 つのフレームワークも同じくApache プロジェクトです。両方とも、より宣言型の構文を使用し、プログラミングの手間を大幅に省きます。Pig はYahoo® の中核的な Hadoop チームのメンバーが開発したデータ・フロー言語である一方、Hive はそれよりも SQL に似ていて、Facebook で開発されました。この 2 つのどちらのフレームワークを使用しても、あるいはより基本的な map/reduce ジョブを使用しても、Web アプリケーションで使用できる出力を生成することができます。


ダウンロード

内容ファイル名サイズ
Article source codeAccessLogAnalyzer.zip6KB

参考文献

学ぶために

製品や技術を入手するために

  • Apache Hadoop を入手してください。この記事ではバージョン 0.20 を使用しました。
  • Dojo ツールキットをダウンロードしてください。
  • Java SDK を入手してください。この記事では Java SDK を使用しました。
  • Hadoop をベースに作成された Pig および Hive フレームワークについて調べてください。
  • IBM 製品の評価版をダウンロードして、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を使ってみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=521204
ArticleTitle=Apache Hadoop と Dojo による経済的なビジネス・インテリジェンス: 第 1 回 既存のデータを Apache Hadoop を使って高速処理する
publish-date=08172010