😃

RDSのスナップショットエクスポート機能をクロスアカウントで使う

6 min read

こんにちは。paulxllです。
これまではqiitaで同じIDで記事を書いていますが、今回はお試しも兼ねてzennを利用してみようと思います。
また、この記事はFOLIO Advent calendar 20202日目の記事として作成しました。

この記事について

RDSのスナップショットをParquet形式でS3にエクスポートできる機能(以下、エクスポートタスク)が発表されました。
スナップショットを使うため、本番DBに負荷をかけることなく分析基盤へのスムーズな連携ができ非常に便利な機能だと思います。

利用方法についてもすでにいくつかの記事が出ています。

  1. クラスメソッド株式会社さんのブログ
  2. 株式会社スナックミーさんの発表資料

上記をはじめいくつかの記事を参考にしつつ実際に業務でも取り入れられないか検討したところ、いくつかハマりどころにあたったので、実際にハマったポイントやtipsをまとめてみました。

なお、試すにあたり、本番DB(RDS)を運用しているAWSアカウント(以下、prodアカウント)と、分析基盤用のAWSアカウント(以下、analyticsアカウント)は別、つまりクロスアカウントアクセスの利用を前提として解説します。
組織の規模が一定以上であれば、チームごとにAWSアカウントが別であるユースケースは珍しくないと思うので、そうしたユースケースにおいて参考になれば幸いです。
またその前提において、コスト管理やデプロイの煩雑さの観点から、行う処理やリソースはできるだけ分析基盤用のAWSアカウントに寄せていく方針で検討してみました。

処理の全体像

処理の全体像
処理の全体像

準備が必要なリソース

analyticsアカウント

  1. S3
    1. スナップショットを出力するためのS3 bucketを準備
      1. prodアカウントのエクスポートタスク用のroleからの書き込みを許可するようにbucket policyを設定
  2. lambda
    1. 下記の2つの関数が必要になります。なお、どちらもprodアカウント配下のリソースを操作しますが、利用するIAMはprodアカウント配下に作成したroleをassume roleして利用することを想定しています。
      1. エクスポートタスクをkickするlambda関数
      2. エクスポートされたParquetのACLを変更するlambda関数
  3. Glue
    1. クローラー
      1. スナップショットを吐き出したS3の閲覧権限・prodアカウントから共有を受けているCMKの利用権限を付与
      2. Glueのデータベース・テーブルの作成・編集権限を付与

prodアカウント

  1. KMS
    1. 分析基盤用AWSアカウントと共有するためのCMK
      1. キーポリシーで、analyticsアカウントのglueにアクセス許可を与えておく
  2. IAM
    1. analyticsアカウントのlambda関数に、assume roleして渡すroleを準備します。
      1. エクスポートタスクが利用するrole
        1. 出力先S3へのアクセス権限
        2. 上記のCMKを利用する権限
          1. なお、RDS本体の暗号化がAWSのマネージドの暗号化キーではなくCMKで行われている場合、そのCMKの利用権限も必要
      2. エクスポートタスクをkickするためのlambda関数用のrole
        1. エクスポートタスクが利用するroleをpassroleできる必要がある
      3. エクスポートタスクで作成されたsnapshotのObjectACLを変更するrole
        1. スナップショットがエクスポートされるS3 bucket(analyticsアカウント配下)へのアクセスが必要

ハマりどころ・ポイントなど

エクスポートタスクの実行に時間がかかる

冒頭で引用したスナックミーさんの発表資料でも処理に2時間程度かかると言及されていた通りではありますが、やはりエクスポートタスクの実行に時間がかかります。

特にタスクの起動に時間がかかる(=おそらくデータ量に関係ない?)印象で、体感ですが毎回2-30分はかかってしまいます…。
また、lambdaの関数を2つに分けているのもそれが理由で、エクスポートタスクの終了を待ちつつACLを変更しようとすると、まずlamddaのtimeout上限に引っかかってしまうため、lambda関数を2つに分けています。

なので、このスナップショットの適していると考えられるユースケースとしては、

  • データの更新はバッチ処理ベースかつ日次更新など頻繁な更新がない
  • 分析基盤においても頻繁な更新ニーズがない

など挙げられるかと思います。マスタデータなどがまさにそうですね。
逆にある程度リアルタイム性を求めたい場合は、AWSだとDMSのCDCなどが有効かと思います。

エクスポートタスク実行時にObjectACLが指定できない

これはおそらく気づいたら機能追加されてそうな気もするのですが…。
現状、エクスポートタスクの実行時に出力オブジェクトのACLを変更するオプションはありません(下記)。
なので、エクスポートタスクが完了後、自分でACLを変更する必要があります。

参考ドキュメント:

  1. AWS CLI
  2. boto3

これがなければlambda一発で実行できて便利なんだけどなあ・・・というのが正直なところですがこれからに期待ですね。。

Glueクローラーでスナップショットを読みに行く際の設定小ネタ

ハマりどころではないですが、例えばこういうコマンドでエクスポートタスクをkickした場合、

start_export_task.sh
aws rds start-export-task \
        --export-task-identifier <export-task-identifier> \
        --source-arn <rds-cluster-snapshot-identifier> \
        --s3-bucket-name <your-s3-bucket> \
        --s3-prefix <your-s3-prefix> \
        --iam-role-arn <snapshot-export-iam-role> \
        --kms-key-id <cmk-arn-for-snapshot-encryption>

出力先のS3のPATHは、
s3://<your-s3-bucket>/<your-s3-prefix>/<export-task-identifier>/<database_name>/<tablename>/<parquet-file-name>.gz.parquet
といった形式になります。

glueのcrawlerを特にオプションなどを設定せず利用した場合、エクスポートごとにすべて個別のテーブルとして認識してしまうのですが、クローラーを作成する際にコンソールで S3 パスごとに単一のスキーマを作成する のチェックボックスをオンにするとこれを回避でき、テーブルのスキーマが同じである限りにおいては、同じテーブルとして認識してくれます。
terraformだとこんな形で設定できます。configuration ブロックに設定を追加してあります。

glue_crawler.tf
resource "aws_glue_crawler" "crawler" {
  database_name = "mydatabase"
  name          = "my-snapshot-crawler"
  role          = "crawl-encrypted-snapshot-role"
  s3_target {
    path       = "s3://my-snapshot-exported-bucket"
    // エクスポートタスクの結果がJSONで吐かれるが、使わないので除いておく
    exclusions = ["*json"]
  }
  configuration = <<EOF
{
    "Version": 1.0,
    "Grouping": {
        "TableGroupingPolicy": "CombineCompatibleSchemas"
    }
}
EOF
}

lambda関数の実行について

lamdba関数については、当初関数も利用するroleもprodアカウント配下に置いていましたが、チームメンバーからのアドバイスに従い、下記のように整理しました。
また、このIAM policyをterraformで書く際にも、アカウントごとにtfstateを管理している場合、参照が容易になるなどのメリットがあります。
特に、KMSのCMKはARNが作成時に決まり、事前に指定できないためこのメリットをよく享受できました。

  • prod-アカウント
    • RDSのスナップショットなど、必要なリソースの操作権限と、analyticsアカウントのlambda関数が実行できるようにassume roleを記載したrole
  • analyticsアカウント
    • prodアカウントで作成したroleを引き受け、lambdaで利用できるようassume role policyを設定したrole
    • 上記のroleを引き受けて実行するlambda関数

また、lambda関数のdeployは、こちらの記事を参考にterraformのリポジトリから行いました。これくらいのboto3の特定のメソッドを叩いて終わり、非常にシンプルな処理だとこのやり方が便利ですね。

あとがき・今後の改善策など

新しい機能らしくいくつかハマりどころはあったものの、ここまでに書いた方法でRDSを管理しているAWSアカウントと分析基盤用のAWSアカウントが別でも最低限のデータ分析環境が作れることを確認しました。
最後に、実装を進める上で気になった点、今後の改善点などにも触れておきます。

  1. 専用のCMKを払い出しているとはいえ、KMSの権限を共有するのやっぱり気持ち悪くない?
    1. ということで、「prodアカウントにあるKMSの復号権限と、S3の操作権限」を持つroleを作成し、「prodアカウントのCMKで暗号化していないオブジェクト」を別bucketに吐き出し、Glue crawlerからはそちらを参照する、なども実装としてあるかと思います。
  2. また全体的にステップ数が多いので、CloudwatchEvents(EventBridge)などを使ってスナップショットの作成からGlueでのクローリングまでをなめらかにつなぐことも検討できればと思っています。