📦

Amazon Aurora から Google Cloud BigQuery へのデータ移行をTerraformを中心に実装してみた話

2025/01/28に公開

はじめに

弊社におけるデータベースはAmazon Auroraを使用しており、 シンプルなデータ閲覧はEC2で構築したRedashを用いて、Auroraインスタンスに対してクエリを実行しています。
最近、プロダクトチームから「Looker Studioを用いてリッチなレポート閲覧を行いたい」という要望があり、GoogleCloudにおけるBigQueryへのデータ連携を実施することになりました。
今回は実装にあたっての構成と環境構築の手順をご紹介します。

実装にあたっての要件

  • リアルタイム性は求められず、任意のタイミングでデータ連携を行えれば良い。
  • 秘匿性のあるデータはなく、特段マスキングの必要はない。
  • 国内リージョンにデータを保存すること。(AWS:ap-northeast-1 GoogleCloud:asia-northeast1)

構成図

AWS側

  • Aurora (System Snapshot)
  • S3 (Snapshot Export の保存先)
  • IAMロール (Snapshot Export 使用時の権限)
  • IAMユーザ (GoogleCloud Data Transfer Service 使用時の権限)
  • KMS (Snapshot Export 使用時の暗号化キー)

GoogleCloud側

  • API有効化 (BigQuery API / BigQuery Data Transfer Service API)
  • サービスアカウント作成 (BigQuery Data Transfer Service用)
  • Data Transfer Service (AuroraからBigQueryへのデータ移行を実施)
  • BigQuery (データ格納先)

実装手順

AWS側

################################################################################
# S3
################################################################################
module "s3_google_cloud_bigquery" {
  source  = "terraform-aws-modules/s3-bucket/aws"
  version = "4.5.0"

  bucket = "hogehoge-aurora-to-bigquery"

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


################################################################################
# IAMロール
################################################################################
module "iam_role_aurora_s3_export" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-assumable-role"
  version = "5.52.2"

  create_role = true
  role_name   = "aurora-s3-export"

  create_custom_role_trust_policy = true
  custom_role_trust_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = ["export.rds.amazonaws.com"]
        }
      },
    ]
  })

  inline_policy_statements = [
    {
      actions   = ["s3:ListAllMyBuckets"]
      effect    = "Allow"
      resources = ["*"]
    },
    {
      actions = [
        "s3:ListBucket",
        "s3:GetBucketLocation",
      ]
      effect    = "Allow"
      resources = [module.s3_google_cloud_bigquery.s3_bucket_arn]
    },
    {
      actions = [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
      ]
      effect    = "Allow"
      resources = ["${module.s3_google_cloud_bigquery.s3_bucket_arn}/*"]
    },
  ]
}


################################################################################
# IAMユーザー
################################################################################
module "iam_user_google_cloud_for_bigquery_data_transfer" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-user"
  version = "5.52.2"

  name                          = "google-cloud_for_bigquery-data-transfer"
  create_iam_user_login_profile = false
}

module "iam_policy_google_cloud_for_bigquery_data_transfer" {
  source  = "terraform-aws-modules/iam/aws//modules/iam-policy"
  version = "5.52.2"

  name = "google-cloud_for_bigquery-data-transfer"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid = "S3ReadOnlyAccessForBucket"
        Action = [
          "s3:Listbucket",
        ]
        Effect   = "Allow"
        Resource = [module.s3_google_cloud_bigquery.s3_bucket_arn]
      },
      {
        Sid = "S3ReadOnlyAccessForBigQuery"
        Action = [
          "s3:GetObject",
        ]
        Effect   = "Allow"
        Resource = ["${module.s3_google_cloud_bigquery.s3_bucket_arn}/*"]
      },
      {
        Action = [
          "kms:Decrypt",
        ]
        Effect   = "Allow"
        Resource = [module.kms_for_aurora_s3_export.key_arn]
      }
    ]
  })
}

resource "aws_iam_user_policy_attachment" "google_cloud_for_bigquery_data_transfer" {
  user       = module.iam_user_google_cloud_for_bigquery_data_transfer.iam_user_name
  policy_arn = module.iam_policy_google_cloud_for_bigquery_data_transfer.arn
}


################################################################################
# KMS
################################################################################
module "kms_for_aurora_s3_export" {
  source  = "terraform-aws-modules/kms/aws"
  version = "3.1.1"

  description = "for Aurora S3 Export"
}

################################################################################
# RDS(Aurora)
################################################################################
data "aws_db_cluster_snapshot" "this" {
  db_cluster_identifier          = "aurora-cluster"
  db_cluster_snapshot_identifier = "rds:aurora-cluster-test-YYYY-MM-DD-hh-mm"
}

resource "aws_rds_export_task" "this" {
  export_task_identifier = "hodehoge-test"
  source_arn             = data.aws_db_cluster_snapshot.this.db_cluster_snapshot_arn
  s3_bucket_name         = module.s3_google_cloud_bigquery.s3_bucket_id
  iam_role_arn           = module.iam_role_for_aurora_s3_export.iam_role_arn
  kms_key_id             = module.kms_for_aurora_s3_export.key_arn
}


################################################################################
# (GoogleCloud側への連携パラメータ)
################################################################################
output "iam_user_google_cloud_for_bigquery_data_transfer_iam_access_key_id" {
  value = module.iam_user_google_cloud_for_bigquery_data_transfer.iam_access_key_id
}

output "iam_user_google_cloud_for_bigquery_data_transfer_iam_access_key_secret" {
  value     = module.iam_user_google_cloud_for_bigquery_data_transfer.iam_access_key_secret
  sensitive = true
}

GoogleCloud側

################################################################################
# Other
################################################################################
data "terraform_remote_state" "aws_account_1" {
  backend = "s3"

  config = {
    bucket = "{aws_account_1においてterraformのstateを保存するS3バケット名}"
    key    = "{上記S3バケットに格納しているterraformのstateファイル名}"
    region = "ap-northeast-1"
  }
}

locals {
  aws_aurora = {
    database_1 = {
      table_1 = {}
      table_2 = {}
    }
    database_2 = {
      table_1 = {}
      table_2 = {}
    }
  }

  # データセットとテーブルの構造を平坦化
  aws_aurora_tables = merge([
    for dataset_name, tables in local.aws_aurora : {
      for table_name, table_config in tables : "${dataset_name}_${table_name}" => {
        dataset_name = dataset_name
        table_name   = table_name
        schema       = table_config
      }
    }
  ]...)
}


###############################################################################
# Project
###############################################################################
resource "google_project" "this" {
  name            = "hogehoge"
  project_id      = data.google_client_config.self.project
}

data "google_client_config" "self" {}


###############################################################################
# Service Account
###############################################################################
resource "google_service_account" "aws_aurora" {
  account_id   = "aws-aurora"
  display_name = "aws-aurora"
  project      = google_project.this.project_id
}


###############################################################################
# BigQuery
###############################################################################
resource "google_bigquery_dataset" "aws_aurora" {
  for_each = local.aws_aurora

  dataset_id = each.key
  location = "asia-northeast1"
}

resource "google_bigquery_table" "aws_aurora" {
  for_each = local.aws_aurora_tables

  dataset_id = each.value.dataset_name
  table_id   = each.value.table_name

  depends_on = [
    google_bigquery_dataset.aws_aurora
  ]
}


###############################################################################
# BigQuery Data Transfer Service
###############################################################################
resource "google_bigquery_data_transfer_config" "aws_aurora" {
  for_each = local.aws_aurora_tables

  location       = "asia-northeast1"
  data_source_id = "amazon_s3"
  display_name   = google_bigquery_table.aws_aurora[each.key].table_id
  schedule_options {
    disable_auto_scheduling = true
  }
  destination_dataset_id = each.value.dataset_name
  service_account_name   = google_service_account.aws_aurora.email

  params = {
    destination_table_name_template = google_bigquery_table.aws_aurora[each.key].table_id
    data_path                       = "s3://${data.terraform_remote_state.kidsna_sitter_stg.outputs.s3_google_cloud_bigquery_bucket_id}/*/${each.value.dataset_name}/${each.value.dataset_name}.${google_bigquery_table.aws_aurora[each.key].table_id}/1/*.parquet"
    access_key_id                   = data.terraform_remote_state.kidsna_sitter_stg.outputs.iam_user_google_cloud_for_bigquery_data_transfer_iam_access_key_id
    file_format                     = "PARQUET"
  }
  sensitive_params {
    secret_access_key = data.terraform_remote_state.kidsna_sitter_stg.outputs.iam_user_google_cloud_for_bigquery_data_transfer_iam_access_key_secret
  }

  depends_on = [
    google_bigquery_dataset.aws_aurora
  ]
}

補足事項

Aurora S3 Export

  • BigQueryに連携する時点で都度実施します。
data "aws_db_cluster_snapshot" "test" {
  db_cluster_identifier          = aws_rds_cluster.cluster_stg_2.id
  db_cluster_snapshot_identifier = "{その時点で最新のシステムスナップショット名}"
}

resource "aws_rds_export_task" "test" {
  export_task_identifier = "test-${formatdate("YYYYMMDDHHmmss", timestamp())}"
  source_arn             = data.aws_db_cluster_snapshot.test.db_cluster_snapshot_arn
  s3_bucket_name         = module.s3_google_cloud_bigquery.s3_bucket_id
  iam_role_arn           = module.iam_role_for_aurora_s3_export.iam_role_arn
  kms_key_id             = module.kms_for_aurora_s3_export.key_arn
}

BigQuery

  • 最終的にデータベースのテーブル単位で”google_bigquery_table”を作成する必要があるため、for_eachを用いて再帰的に各テーブルを作成できるようしている。
locals {
  aws_aurora = {
    database_1 = {
      table_1 = {}
      table_2 = {}
      new_table_1 = {}  // 新規追加
    }
    database_2 = {
      table_1 = {}
      table_2 = {}
    }
    new_database_1 = {  // 新規追加
      new_table_1 = {}
    }
  }
}

BigQuery Data Transfer

  1. GoogleCloud Consoleにログインし、該当のプロジェクトを選択したうえで、BigQueryの画面に遷移します。

  2. 連携するテーブル名のデータ転送を選択し、「今すぐ転送を実行」 -> 「OK」をクリックします。

  3. ログ上、「Summary: succeeded 1 jobs, failed 0 jobs.」となっていれば成功です。

  4. 該当のBigQueryテーブルにデータが取り込まれていることを確認します。

最後に

今回はβ版的な扱いでベースとなる環境構築の手順をご紹介しました。次回は週次・日次でデータ連携を行う場合に必要な環境構築などをご紹介できればと思います。

nextbeat Tech Blog

Discussion