Amazon Web Services ブログ

Amazon Athena を使用したクロスアカウントの AWS Glue データカタログ

多くの AWS のお客様は、複数アカウント戦略を用いています。一元化された AWS Glue データカタログは、異なるアカウント間におけるメタデータの共有に関連する管理の量を最小化するために重要です。この投稿では、Amazon Athena が異なる AWS アカウント間で一元化されたデータカタログをクエリすることを可能にする機能を紹介します。

ソリューションの概要

2019 年後半、AWS は、Amazon Athena を Apache Hive Metastore に接続する機能を導入しました。この機能により、別のアカウントのデータカタログをポイントするように Athena を設定することもできます。Hive Metastore 機能は、AWS Lambda 関数を使用して、選択したデータカタログにクエリをフェデレーションします。この同じ機能で、カタログクエリを別のアカウントのデータカタログにプロキシできます。

次の図は、2 つの異なるアカウントで使用される必要なコンポーネントと、Athena を使用したクロスアカウントの Glue データカタログアクセスのためのアカウント間のフローを示しています。

このチュートリアルでは、Athena クエリを実行するのと同じアカウント (アカウント B) で Lambda 関数を作成します。リソースポリシーを使用して Lambda 関数にクロスアカウントアクセスを許可します。これにより、アカウント B の関数がアカウント A のデータカタログをクエリできます。アカウント B のユーザーは、テーブルがポイントし、Lambda 関数を実行するためのアクセス権を有する Amazon S3 リソースへのアクセス権を持っている必要があります。Lambda 関数の実装の詳細は、Github リポジトリを参照してください。

この投稿では、Lambda 関数およびその関数の読み取り専用 IAM ロールを作成するための AWS CloudFormation スタックも提供しています。この投稿では、AWS の Registry of Open Data のデータを使用します。S3 のデータへのクロスアカウントアクセスを確保する必要はありません。

前提条件

このウォークスルーには、以下の前提条件が必要です。

  • IAM ロールおよび Lambda リソースを作成し、Athena クエリを実行する機能を持つ AWS アカウント (図のアカウント B)
  • データカタログを作成できる 2 つ目の AWS アカウント (アカウント A)
  • アカウント A の AWS Glue への AWS CLI 管理者アクセス

この投稿の一部として、アカウント A のデータカタログにアクセスするため、アカウント B で (CloudFormation スタックが起動する) 読み取り専用の IAM ロールをアカウント B の Lambda 関数用に作成します。

このロールには、2 つのポリシーが関連付けられています。最初のポリシーは、クロスアカウントのデータカタログ内にある指定されたリソースへの読み取り専用アクセスを Lambda 関数に提供します。次のコードを参照してください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "glue:BatchGetPartition",
                "glue:GetDatabase",
                "glue:GetDatabases",
                "glue:GetPartition",
                "glue:GetPartitions",
                "glue:GetTable",
                "glue:GetTables",
                "glue:GetTableVersion",
                "glue:GetTableVersions"
            ],
            "Resource": [
                "arn:aws:glue:us-east-1:<account-id-A>:catalog",
                "arn:aws:glue:us-east-1:<account-id>:database/<database-name>",
                "arn:aws:glue:us-east-1:<account-id-A>:table/<database-name>/<table-name>"
            ],
            "Effect": "Allow"
        }
    ]
}

2 つ目のポリシーは、CloudWatch Logs への書き込み権限を Lambda 関数に提供します。次のコードを参照してください。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

次の追加リソースも作成します。

  • データカタログリクエストをプロキシするアカウント B の Lambda 関数 (CloudFormation スタックによって起動されます)
  • アカウント A のデータカタログにポリシーを追加することによって Lambda 関数に付与されたクロスアカウント実行アクセス
  • アカウント A のサンプルテーブル

この投稿の CloudFormation スタックは、アカウント B に Lambda 関数を作成し、上記の 2 つのポリシーをアタッチすることにより、必要なアクセス権を付与します。このボタンをクリックして、CloudFormation テンプレートを CloudFormation コンソールから us-east-1 リージョンにデプロイします。

以下の説明のとおり、スタックを起動するパラメータの値を入力してください。

  • GlueDataCatalogAccountID: 一元化されたデータカタログとして機能するアカウント (アカウント A) からアカウントIDを決定し、値として入力します
  • Region: us-east-1
  • DatabaseName: クロスアカウントの Glue カタログのこのデータベースにアクセスすることを Lambda 関数に許可します。このブログでは opendata と入力します
  • TableName: クロスアカウントの Glue カタログで指定された上記のデータベースのこのテーブルにアクセスすることを Lambda 関数に許可します。このブログでは * を入力します。これにより、上記で指定したデータベースのすべてのテーブルにアクセスできます

スタックを作成するボタンが表示されるまで、[Next] ボタンを押します。スタックのプロビジョニングが完了すると、チュートリアルを開始する準備が整います。

注: Lambda 関数がより多くのデータベースおよびテーブルにアクセスできるようにするには、CloudFormation スタックによって作成された IAM ロールにアタッチされたインライン IAM ポリシーの AWSGlueReadOnlyAccess を編集して、リソースを追加できます。

クロスアカウントアクセスの付与

クロスアカウントのデータカタログにアクセスする Athena クエリを実行するアカウントで Lambda 関数を作成した後、Athena 内部のデータソースとしてその Lambda 関数を登録する必要があります。

集中型データカタログの登録

最初に、プレビュー機能にアクセスするためのワークグループを作成します。次に、Athena を Apache Hive Metastore に接続する手順に従います。[接続の詳細] ページで、Lambda 関数について、CloudFormation スタックが作成した Lambda 関数を選択します。 <StackName>-AthenaCrossAccountLambdaFunc-<StackID>のようになります。データカタログに centraldata という名前を付けます。

クロスアカウント実行アクセス権の付与

アカウント B の IAM ロールへのアクセス権を付与するポリシーをアタッチして、アカウント A のデータカタログのデータベースおよびテーブルを読み取ります。アカウント A で次のコマンドを実行します。ただし、次の変更を行ってください。

  • (アカウント B から) CloudFormation スタックの [出力] タブにある CrossAccountPrincipal の値でプリンシパルを置き換えます。
  • <ACCOUNT_ID> をアカウント A の AWS アカウント ID で置き換えます。

リソースセクションで、<database-name> を、クエリするクロスアカウントのカタログのデータベース名で置き換えます。この投稿では opendata を使用しています。

aws glue put-resource-policy --policy-exists-condition NOT_EXIST --policy-in-json '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "glue:GetDatabase",
        "glue:GetDatabases",
        "glue:GetPartition",
        "glue:GetPartitions",
        "glue:GetTable",
        "glue:GetTables"
      ],
      "Principal": {"AWS": [
        "<REPLACE_WITH_CLOUDFORMATION_CrossAccountPrincipal_VALUE>"
      ]},
      "Resource": [
        "arn:aws:glue:us-east-1:<ACCOUNT_ID_A>:catalog",
        "arn:aws:glue:us-east-1:<ACCOUNT_ID_A>:database/<database-name>",
        "arn:aws:glue:us-east-1:<ACCOUNT_ID_A>:table/<database-name>/*"
      ]
    }
  ]
}'

上記のコードは、既存のポリシーがまだアタッチされていない場合に、データカタログにポリシーをアタッチします。ポリシーがアタッチされており、上書きしたい場合は、コードから --policy-exists-condition NOT_EXIST を削除します。既存のポリシーを –policy-in-json の値で置き換えます。AWS Glue コンソールでポリシーを直接編集して、既存のポリシーを --policy-in-json の値とマージすることもできます。

アカウント間でクエリを実行する

Lambda 関数を作成して Athena に登録したので、アカウント間でクエリを実行できるようになりました。

AWS の Registry of Open Data から Amazon カスタマーレビューのサンプルテーブルを作成します。このテーブルは、アカウント B でクエリを実行するために使用できます。次の手順を実行してください。

  1. アカウント A にログインし、[Athena コンソール] を開きます。
  2. 次の 3 つのクエリを 1 回に 1 つずつ実行します。
    • この例のデータベースを作成するには、次のクエリを実行します: CREATE DATABASE opendata
    • サンプルテーブルを作成するには、次のクエリを実行します。
      CREATE EXTERNAL TABLE amazon_reviews_parquet(
        marketplace string,
        customer_id string,
        review_id string,
        product_id string,
        product_parent string,
        product_title string,
        star_rating int,
        helpful_votes int,
        total_votes int,
        vine string,
        verified_purchase string,
        review_headline string,
        review_body string,
        review_date bigint,
        year int)
      PARTITIONED BY (product_category string)
      ROW FORMAT SERDE 
        'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' 
      STORED AS INPUTFORMAT 
        'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' 
      OUTPUTFORMAT 
        'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
      LOCATION
        's3://amazon-reviews-pds/parquet/'
    • テーブルにパーティションを追加するには、次のクエリを実行します: MSCK REPAIR TABLE amazon_reviews_parquet
  3. Athena アカウント (アカウント B) にログインします。
  4. Athena コンソールから、次のクエリを入力します: SELECT * FROM centraldata.opendata.amazon_reviews_parquet LIMIT 10;

次のスクリーンショットは、上記のクエリの結果である 100 のランダムな行を示しています。

このクエリは Athena アカウントで実行されますが、プライマリアカウントの一元化されたデータカタログにアクセスします。次に、Athena は、S3 からデータを取得し、Athena アカウントでクエリを処理します。アカウント間でデータを結合したり、あるアカウントのデータを使用して別のアカウントに派生テーブルを作成することもできます。

アカウント A のデータからアカウント B の派生テーブルを作成する

この投稿では、ある製品カテゴリにおいて、役立つレビューを残した顧客が、他のカテゴリでもレビューを残すかどうかを判断するために、Amazon Reviews テーブルから派生データセットを作成することを希望している状況を想定します。

Athena の CREATE TABLE AS SELECT 機能で、一元化されたデータカタログのデータを使用して、Athena クエリを実行しているアカウントに新しいテーブルを作成します。Toys の製品カテゴリで 1,000 件を超えるレビューを書いた顧客を検索するテーブルをデフォルトのデータベースに作成できます。次のコードを参照してください。

CREATE TABLE default.helpful_reviewers
WITH (
  format='PARQUET'
) AS
SELECT customer_id,
    SUM(helpful_votes) AS total_helpful_votes,
    SUM(total_votes) AS total_votes,
    AVG(star_rating) AS avg_star_rating,
    SUM(total_votes)-SUM(helpful_votes) AS vote_differential,
    ROUND(SUM(helpful_votes)/CAST(SUM(total_votes) AS double),4) AS pct_helpful
FROM centraldata.opendata.amazon_reviews_parquet 
WHERE product_category='Toys' 
GROUP BY 1
HAVING SUM(total_votes) > 1000
ORDER BY 2 DESC

クエリを実行すると、[Query successful] というメッセージが表示されます。これで、結果のデータを確認できます。次のクエリを実行します。

SELECT * FROM default.helpful_reviewers LIMIT 100;

次のスクリーンショットは、default.helpful_reviewers での上記のクエリの結果である 10 のランダムな行を示しています。

結果は、一部の人々が多数のレビューを残すことを示しています。アカウントのこの派生データセットに対するクエリを実行できるようになりました。これは、研究に関連するデータの集計を既に行っているため、より効率的です。

アカウント間でテーブルを結合する

同じ人からのレビューがある他の製品カテゴリを調べることもできます。次のクエリは、アカウント B のテーブルからデータを選択し、アカウント A の元のデータと結合します。

SELECT customer_id,
         product_category,
         COUNT(*) AS count
FROM centraldata.opendata.amazon_reviews_parquet
WHERE customer_id IN 
    (SELECT customer_id
    FROM default.helpful_reviewers)
        AND product_category != 'Toys'
GROUP BY  1, 2
ORDER BY  3 DESC

次のスクリーンショットは、上記のクエリの結果を示しています。これは、Toys の製品カテゴリで 1,000 件以上のレビューを書いた顧客、「toy」以外の製品カテゴリ、特定の製品カテゴリで顧客が残したレビュー数です。

Toys のカテゴリにレビューを残した人は、Books および Video のカテゴリにも多くのレビューを残したことがわかります。

クリーンアップ

今後の請求が発生しないようにするには、Athena アカウントからデータカタログエントリの登録を解除し、CloudFormation スタックを削除します。

制限

これは 1 つ以上のアカウントでデータカタログを共有する効果的な方法ですが、このアプローチには次の制限があります。

  • 承認 – Lambda 関数は、CloudFormation テンプレートの一部として作成した IAM ロールとして実行されるため、アカウント間でこのデータカタログをクエリする IAM ユーザーまたはロールは、IAM ロールがアクセスできるデータカタログから同じメタデータを見ることができます。S3 のアクセスポリシーは引き続き有効です。
  • 読み取り専用 – 現在の実装では、中央チームが一元化されたデータカタログも管理していると想定されているため、読み取り専用アクセスに必要な機能のみが実装されています。

まとめ

この投稿では、Athena External Hive Metastore 機能を使用して、AWS アカウント間でデータカタログをクエリする方法を示しました。また、アカウントで派生データセットを作成し、これらの 2 つのアカウント間でデータを結合しました。Lambda 関数のコードはオープンソースです。GitHub リポジトリをご覧いただき、貢献いただくことを歓迎いたします。

 


著者について

Pathik Shah は AWS の Amazon EMR のビッグデータアーキテクトです。