S3のファイルの中身をSQLで検索できるAWS Athenaを(遅まきながら)触ってみた

AWS Athenaは表題の通り、S3にあるファイルをSQLで検索できるAWSのサービスで、
内部には**Presto**が使用されているそうです。

# Athenaの利用

Athenaは東京リージョン(ap-northeeast-1)に対応していないので、東京リージョンからAthenaを選択すると、

![1.png](https://qiita-image-store.s3.amazonaws.com/0/143854/c7091060-b42f-272d-601a-f83e5e97f50b.png)
![undefined]()


と出ますが、対応しているリージョンを選択すればいいので使用することに問題はありません。
**Atenaを利用するリージョンと異なるリージョンにあるS3バケットも、検索の対象にすることができます。**

**注意：**
**AthenaのリージョンとS3のリージョンが異なる場合、リージョン間通信料金がかかります。**

詳しくは、[S3の料金について](https://aws.amazon.com/jp/s3/pricing/)を参照ください。

## テーブルの作成

今回は、米国東部リージョンを利用しました。
Athenaが利用可能だと以下のような画面が表示されます。

![2.png](https://qiita-image-store.s3.amazonaws.com/0/143854/52aa2342-0951-4162-58c9-45e366ad0bdf.png)


Query実行欄があるので直接Queryを書いてテーブルを作ることもできますが、
Hiveの記法で書く必要があり、Hiveを知らないので、
**CatalogManager**に従ってテーブルを作成します。

**CatalogManager**を押下して遷移する以下の画面で、**Add table**を押下します。



![3.png](https://qiita-image-store.s3.amazonaws.com/0/143854/57f10f60-9f13-4371-e6fc-7d563fe174d4.png)



## Name & Location

この画面では、作成するテーブルを配置するデータベースやテーブル名、
およびテーブルの元になるS3バケットのパスを指定します。

今回、テーブルを作成した時のバケット構成は以下の通りです。
(CloudWatch Logs ＋ AWS Lambda で日付ごとインスタンス毎のログが.gzファイルに入っています。)

s3://xxxxxxxxxx-var-log-mssages/
　┣━2016-12-29
　┃　┣━aws-logs-write-test
　┃　┗━5ce6a99c-bc8a-4af7-b818-061ed575183f
　┃　　　┣━i-03e629e997aa95f18
　┃　　　┃　┗━000000.gz
　┃　　　┗━i-074549fdee10c46bc
　┃　　　 　┗━000000.gz
　┗━2016-12-30
　　　┣━aws-logs-write-test
　　　┗━6c868fe1-d6ef-4d03-a6e0-4b2c89d1b44b
　　　　　┣━i-03e629e997aa95f18
　　　　　┃　┗━000000.gz
　　　　　┗━i-074549fdee10c46bc
　　　　　　　┗━000000.gz


S3バケットで指定したパス以下にあるファイルがQuery対象のファイルになるので、
s3://xxxxxxxxxx-var-log-mssages/ 以下にあるファイルがQuery対象です。

![4.png](https://qiita-image-store.s3.amazonaws.com/0/143854/c88a3755-f50a-a6ee-4cb9-91c5696ccaa7.png)


## Data Format

ここでは、テキストをテーブルの列として扱うために、
どのようなフォーマットで列を区切るか指定します。
CSVやTSV、正規表現等が使えます。

今回Athenaで検索対象とするテキストは以下のように記載されているため、
```
2016-12-30T13:46:43.279Z Dec 30 22:46:42 ip-172-16-2-115 dhclient[2069]: bound to 172.16.2.115 -- renewal in 1663 seconds.
2016-12-30T13:46:43.279Z Dec 30 22:46:42 ip-172-16-2-115 ec2net: [get_meta] Trying to get http://169.254.169.254/latest/meta-data/network/interfaces/macs/06:65:2d:4e:6d:cf/local-ipv4s
2016-12-30T13:46:43.280Z Dec 30 22:46:42 ip-172-16-2-115 ec2net: [rewrite_aliases] Rewriting aliases of eth0
```
先頭の時刻部分(CloudWatch Logsの時刻)とDec 30以降の部分(ログファイルの本体)との2列になるよう、
以下の正規表現で列の区切りを設定しました。
```
([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z) (.*)
```

![5.png](https://qiita-image-store.s3.amazonaws.com/0/143854/31cc621a-5572-8d38-d65e-d668fe23f5a1.png)


## Columns

**Data Format**で設定した列の区切りに対して、列名と型を設定します。

今回の列の区切りでは、1列目と2列目を文字列とするため、
以下のように設定しました。

![6.png](https://qiita-image-store.s3.amazonaws.com/0/143854/eb45216f-9382-7fd9-41f2-732020025a44.png)


## Partitions

テーブルのパーティショニングを行うかどうかの設定ですが、
今回はお試しなのでパーティショニングは設定しません。
（Athenaはスキャン量に応じて課金されるので、
　実際に運用する場合はスキャン量を減らすためにパーティショニングをしたほうがよいそうです。）

![7.png](https://qiita-image-store.s3.amazonaws.com/0/143854/6beb9e33-5c06-860f-1076-20a78be5d0b2.png)


## テーブル作成

**Partitions**で**Create table**を押下すると、
Query画面に戻され、先ほどまで設定した内容に基づいてSQLを作成・発行します。

![8.png](https://qiita-image-store.s3.amazonaws.com/0/143854/55205642-cab2-4580-74bb-98efc01cd403.png)


# 検索

テーブルができましたので、
log_bodyにerrorの文字列が存在するものを検索します。

```
SELECT * FROM messages_log where log_body like '%error%' order by log_time desc;
```

![9.png](https://qiita-image-store.s3.amazonaws.com/0/143854/fdeacf70-d17f-264e-3138-dae7c7488120.png)


従来のSQLの書き方で、ログファイルにerrorの文字が存在するものを抜き出すことができました。


# 最後に
使った感想ですが、やはりログファイルをSQLで検索できるのは、
エラー頻度やエラー解析を行うには便利なように感じました。
ただ、クエリでスキャンしたデータ量に対して課金されるので、
エラー解析のためにガンガンSQLを実行するのは難しいかと思います。