Terraform＋Amazon Athenaで快適なログ集計の準備をする

# はじめに
Amazon AthenaはS3に入ったログをSQLで抽出したり集計したりできる超便利機能。
でも、Terraformで一発で作ろうとするとちょっとクセがあるので、整理をしてみた。

# Amazon Athenaに必要なリソース
Amazon Athenaに必要なリソースは以下の5つ。

- ワークグループ
- データベース
- テーブル
- データ
- SQL

だ。データベースに登録するデータはS3に格納しておく必要がある。

# ワークスペース＋データベースの作成
これはそんなに難しいことはない。Terraformで普通に定義していこう。

```HCL
################################################################################
# Athena                                                                       #
################################################################################
resource "aws_athena_workgroup" "example" {
  name = local.athena_workgroup_name

  configuration {
    enforce_workgroup_configuration    = true
    publish_cloudwatch_metrics_enabled = false
    result_configuration {
      output_location = "s3://${aws_s3_bucket.athena_output.id}/"
    }
  }

  force_destroy = true
}

resource "aws_athena_database" "example" {
  name   = local.athena_database_name
  bucket = aws_s3_bucket.athena_output.id

  force_destroy = true
}

resource "aws_s3_bucket" "athena_output" {
  bucket = local.s3_output_bucket_name
  acl    = "private"

  force_destroy = true
}
```

それぞれのリソースの属性を ```force_destroy = true```にしているのは、中身が入っていると ```terraform destroy``` でエラーが発生するため。壊す必要がない商用のリソース等であれば、付与する必要はない。

# テーブルの作成
ここは少し曲者で、Terraform ではテーブルの作成をするSQLを実行することはできない。```aws_athena_named_query``` というリソースはあるが、実行することはできないのと、初回しか実行しないテーブル作成のSQLを保存しておいてもなので、データベース作成時のみ発動する ```null_resource``` を活用しよう。

```HCL
resource "null_resource" "initialize_db" {
  provisioner "local-exec" {
    command = <<-EOF
      aws athena start-query-execution \
        --work-group "${aws_athena_workgroup.example.id}" \
        --query-execution-context Database="${aws_athena_database.example.id}" \
        --query-string "${replace(replace(replace(data.template_file.create_example_table_sql.rendered, "`", "\\`"), "\"", "\\\""), "$", "\\$")}"
    EOF
  }
}

data "template_file" "create_example_table_sql" {
  template = file("../sql/01_create_table.sql")

  vars = {
    athena_database_name = aws_athena_database.example.id
    athena_table_name    = local.athena_table_name
    input_bucket_name    = aws_s3_bucket.athena_input.id
  }
}

resource "aws_s3_bucket" "athena_input" {
  bucket = local.s3_input_bucket_name
  acl    = "private"

  force_destroy = true
}
```

```SQL:01_create_table.sql
CREATE EXTERNAL TABLE IF NOT EXISTS ${athena_database_name}.${athena_table_name} (
  `request` STRING,
  `remote_addr` STRING, 
  `time_local` STRING
)
PARTITIONED BY (
  `file_datehour` STRING 
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  "serialization.format" = "1"
)
LOCATION 's3://${input_bucket_name}/${athena_table_name}/'
TBLPROPERTIES (
  'has_encrypted_data'='false',
  'projection.enabled'='true',
  'projection.file_datehour.type'='date',
  'projection.file_datehour.format'='yyyy/MM/dd/HH',
  'projection.file_datehour.range'='2021/02/27/00,NOW',
  'projection.file_datehour.interval'='1',
  'projection.file_datehour.interval.unit'='HOURS',
  'storage.location.template'='s3://${input_bucket_name}/${athena_table_name}/$${file_datehour}'
);
```

なお、Athena は大量のログを扱う際、スキャンに対して料金がかかるため、テーブルの設計が重要になる。
集計をする単位でパーティショニングしておくことで、ログが有効活用できるようになる。
今回は、FireLens で出力されたログを集計することを前提にしたテーブル構成にしてみた。
※FireLens については、[こちらの過去記事](https://qiita.com/neruneruo/items/b3fb35ad5064c045a15b)を参照。

パーティショニングに関する記述が、SQL の ```TBLPROPERTIES``` に書かれた部分だ。```file_datehour``` というキーを定義し、```PARTITIONED BY``` で指定を行い、```TBLPROPERTIES``` で詳細を指定するイメージだ。```TBLPROPERTIES``` の射影の設定については、[公式のドキュメント](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/partition-projection.html)が参考になる。

さて、これでデータを投入すれば準備は完了だ。

# SQLの実行

Athena のマネージメントコンソールでワークスペースを切り替え……

![キャプチャ1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/3c021da7-9a88-ad72-8c9c-96f9d56c1f34.png)

Query editor でSQLを実行！

![キャプチャ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/6f74aa2f-3c38-31b8-1bba-d4d0c9973994.png)

やった！結果が得られた！
抽出結果は、```local.s3_output_bucket_name``` で指定したS3バケットにもCSV形式で保存されている。

何度も実行するようなSQLであれば、```aws_athena_named_query``` で保存しておこう。

```HCL
resource "aws_athena_named_query" "between_term" {
  name      = "between term"
  workgroup = aws_athena_workgroup.example.id
  database  = aws_athena_database.example.name
  query     = "select * from ${aws_athena_database.example.name}.${local.athena_table_name} where file_datehour between '2021/02/27/11' and '2021/02/28/10';"
}
```

こうしておくと、コンソールから簡単に実行ができる。CLIはちょっと面倒だけど……。

![キャプチャ3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111314/fbcdb581-4c95-e9c3-5792-bd4e45e3d6ce.png)

これで、快適なS3ログ解析生活が送れるようになる！