CloudFrontのアクセスログをKibanaで可視化する

2016.04.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

藤本です。

本ブログでELBのアクセスログをKibanaで可視化するエントリをいくつか書いています。

今回はCloudFrontのアクセスログをKibanaで可視化する方法をご紹介します。

概要

CloudFrontは設定によりアクセスログをS3へ出力することが可能です。この仕様はELB同様ですが、ELBと違ってgzip圧縮がされています。そのため、gzip圧縮を解凍してログメッセージを取り出す必要があります。

今回はLogstashのS3 Input Pluginを利用することで簡単に実現する方法をご紹介します。

加えて、CloudFrontのアクセスログを解析することでどのような嬉しいことがあるのかユースケース混じえてご紹介します。

環境

Untitled(1)

構成はこのようになります。 今回実装するのは黄色背景の箇所となります。

  • Logstashサーバ
  • OS : Amazon Linux 2016.03
  • Logstash : v2.3.1
  • Amazon Elasticsearch Service
  • Elasticsearch : v1.5.2

Elasticsearchドメイン作成

Amazon Elasticsearch Serviceのドメイン作成は[新機能]Amazon Elasticsearch Serviceがリリースされました!を参照。

Logstashインストール、AmazonES Output Pluginインストール

Logstashのインストール、およびAmazonESのOutput PluginインストールはlogstashでELBのログを2週間分だけAmazon Elasticsearch Serviceに取り込むを参照。

Logstash設定

今回は入力処理にS3 Input Pluginを利用します。

S3 Input Plugin

指定したS3バケット、キー配下のオブジェクトを定期的にポーリングし、新規作成されたオブジェクトを取得することができます。更にはオブジェクトがアーカイブされていれば、取得後に展開します。そのため、圧縮保存されるCloudFrontのアクセスログであっても圧縮されていることを意識する必要はありません。

いくつかパラメータを紹介します。

KEY Description
bucket 取得対象のバケット名
prefix 取得対象のディレクトリ
region リージョン名(Default: us-east-1)
access_key_id IAMのアクセスキー
secret_access_key IAMのシークレットキー
session_token IAMの一時トークン
interval 取得間隔(Default: 60)
sincedb_path 前回からの差分を記録するファイルパス(Default: $HOME)

設定内容

まずは単純にCloudFrontのログをAmazon ESに投入します。

# vi cloudfront.conf
---
input {
s3 {
bucket => ""
prefix => ""
region => "ap-northeast-1"
}
}

output {
amazon_es {
hosts => [".ap-northeast-1.es.amazonaws.com"]
region => "ap-northeast-1"
index => "cloudfront-logs-%{+YYYY.MM.dd}"
}
}

Logstash起動

設定ファイルを指定して、Logstashを起動します。

初回起動時は指定したS3バケット、Key Prefixの全てのファイルを対象とします。既に多くのログファイルが存在する場合、とても時間がかかります。 今回のログメッセージ件数120万件でも20〜30分近くかかりました。

# /opt/logstash/bin/logstash -f cloudfront.conf
Settings: Default pipeline workers: 2
Pipeline main started

動作確認

CloudFrontのログが出力されるのを待ちます。

Amazon ES設定画面からのIndex確認

Amazon ESの設定画面からインデックス情報を確認します。

Amazon_Elasticsearch_Service_Management_Console

Logstashの設定ファイルで指定したIndexが作成されていることが確認できます。

Kibanaによるデータ確認

Webブラウザを起動し、Kibanaに接続します。

Indexには上に表示された名前(日付はワイルドカード)を指定します。

Settings_-_Kibana_4

Discoverからデータを確認します。

Discover_-_Kibana_4

ログメッセージが登録されていることを確認できました。

わーい、やったー。。。とはこのデータではなりませんよね。 目的はログデータを集めることではありません。

ログのデータから分析することです。そして、分析したデータからどのような施策・対策を打てるのかを判断する材料が欲しいですよね。

Logstashの機能はこんなものではありません。

Logstash設定(Advanced)

設定ファイルの完全版は本エントリの下部に記載します。

ログメッセージのパース

grokフィルターを利用すると決まったフォーマットのログメッセージからブロック単位で取り出すことができます。CloudFrontのアクセスログフォーマットはこちらをご参照ください。

更には数値は文字列ではなく、数値としてElasticsearchに投入することができます。 CloudFrontのアクセスログフォーマットはgrokフィルターで以下のように記載します。

grok {
match => { "message" => "%{DATE_EU:date}\t%{TIME:time}\t%{WORD:x_edge_location}\t(?:%{NUMBER:sc_bytes}|-)\t%{IPORHOST:c_ip}\t%{WORD:cs_method}\t%{HOSTNAME:cs_host}\t%{NOTSPACE:cs_uri_stem}\t%{NUMBER:sc_status}\t%{GREEDYDATA:referrer}\t%{GREEDYDATA:User_Agent}\t%{GREEDYDATA:cs_uri_stem}\t%{GREEDYDATA:cookies}\t%{WORD:x_edge_result_type}\t%{NOTSPACE:x_edge_request_id}\t%{HOSTNAME:x_host_header}\t%{URIPROTO:cs_protocol}\t%{INT:cs_bytes}\t%{GREEDYDATA:time_taken}\t%{GREEDYDATA:x_forwarded_for}\t%{GREEDYDATA:ssl_protocol}\t%{GREEDYDATA:ssl_cipher}\t%{GREEDYDATA:x_edge_response_result_type}" }
}

messageフィールドがパターンに一致する場合、各ブロックの文字列をフィールドに置き換えます。

日時の指定

最初のLogstash設定の場合、@TimestampはLogstashがメッセージを取得した時間がセットされています。 でも時系列データとして分析する場合、Logstashの時間ではなく、CloudFrontへアクセスされた時間を利用したいです。この場合、dateフィルターを利用し、@Timestampフィールドを任意のフィールドに置き換えます。

またCloudFrontのアクセスログの場合、日付と時間の間にタブが入っているため、一つのフィールドとして扱うことが難しいです。この場合、mutateフィルターを利用し、Date、Time異なるフィールドから新しいフィールドを作成します。

mutate {
add_field => [ "listener_timestamp", "%{date} %{time}" ]
}

日付、スペース、時間で日時のフィールドを作成します。

date {
match => [ "listener_timestamp", "yy-MM-dd HH:mm:ss" ]
target => "@timestamp"
}

作成したフィールドを指定したフォーマットで日時型とし、@timestampフィールドへセットします。

GeoIPフィールドの追加

LogstashはGeoIPデータベースを保持しており、IPアドレスから国名や州、町、緯度/経度情報を取得することができます。この場合、geoipフィルターを利用します。引数sourceにIPアドレスが入るフィールド名を指定します。

geoip {
source => "c_ip"
}

c_ipフィールドのIPアドレスをGeoIPデータベースと照合し、geoipフィールド(Nested)を作成します。

User Agentの分析

CloudFrontのアクセスログはUser-Agentヘッダを出力します。Logstashのuseragentフィルターを利用することでアクセス元のOS、Webブラウザなどを分析することができます。引数sourceにUser-Agentヘッダのフィールド、targetに分析結果を出力するフィールド名を指定します。targetのフィールドはNested Fieldで結果が展開されます。

useragent {
source => "User_Agent"
target => "useragent"
}

User_Agentフィールド(User-Agentヘッダの値)を解析し、useragentフィールド(Nested)を作成します。

不要フィールドの削除

フィールド削除

利用しないフィールドをmutateフィルターにより削除することができます。s3 input filterを利用して、CloudFrontのログを取得すると、cloudfrontのバージョン、ログフォーマットのフィールド名、メッセージ全体を出力します。また、一時的に追加した日時用フィールドも@Timestampとフィールドと同じものなので必要ありません。不要なフィールドはディスク枯渇やパフォーマンス劣化に繋がるため、利用することがないデータが入るフィールドは削除した方がよいです。

mutate {
remove_field => ["listener_timestamp", "cloudfront_version", "message", "cloudfront_fields", "User_Agent"]
}

Index Template設定

Elasticsearchは適切な設定を行うことによりパフォーマンスを向上させることができます。 今回は簡単にIndexの設定を行い、パフォーマンスを向上させます。

またLogstashのElasticsearch Output Plugin(amazon_es Output Plugin含む)はTemplateファイルを指定することでLogstash起動時にElasticsearchに対して、Index Templateを作成することができます。

設定ファイルの完全版は本エントリの下部に記載します。

_source、_allフィールドの無効化

_sourceフィールド、_allフィールドは横断した検索には便利ですが、CloudFrontログの分析のようにフィールド一つ一つで意味を持つ場合、不要フィールドを削除する理由で保持しない方がよいです。

{
"template": "cloudfront-logs-*",
"mappings": {
"logs": {
"_source": {
"enabled": false
},
"_all": {
"enabled": false
}
}
}
}

Analyzedの無効化

ElasticsearchはデフォルトでStringフィールドをトークンに分解してしまいます。例えば、URLのパスであれば、「/」「.」区切りなどで単語を抽出され、意図しない集計が行われます。今回のCloudFrontのアクセスログであれば、Analyzeされて嬉しいフィールドはないと思うので、全てAnalyzeしない設定とします。

"dynamic_templates": [
{
"string_fields": {
"mapping": {
"index": "not_analyzed",
"type": "string"
},
"match_mapping_type": "string",
"match": "*"
}
}
]

ユースケースご紹介

過去7日間の某ブログサイトのCloudFrontのアクセスログをS3に配置し、Kibanaで分析してみましょう。

Indexの確認

フィールドが増えました。

Amazon_Elasticsearch_Service_Management_Console 2

  • CloudFrontのログメッセージを意味ある単位で分割したフィールドが追加されました。
  • GeoIPのNested Fieldが追加されました。
  • UserAgentのNested Fieldが追加されました。

ここからはアクセスログの分析のユースケースとともにKibanaによる可視化を幾つかご紹介します。

人気ページ分析

弊社のブログページのようにどの記事、どの著者の閲覧数が多いのか、どの技術がトレンドなのかページアクセス数によりある程度判断することができます。

今回の可視化は日次のURLのパス単位のアクセス数Top5を表示しています。ブログのトップページなどは上部テキストボックスにLucene Queryを入力することで条件を絞り、除外することができます。

例えば、4月26日の飛び抜けたエントリはおおはしりきたけの突撃!隣のDevOps パート1【Wantedly編】が多く閲覧されていることが分かります。羨ましい。

screenshot 2016-04-27 18.43.21(2)

一方で毎日Top5入りしているロングセラーなエントリはブログ神のよく分かる!Android アプリのリリース手順のまとめ | アドカレ2013 : SP #20だということが分かります。え、2013年のアドカレ!?ロングセラーすぎる。スゴイ。

screenshot 2016-04-27 18.45.36(2)

キャッシュヒット分析

CloudFrontの利用目的にコンテンツのキャッシュがあります。このキャッシュの有用性(どの程度のリクエストがキャッシュを返しているのか)を分析することができます。あるページのレスポンスが遅い時にURLのパスとキャッシュステータスから本来、キャッシュさせたいページがキャッシュされていないと言った判断にも利用できます。

弊社ブログサイトではHit、RefreshHitが約80%を占めており、非常に効率よくキャッシュを返しているのではないでしょうか。

スクリーンショット_2016_04_27_18_58

利用者分析

アクセス元の位置情報

GeoIPを可視化することでアクセス元IPアドレスからTile Mapに位置情報を表示することでどの地域からのアクセスが多いのか、それによってその多く訪れている地域の人向けに有効な施策を打つといったマーケティングに利用できたり、どの範囲のアクセス元がどのEdgeロケーションにアクセスしているのかといったことも解析することができます。

件数は隠れてしまっていますが、世界から愛されるDevelopers.IOだということが分かります。

Visualize_-_Kibana_4

日本国内に絞るとこのようになります。やはり関東からのアクセスが目立ちます。東京都の一箇所からクローリングされているのかぶっちぎっている箇所があり、アクセス件数の濃淡がわかりづらい。。愛されるDeverlopers.IOだということが分かります。

Visualize_-_Kibana_4 2

Tile MapはKibana 5が好きです。

利用者の端末情報

User Agent情報を可視化することで利用者のOSやWebブラウザなどの情報を分析することができます。それによってどのOS、WebブラウザのUIを優先して実装すべきかと判断することができます。

OSがWindowsでWEBブラウザにChromeを利用している方が多くDevelopers.IOにアクセスしていることが分かります。

screenshot 2016-04-27 19.22.08

不正アクセス分析

特定のアクセス元IPアドレス、特定のアクセス地域からDoS/DDoS攻撃を受けている場合、そのIPアドレスを特定し、セキュリティグループやAWS WAFといった機能を利用してブロックすることが可能です。

マウスオーバーした時間で1時間に特定のIPアドレスから2600件のアクセスがあったことが分かります。愛され(ry

screenshot_2016-04-27_19_29_34

ダッシュボード化

最後にこれらグラフ・マップをひとまとめにダッシュボード化すれば、こんな見栄えがよいページが作成されました。もちろんデータを取り込み続ければ、リアルタイムでダッシュボード、グラフに最新データが表示されます。

Dashboard_-_Kibana_4

まとめ

いかがでしたでしょうか? CloudFrontのログをただS3に置いているだけの方もいらしゃるのではないでしょうか。CloudFrontのログに限らず、AWSは数多くのログを出力することが可能です。ログからは今回ご紹介したユースケースは一例でシステムに応じた色々な活用方法があります。是非一度、今のビジネス、システムで困っていることがログから拾える情報で解決出来ないか、見直していただくことをオススメします。

OSSであるElasticsearch/Kibanaはパワフルな可視化/分析ツールです。AWS環境であれば、Amazon Elasticsearch Serviceを利用することでElasticsearch/Kibanaを運用したことがない方でもAWSにある程度の運用を委ねることができ、カジュアルに利用できるのではないでしょうか。

設定ファイル

Logstash設定ファイル

Index Templateファイル