読者です 読者をやめる 読者になる 読者になる

R と Treasure Data で Web サーバのアクセスログ解析

この記事では、Apache HTTP Server で稼働している Web サーバへ Fluentd (td-agent) を導入し、Treasure Data でアクセスログを収集し、R で収集したデータのアクセスログ解析を行うまでの手順を解説します。

Treasure Data (トレジャーデータ) とは

Treasure Data は Cloud 型 DWH (Data Warehouse) サービスの一つで、Cloud 上で SaaSとしてビッグデータの格納、処理を利用できるサービスです。 (Treasure Data 社では BigData-as-a-Service と呼んでいます)

同様のサービスでは、Amazon Web ServicesAmazon RedshiftElastic MapReduce 等がありますが、Treasure Data の特長で、バッチで大量のデータをバルクロードするのはもちろん、fluentd (td-agent) を用いて各種ログデータを効率よく送信し、格納できることから、リアルタイムで出力されるログデータの格納に向いている特長があります。

具体的には、以下のようなデータです。

  • Web サーバのアクセスログやアプリログ
  • 各種サーバやネットワーク機器から出力される Syslog
  • POS、EC サイトやオンラインゲームの購買履歴
  • 自動車や組み込み機器、工作機械等マシンデータ等のシリアルポート等から得られるログ

f:id:hiratake55:20131204233157p:plain

Treasure Data の活用イメージ

今回の分析例

今回の分析例は Apache で稼働している Web サーバのログを Treasure Data に転送するよう設定し、R を用いて Treasure Data に格納されたデータの抽出を行い、Rを使ってグラフ化するものです。

本例では、データソースにおなじみの統計分析ソフトウェア R 専門検索エンジンseekRをストアしているサーバのアクセスログを使用しました。

f:id:hiratake55:20131204233221p:plain

分析処理のフロー(イメージ)

今回使用した Web サーバの環境

本例で、取得元として使用した Web サーバの環境は以下です。

1. Treasure Data に テーブル を作成

Treasure Data へのユーザ登録

まず、Treasure Data 社サイトにアクセスし、ユーザ登録を行います。登録完了後、Treasure Data の Console にログインします。

DB とテーブル の作成

Console にログイン後、以下画面にて DB を作成します。本例では、「apache」という名前の DB を作成しました。

f:id:hiratake55:20131205162325p:plain

次にテーブルを作成します。本例では、「access」というテーブルを作成しました。

f:id:hiratake55:20131205162327p:plain

なお、Treasure Data では Column を自動的に認識し必要に応じて追加する機能を持っているため、Column を手動で作成する必要はありません。(もちろん、通常の RDBMS と同様に手動で作成することも可能です)

2. Web サーバ に fluentd (td-agent) をインストール

Treasure Data のクライアントである fluentd (td-agent) をログ採取対象の Web サーバにインストールする必要があります。なお、td-agent と fluentd の違いですが、Treasure Data 社によると、td-agent は fluentd の機能強化版、安定版という位置づけです。

手順は以下のサイトを参考にしました。

Installing Fluentd Using rpm Package | Fluentd

td-agent のインストール

curl -L http://toolbelt.treasuredata.com/sh/install-redhat.sh | sh

Apache アクセスログの Permission 変更

ログディレクトリを other (他ユーザ) でも Read できるよう、パーミッションを変更します。

chmod 0755 /var/log/httpd

設定ファイル作成

設定ファイルにて、Source (ログの場所) と Match (ログ抽出条件) を指定します。

vi /etc/td-agent/td-agent.conf
<source>
  type tail
  path /var/log/httpd-access.log #Apacheのアクセスログの場所
  pos_file /var/log/td-agent/httpd-access.log.pos # Position ファイルの場所
  tag apache.access #格納先の DB 名、テーブル名
  format apache2 # フォーマットをApache2形式に設定 (※)
</source>

<match **>
  type tdlog
  apikey <Treasure Data API key> # Trasure data の APIキー
  auto_create_table
  buffer_type file
  buffer_path /var/log/td-agent/buffer/td
</match>

(※) Format は以下のようにカスタムで定義することも可能です。私は Apache のカスタムログで以下のようにバーチャルホスト名をログに組み込んでいるため、Format を独自に定義しています。

httpd.conf

LogFormat "%V %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

td-agent.conf

format /^(?<vhost>[^ ]*) (?<host>[^ ]*) (?<logname>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$/
time_format %d/%b/%Y:%H:%M:%S %z

サービス起動、自動起動設定

service td-agent start
chkconfig td-agent on

Console からデータが登録されたことを確認する

Fluentd の仕組み上、反映されるまでに少々時間がかかります。データ反映後、Console から以下のようにデータが参照できます。

f:id:hiratake55:20131205075817p:plain

3. R から Treasure Dataに接続

R からは、RJDBC 経由で Treasure Data に JDBC 接続します。 RJDBC は内部で rJava パッケージを用いており、事前に、Oracle 社サイトから適切な bit の JRE をインストールしておく必要があります。

Treasure Dataに接続する為の R コードは以下です。今回、約 14 万件のレコードから抽出処理を行いましたが、Hive の抽出、集計処理に要した時間はおよそ 1-2 分 でした。

Treasure Data の JDBC ドライバをダウンロードする

下記ページから Treasure Data の JDBC ドライバをダウンロードして、ローカルに保存します。

rJava, RJDBC パッケージをインストールする

install.packages("rJava")
install.packages("RJDBC")

RJDBC パッケージを使って Treasure Data に接続する

library(rJava)
library(RJDBC)
drv <- JDBC("com.treasure_data.jdbc.TreasureDataDriver",
            "./lib/td-jdbc-0.2.3-jar-with-dependencies.jar", # JDBCドライバの場所
            identifier.quote="`")
conn <- dbConnect(drv,
                  "jdbc:td://api.treasure-data.com/<DB name>",
                  "<登録した Email Address>",
                  "<登録した Password>")

HiveQL を利用してデータセットを取得する

HiveQL は HadoopMySQL ライクな SQLHadoop からデータの抽出が行える言語です。HiveQL を利用してデータセットを抽出します。

# HTTP レスポンスコード別のログ件数を取得する
> dataset.code <- dbGetQuery(conn, "SELECT code, count(1) FROM access where vhost='seekr.jp' group by code order by code desc")
> dataset.code
  code  cnt
1  200 9701
2  304 1264
3  404  245
4  206   93

# 日付別のログ件数を取得する
> dataset.date<-dbGetQuery(conn, "SELECT substr(from_unixtime(time),1,10) as dt, count(1) cnt FROM access where vhost='seekr.jp' group by substr(from_unixtime(time),1,10) order by dt")
> head(dataset.date)
           dt cnt
1  2013-11-12 126
2  2013-11-13 561
3  2013-11-14 435
4  2013-11-15 636
5  2013-11-16 248
6  2013-11-17 425

# 時間別のログ件数を取得する
> dataset.hr<-dbGetQuery(conn, "SELECT substr(from_unixtime(time),12,2) hr, count(1) cnt FROM access where vhost='seekr.jp' group by substr(from_unixtime(time),12,2) order by hr")
> head(dataset.hr)
   hr cnt
1  00 719
2  01 627
3  02 649
4  03 665
5  04 693
6  05 849

4. googleVis パッケージを利用してグラフ描画する

今回、レポーティング用途で扱うケースを想定し、グラフ描画には標準の plot 関数ではなく、GoogleVis パッケージを使用してみました。googleVis パッケージでは、Google Charts を用いて、高度なグラフを出力するものです。

# パッケージのロード
install.packages("googleVis")
library(googlevis)

# 折れ線グラフで表示 
chart.line <- gvisLineChart(dataset.date, options=list(height=400))

# 棒グラフで表示 
chart.bar <- gvisBarChart(dataset.hr, options=list(height=400))

# 円グラフで表示
chart.pie <- gvisPieChart(dataset.code, options=list(height=280))

# 折れ線グラフと棒グラフを結合
charts.top <- gvisMerge(chart.line,chart.bar,horizontal=T)

# 上記と円グラフを結合
charts <- gvisMerge(charts.top,chart.pie,horizontal=F)

# 描画
plot(charts)

plot 関数を実行後、Web ブラウザが起動し、出力結果を参照できます。チャート内では、オンマウスでラベルを表示するなどといった使い方が可能です。

f:id:hiratake55:20131205082435p:plain

※ 右上の図で深夜帯がピークになっているのは、Amazon EC2 (Amazon Linux) の Timezone を日本時間に設定するのを忘れているためです^^;

まとめ

本記事では、Treasure Data でのテーブル作成、Web サーバへの fluentd のインストール、R から Treasure Data にアクセスする方法、googleVis パッケージを利用してグラフを出力する手順を解説しました。

この仕組みを応用して Web サーバのアクセスログをもとに、以下のような分析を実施し Web サービスのパフォーマンス最適化やキャパシティプランニング等に利活用できると考えられます。

  • 異常アクセスの早期検知
  • 時系列解析によるアクセス頻度の高い期間の周期性発見
  • アクセス急増後のアクセス減少状況モデリングと予測
  • リンク切れコンテンツの発見と可視化
  • パフォーマンス改善のための診断
  • 処理時間の長いコンテンツの抽出

※ この Post は R Advent Calendar 2013 の 12/5 分の記事です。

参考サイト