19
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Amazon Athenaに入門してみた(Terraformで構築)

Last updated at Posted at 2021-01-11

概要

Amazon Athena を利用する機会があったので、入門した際に学んだことを記載したいと思います。
また、AthenaはTerraformで構築しました。サンプルコードもあります。

試したことは下記の通りです。

  • TerraformでAthenaを構築
    • JSONのデータにSQLを実行できる設定
    • Partition Projectionの設定
  • クエリの実行
    • Nested JSONにクエリを実行する
    • パーティションの指定

想定読者

  • Amazon Athenaに入門したい人
  • Amazon AthenaをTerraformで構築したい人

ソースコード

Terraformで構築したサンプルコードです。
https://github.com/kobayashi-m42/terraform-aws-athena-sample

Amazon Athenaとは

AthenaはS3にあるデータ(およびさまざまなデータソース)に対して標準SQLを利用して簡単に分析可能とするインタラクティブなクエリサービスです。

AthenaはS3のデータに直接SQLを実行できるサーバレスな分析サービスであるため、インフラ管理が不要であり簡単に導入することができます。

簡単にログの分析などユースケースで利用できるかと思います。

Athenaの構築

S3バケットに保存されたJSON形式のログに対して、Athenaを利用してSQLを実行できる環境を構築します。
ログのサンプルは下記の通りです。

sample-log
{"level":"info","msg":"message0","name":{"first":"first0","last":"last0"},"time":"2021-01-10T18:08:40+09:00"}
{"level":"info","msg":"message1","name":{"first":"first1","last":"last1"},"time":"2021-01-10T18:08:41+09:00"}
{"level":"info","msg":"message2","name":{"first":"first2","last":"last2"},"time":"2021-01-10T18:08:42+09:00"}
{"level":"info","msg":"message3","name":{"first":"first3","last":"last3"},"time":"2021-01-10T18:08:43+09:00"}
{"level":"info","msg":"message4","name":{"first":"first4","last":"last4"},"time":"2021-01-10T18:08:44+09:00"}
{"level":"info","msg":"message5","name":{"first":"first5","last":"last5"},"time":"2021-01-10T18:08:45+09:00"}
{"level":"info","msg":"message6","name":{"first":"first6","last":"last6"},"time":"2021-01-10T18:08:46+09:00"}
{"level":"info","msg":"message7","name":{"first":"first7","last":"last7"},"time":"2021-01-10T18:08:47+09:00"}
{"level":"info","msg":"message8","name":{"first":"first8","last":"last8"},"time":"2021-01-10T18:08:48+09:00"}
{"level":"info","msg":"message9","name":{"first":"first9","last":"last9"},"time":"2021-01-10T18:08:49+09:00"}

今回は、Partition Projectionも試したいので、下記の構成でS3バケットに保存します。
それぞれ同じファイルを置いています。日付を指定してSQLを実行できるようにすることを想定しています。

s3://<bucket_name>/logs/2020/01/10/sample-log
s3://<bucket_name>/logs/2020/01/11/sample-log

Partition Projection については公式のドキュメントや下記の記事がわかりやすかったので参考にしてください。
[新機能]Amazon Athena ルールベースでパーティションプルーニングを自動化する Partition Projection の徹底解説 | Developers.IO

S3 Bucket

事前にS3バケットを2つ作成します。

  • ログを保存するためのS3バケット
  • Athenaの実行結果を保存するS3バケット

Athena Workgroup

resource "aws_athena_workgroup" "this" {
  name = var.athena_workgroup_name

  configuration {
    enforce_workgroup_configuration    = true
    publish_cloudwatch_metrics_enabled = false
    result_configuration {
      output_location = "s3://${var.athena_result_bucket_name}/athena-result/"
    }
  }
}

variable "athena_result_bucket_name" {
  description = "Athenaの結果を出力するためのS3バケット名"
}

AWSコンソールから確認すると、Workgroupが作成されています。
スクリーンショット 2021-01-11 1.06.37.png

Workgroupを利用するためのIAMポリシーを設定することで、特定のWorkgroupの実行権限のみIAMユーザに付与することも可能です。詳細は、下記のドキュメントを参照してください。
Workgroup Example Policies - Amazon Athena

Athena Database と Athena Named Query

Athennaのデータベースとテーブルを構築します。

Athena Database

resource "aws_athena_database" "this" {
  name   = var.athena_database_name
  bucket = var.log_bucket_name
}

variable "log_bucket_name" {
  description = "ログが保存されているS3バケット名"
}
variable "athena_database_name" {
  description = "任意のデータベース名"
}

Athena Named Query

// SQLは別のファイルで定義
data "template_file" "create_table_sql" {
  template = file("${path.module}/sql/create-table.sql.tpl")
  vars = {
    athena_database_name = var.athena_database_name
    athena_table_name    = var.athena_table_name
    log_bucket_name      = var.log_bucket_name
  }
}

resource "aws_athena_named_query" "create_table" {
  name        = "Create table"
  description = "テーブルを作成"
  workgroup   = var.athena_workgroup_name
  database    = var.athena_database_name
  query       = data.template_file.create_table_sql.rendered
}

variable "athena_workgroup_name" {
  description = "Athena Workgroup名"
}
variable "athena_database_name" {
  description = "任意のデータベース名"
}
variable "athena_table_name" {
  description = "任意のテーブル名"
}
create-table.sql.tpl
CREATE EXTERNAL TABLE IF NOT EXISTS ${athena_database_name}.${athena_table_name} (
    `level` string,
    `msg` string,
    `name` struct<`first`:string, `last`:string>,
    `time` string
)
PARTITIONED BY (`orderdate` string)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
    'serialization.format' = '1'
)
LOCATION 's3://${log_bucket_name}/logs/'
TBLPROPERTIES (
    'projection.enabled' = 'true',
    'projection.orderdate.type' = 'date',
    'projection.orderdate.range' = '2020/01/10,NOW',
    'projection.orderdate.format' = 'yyyy/MM/dd',
    'projection.orderdate.interval' = '1',
    'projection.orderdate.interval.unit' = 'DAYS',
    'storage.location.template' = 's3://${log_bucket_name}/logs/$${orderdate}'
);
  • Nested JSON形式のログを想定しているため、 name struct<first:string, last:string>のように定義。
  • TBLPROPERTIESでPartition Projectionの設定を追加
    • S3にはs3://<bucket_name>/logs/yyyy/MM/ddの形式でログが保存される
    • 年月日(yyyy/MM/dd)でパーティションを設定する

AWSコンソールから確認すると、クエリが作成されています。

スクリーンショット 2021-01-10 21.37.02.png

クエリを選択し実行すると、テーブルが作成されます。
スクリーンショット 2021-01-10 23.17.33.png

テーブルプレビューを実行すると、クエリの結果が表示されます。

クエリエディタ
SELECT * FROM "terraform_aws_athena_sample_db"."terraform_aws_athena_sample_table" limit 10;

実行結果
スクリーンショット 2021-01-10 23.22.13.png

ここで、AWSコンソールからGlueを見てみると、Athenaで構築されたDatabaseとテーブルを確認することができます。
データベース
スクリーンショット 2021-01-10 23.23.28.png

テーブル
スクリーンショット 2021-01-10 23.24.04.png

当初、AthenaとGlueの関係性がわからなかったのですが、Black Beltの資料にこのように記載がありました。

Athenaではクエリのために、テーブル定義が必要。デフォルトでは、AWS Glue Data Catalog上のテーブル定義を使用。
AWS Glue Data Catalogは、Apache Hive MetastoreというOSSと互換性のある、メタデータを管理するためのリポジトリ。

AWS Glue Data Catalogにテーブル定義を作成する方法は、次の3つ
・ Athena DDL
・ AWS Glue Catalog API
・ AWS Glue Crawler

参考:20200617 AWS Black Belt Online Seminar Amazon Athena

上記は「Athena DDL」を利用して、テーブル定義を利用した例でした。
ここからは、「AWS Glue Catalog API」の方法(TerraformのGlueのリソースを利用して)でデータベースとテーブルを作成してみます。

Glue Catalog Database と Glue Catalog Table

Glue Catalog Database


resource "aws_glue_catalog_database" "this" {
  name = var.glue_catalog_database_name
}
variable "glue_catalog_database_name" {
  description = "任意のデータベース名"
}

Glue Catalog Table

resource "aws_glue_catalog_database" "this" {
  name = var.glue_catalog_database_name
}

resource "aws_glue_catalog_table" "this" {

  database_name = aws_glue_catalog_database.this.name
  name          = var.glue_catalog_table_name

  table_type = "EXTERNAL_TABLE"

  parameters = {
    "projection.enabled"                 = "true"
    "projection.orderdate.format"        = "yyyy/MM/dd"
    "projection.orderdate.type"          = "date"
    "projection.orderdate.interval"      = "1"
    "projection.orderdate.interval.unit" = "DAYS"
    "projection.orderdate.range"         = "${var.date_range_start},NOW"
    "storage.location.template"          = "s3://${var.log_bucket_name}/logs/$${orderdate}"
  }

  storage_descriptor {
    location      = "s3://${var.log_bucket_name}/logs"
    input_format  = "org.apache.hadoop.mapred.TextInputFormat"
    output_format = "org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat"
    ser_de_info {
      serialization_library = "org.openx.data.jsonserde.JsonSerDe"
      parameters = {
        "serialization.format" = "1"
      }
    }

    columns {
      name = "level"
      type = "string"
    }
    columns {
      name = "msg"
      type = "string"
    }
    columns {
      name = "name"
      type = "struct<first:string,last:string>"
    }
    columns {
      name = "time"
      type = "string"
    }
  }
  partition_keys {
    name = "orderdate"
    type = "string"
  }
}

variable "glue_catalog_database_name" {
  description = "任意のデータベース名"
}

variable "glue_catalog_table_name" {
  description = "任意のテーブル名"
}

variable "date_range_start" {}

Terraformを実行すると、Athenaのデータベースとテーブルが作成されていることを確認できました。
AWSコンソールでテーブルのプレビューを実行してみると、Athenaリソースで定義したもの同様の結果が表示されています。

クエリエディタ
SELECT * FROM "terraform_aws_athena_sample_glue_db"."terraform_aws_athena_sample_glue_teble" limit 10;

実行結果
スクリーンショット 2021-01-10 23.41.09.png

SQLの実行方法

Nested-JSON の例

ログの一部がNested-JSONになっています。要素名nameの下に、firstlastが入れ子になっています。

{"level":"info","msg":"message0","name":{"first":"first0","last":"last0"},"time":"2021-01-10T18:08:40+09:00"}

name."last"のようにカラムを指定することができます。またfirstlastのような予約語はダブルクォートで囲む必要があります。

クエリエディタ
SELECT name."first",  name."last" FROM "terraform_aws_athena_sample_db"."terraform_aws_athena_sample_table" limit 1;
実行結果(CSV)
"first","last"
"first0","last0"

絞り込むことも可能です。

クエリエディタ
SELECT *
FROM "terraform_aws_athena_sample_db"."terraform_aws_athena_sample_table"
WHERE name."first" = 'first0' limit 1;
実行結果(CSV)
"level","msg","name","time","orderdate"
"info","message0","{first=first0, last=last0}","2021-01-10T18:08:40+09:00","2020/01/11"

パーティションの指定

パーティションを設定しているので、年月日で対象を絞り込むことが可能です。

クエリエディタ
SELECT *
FROM "terraform_aws_athena_sample_db"."terraform_aws_athena_sample_table"
WHERE msg = 'message0' and orderdate = '2020/01/11';

orderdateを指定しなかった場合、結果は2件になります。

実行結果(CSV)
"info","message0","{first=first0, last=last0}","2021-01-10T18:08:40+09:00","2020/01/11"

参考

公式ドキュメント: What is Amazon Athena? - Amazon Athena
[新機能]Amazon Athena ルールベースでパーティションプルーニングを自動化する Partition Projection の徹底解説 | Developers.IO
Amazon Athena Nested-JSONのSESログファイルを検索する | Developers.IO

19
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?