Amazon Athena で Amazon CloudFront のアクセスログを分析する


# 概要

AWS re:Invent 2016 で発表された新サービス Amazon Athena を使用して Amazon CloudFront のアクセスログを分析してみました。


# Amazon Athena とは

Amazon Athena は AWS re:Invent 2016 で発表された新しいサービスで、AWSのサービスの中では Analytics カテゴリに分類されています。

Athena を使用すると、Amazon S3 に保存してあるデータを、どこかに取り込んだり加工したりといった作業を必要とせずに、そのまま ANSI SQL のクエリで対話的に集計や分析をすることができます。

ざっくり言うと Amazon EMR がクエリを実行した分だけの料金で使えるようになった、サーバレスなデータ分析サービスという感じです。

Athena の利用料はクエリを1回実行するごとに課金され、金額はそのクエリでスキャンされたデータ1TBごとに5$となっています。

課金されるデータのサイズは最低10MBとなっていて、これを超える分については1MB単位で切り上げたバイト数で計算されます。

途中でキャンセルしたクエリについては、キャンセルするまでにスキャンしたデータ量に対して課金され、エラーになったクエリには課金されません。

Athena の料金の詳細については [Amazon Athena Pricing](https://aws.amazon.com/jp/athena/pricing/) のページを参照して下さい。

クエリはマネジメントコンソールから対話的に実行できるほか、JDBCドライバも提供されているので、Javaアプリケーションからも実行することができます。


# CloudFront のアクセスログを分析する

Athena を使って CloudFront のアクセスログを分析してみます。


## 前提事項

以下の内容で CloudFront のアクセスログを S3 バケットに保存するように設定されているものとします。

| 設定項目 | 設定内容 |
|:-----------|:-----------|
| Logging | ON |
| Bucket for Logs | YOUR_BUCKET_NAME |
| Log Prefix | CLOUDFRONT_LOG_PREFIX/ |


バケット名やプレフィックスは実際の設定に読み替えて下さい。


## データベースの作成

マネジメントコンソールから Athena の [Query Editor](https://console.aws.amazon.com/athena/home) を開いて、以下のクエリを実行してデータベースを作成します。

```
CREATE DATABASE logs;
```

![CREATE DATABASE](https://qiita-image-store.s3.amazonaws.com/0/18609/b5a60010-e604-7c26-3523-4bd2d7eca29b.png "CREATE DATABASE")

既存のデータベース default をそのまま使用する場合は、このデータベースの作成手順は不要です。


## テーブルの作成

使用するデータベースを選択し、以下のクエリを実行してテーブルを作成します。

LOCATION にはアクセスログを保存している S3 のバケット名とプレフィックスを指定します。

```
CREATE EXTERNAL TABLE IF NOT EXISTS cloudfront_logs (
    date DATE,
    time STRING,
    x_edge_location STRING,
    sc_bytes INT,
    c_ip STRING,
    cs_method STRING,
    cs_host STRING,
    cs_uri_stem STRING,
    sc_status INT,
    cs_referer STRING,
    cs_user_agent STRING,
    cs_uri_query STRING,
    cs_cookie STRING,
    x_edge_result_type STRING,
    x_edge_request_id STRING,
    x_host_header STRING,
    cs_protocol STRING,
    cs_bytes INT,
    time_taken INT,
    x_forwarded_for STRING,
    ssl_protocol STRING,
    ssl_cipher STRING,
    x_edge_response_result_type STRING,
    cs_protocol_version STRING
)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES (
    'serialization.format' = '	', -- タブ文字（区切り文字）
    'field.delim' = '	' -- タブ文字（区切り文字）
)
LOCATION 's3://YOUR_BUCKET_NAME/CLOUDFRONT_LOG_PREFIX/'
;
```

![CREATE TABLE](https://qiita-image-store.s3.amazonaws.com/0/18609/e9bc6445-aa76-883c-572d-6a906ef49920.png "CREATE TABLE")

作成しているフィールドは、CloudFront のアクセスログに出力される項目に対応しています。

各項目の内容について CloudFront の [開発者ガイド](http://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html#LogFileFormat) に記載されています。

CloudFront のアクセスログは、項目がタブで区切られたTSV形式のテキストファイルなので、SERDE には org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe を、SERDEPROPERTIES で serialization.format と field.delim にタブ文字（\t）を指定しています。

Athena がサポートするデータの形式と SerDes についてはユーザーガイドの [Supported Formats and SerDes](http://docs.aws.amazon.com/athena/latest/ug/supported-formats.html) を参照して下さい。


## クエリの例

アクセスログを分析する準備が整ったのでクエリを実行してみます。


### 指定した1時間のキャッシュヒットの状況

```
SELECT x_edge_response_result_type, count(1) count
    FROM cloudfront_logs
    WHERE date = date '2016-12-01'
        AND time LIKE '03:%'
    GROUP BY x_edge_response_result_type;
```

![SELECT](https://qiita-image-store.s3.amazonaws.com/0/18609/8eaac709-e09c-0683-5fb6-211669b80f55.png "SELECT")

このように検索条件として時刻を指定する場合、CloudFront のログの時刻は UTC で出力されていることに注意が必要です。

他にも以下のようなクエリを実行してみました。


### 指定した1日のHTTPステータスコード別のリクエスト件数

```
SELECT sc_status, count(1) count
    FROM cloudfront_logs
    WHERE date = date '2016-12-03'
    GROUP BY sc_status
    ORDER BY sc_status;
```


### 指定した1ヶ月間の日別のエラー応答件数

```
SELECT date, count(1) count
    FROM cloudfront_logs
    WHERE date BETWEEN date '2016-11-01' AND date '2016-11-30'
        AND x_edge_response_result_type = 'Error'
    GROUP BY date
    ORDER BY date ASC;
```


### 指定した1分間にエラーで応答したリクエストの内容

```
SELECT *
    FROM cloudfront_logs
    WHERE date = date '2016-12-01'
        AND time LIKE '12:34:%'
        AND x_edge_response_result_type = 'Error'
    ORDER BY time ASC;
```

### 指定した1分間に応答に20秒以上かかったリクエストの内容

```
SELECT *
    FROM cloudfront_logs
    WHERE date = date '2016-12-01'
        AND time LIKE '12:34:%'
        AND time_taken >= 20
    ORDER BY time ASC;
```

このように、SQLを使用して CloudFront のアクセスログを集計したり分析することができます。


# 課題

Amazon Athena の利用料はクエリによってスキャンされたデータの容量で決まるため、実行するSQLの内容によって大きく変わります。

クエリで実際にスキャンされたデータのサイズは "Data scanned" という欄に表示されます。

例えば Athena のサンプルデータとして用意されている ELB のログファイルを以下のようなクエリで検索してみます。

```
SELECT * FROM elb_logs limit 10;
```

![Query Sample 1](https://qiita-image-store.s3.amazonaws.com/0/18609/7f31872d-9292-1642-6848-3c17defe0018.png "Query Sample 1")


次に、同じクエリをタイムスタンプでソートして実行してみます。

```
SELECT * FROM elb_logs ORDER BY timestamp DESC limit 10;
```
![Query Sample 2](https://qiita-image-store.s3.amazonaws.com/0/18609/096e171b-f96d-19a6-0ad3-b1d95bb66187.png "Query Sample 2")


このように条件によってスキャンされるデータのサイズが変わることが分かります。

実際にログファイルを分析する場合は、調べたい日付を指定したり、時系列でソートして処理する事が多くなりますが、このような条件を指定してクエリを実行するとスキャンするデータのサイズが大きくなります。

アクセスが多いサイトや、長期間ログを溜め込んだバケットを分析する場合は気をつけましょう。


Athena ではデータのパーティショニングを行うことで、クエリによってスキャンされるデータの範囲をコントロールできます。

データのパーティショニングについては、ユーザーガイドの [Partitioning Data](http://docs.aws.amazon.com/athena/latest/ug/partitions.html) のページに記載されています。

ログファイルの場合は、年、月、日あたりでパーティショニングしておけば良さそうですが、CloudFront で指定できるプレフィックスや標準のオブジェクト名のままではパーティショニングができません。

対策としては、

- S3 に保存された時に Lambda Function を使用してプレフィックスをパーティショニングできるような名前に変更する
- 分析したい日付のファイルだけを別のバケットにコピーしてテーブルを作成する

といった方法が良いのではないかと思います。

カラム形式に変換しておくという方法もありますが、そこまでするなら EMR を使えば良いという気もするので省略。詳細はユーザーガイドの [Converting to Columnar Formats](http://docs.aws.amazon.com/athena/latest/ug/convert-to-columnar.html) に記載されています。

今後のバージョンアップで CloudFront の設定だけでパーティショニングに対応したオブジェクト名でアクセスログを保存できるようになると良いですね。


# まとめ

Athena の登場で S3 に保存していたログをとても簡単に処理できるようになりました。

コスト面でも分析を行わなければ一切利用料がかからないというのも大きな魅力です。

とりあえず S3 に送っておいたログファイルの価値が一気に高まった感じがします。まるで何気なく拾って取っておいた石ころが、突然宝石の原石になったような気分です。

Athena は必要になったときにすぐに使い始めることができるので、ログファイルを S3 に集めておけばいざという時にきっと役にたつと思います。



# 参考

- [Amazon Athena - Amazon S3上のデータに対話的にSQLクエリを | Amazon Web Services ブログ](https://aws.amazon.com/jp/blogs/news/amazon-athena-interactive-sql-queries-for-data-in-amazon-s3/)

- [Amazon Athena - Serverless Interactive Query Service](https://aws.amazon.com/jp/athena/)

- [Amazon Athena - User Guide](http://docs.aws.amazon.com/athena/latest/ug/index.html)