S3のログをAthenaから操作する（Terraform）

# S3のログをAthenaから操作する（Terraform）

# ながれ

- ワークグループを作る
- データベースを作る
- テーブルを作る

※その他テーブル作成用のクエリやクエリ実行結果の保存先バケットなどは別途つくる

# 用語解説

## ◾️ ワークグループ

リソースの分離や上限の設定を行える仮想的なグループ。グループごとにIAMを設定し、誰がどのような権限で縛られたワークグループを操作できるかといった管理などが行える。

## ◾️ データベース、テーブル

このあたりはRDBMSと同じ考え方でokだと思います。

# リソース構築

まずはワークグループとデータベースを作成します。

athena.tf

```go
resource "aws_athena_workgroup" "logs" {
  name = "logs"
  configuration {
    // クライアント側のクエリの結果、暗号化設定を上書きするか
    enforce_workgroup_configuration = true
    // クエリ関連のメトリクスをCloud Watchで発行するか
    publish_cloudwatch_metrics_enabled = true

    result_configuration {
      // クエリ実行結果の保存先
      output_location = "s3://${aws_s3_bucket.athena.bucket}/output/"
      // クエリ結果の暗号化設定
      encryption_configuration {
        encryption_option = "SSE_S3"
      }
    }
  }
}

resource "aws_athena_database" "logs" {
    name = "logs"
    // クエリ実行結果の保存先
    bucket = aws_s3_bucket.athena.id
}
```

`enforce_workgroup_configuration`は？となったのですが、クライアントサイドからのAPI実行時などパラメータ指定でクエリ結果の保存先や暗号化の有無を指定した際、その指定を上書きするかどうか（すなわちAPI実行時に任意のバケットへの保存と暗号化操作を行えなくする）を司っている？みたいです。（tureなら上書きする）

また、クエリの実行結果を保存するバケットも必要なので、作成しておきます。

s3.tf

```go
resource "aws_s3_bucket" "athena" {
  bucket = "athena"
  acl    = "private"

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}

resource "aws_s3_bucket_public_access_block" "athena" {
  bucket = aws_s3_bucket.athena.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
```

次に実際にログを取り込む対象のバケットの指定とフォーマットを決めていきます。

[AWS のサービスの Athena との統合](https://docs.aws.amazon.com/ja_jp/athena/latest/ug/athena-aws-service-integrations.html#integ-ate-cf) という公式ページでS3へログの吐き出しを行なっている各関連サービスのログ table作成のテンプレがありますので、それを引っ張って来ましょう。

今回はALBのログを例にとって試します。（ALBのログ保存用バケットは作成済みとします。）

前述したようにその他ログのAthena連携を行いたいサービスは同様に上記リンクのテンプレからコピリましょう。（vpcフローログ、cloudfrontなどなど）

albログテーブル作成用テンプレ

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/application-load-balancer-logs.html

sql文

```sql
CREATE EXTERNAL TABLE IF NOT EXISTS alb_logs (
            type string,
            time string,
            elb string,
            client_ip string,
            client_port int,
            target_ip string,
            target_port int,
            request_processing_time double,
            target_processing_time double,
            response_processing_time double,
            elb_status_code string,
            target_status_code string,
            received_bytes bigint,
            sent_bytes bigint,
            request_verb string,
            request_url string,
            request_proto string,
            user_agent string,
            ssl_cipher string,
            ssl_protocol string,
            target_group_arn string,
            trace_id string,
            domain_name string,
            chosen_cert_arn string,
            matched_rule_priority string,
            request_creation_time string,
            actions_executed string,
            redirect_url string,
            lambda_error_reason string,
            target_port_list string,
            target_status_code_list string,
            classification string,
            classification_reason string
            )
            ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
            WITH SERDEPROPERTIES (
            'serialization.format' = '1',
            'input.regex' = 
        '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) ([^ ]*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\s]+?)\" \"([^\s]+)\" \"([^ ]*)\" \"([^ ]*)\"')
            LOCATION 's3://{bucket_name}/{prefix}/AWSLogs/{account_id}/elasticloadbalancing/{region}/'; // ここはそれぞれ違うと思うので、よしなに変えてください。実際にログを取り込む対象のバケットのパスの指定です。
```

構文を少し見ていきます。

- `EXTERNAL`

取り込むデータがS3の基盤に基づいている限りこれを指定するらしい。

- `IF NOT EXISTS`

すでにテーブルが存在しないか。

- `ROW FORMAT SERDE`

データをシリアライズ（デシリアライズ）するためのフォーマット指定。

今回はjsonデータのシリアライズ（デシリアライズ）を行うため、`org.apache.hadoop.hive.serde2.RegexSerDe`のライブラリを指定している。

- `WITH SERDEPROPERTIES`

`ROW FORMAT SERDE`で指定したフォーマットのカスタムプロパティを指定できる。

https://docs.aws.amazon.com/ja_jp/athena/latest/ug/json-serde.html

- `LOCATION`

実際にデータが保存されているS3の指定。

**ここでterraform applyをしましょう。ズドーンとリソースが出来上がったら次へ進みます。**

次にコンソールからAthenaのサービス画面へ行き、実際にDDLを実行してテーブルを作成していきます。

右上のプルダウンから作成したワークグループを選択します。

![スクリーンショット 2022-02-08 22.46.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525590/8a2afe8f-b4e7-e271-f9c5-616ef79d56a1.png)

次に左真ん中あたりのプルダウンから作成したデータベースを選択します。

![スクリーンショット 2022-02-08 22.47.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525590/a2362bfe-ab62-2fa8-167a-8c277232ecb4.png)

ワークグループとデータベースの切り替えが完了したら、先ほど作ったクエリをエディタに貼り付けて実行

![スクリーンショット 2022-02-08 22.48.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525590/d4706882-5ba7-516f-a79e-272dfa001205.png)

テーブルができるので、あとはクエリを操作すればログを取得できます。

![スクリーンショット 2022-02-08 23.12.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/525590/fd6d58ed-da3c-3fa1-1098-4e0841eacc1c.png)

# 補足

- 本記事では触れていませんが、テーブル作成時、パーティションは考慮した方が良いと思われます。（パフォーマンス向上とコスト削減）
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/partitions.html
- `aws_athena_workgroup` と `aws_athena_database ` のリソースタイプでそれぞれクエリの実行結果保存先を指定しているのは何故なんだろうか。。。