Amazon Redshift: AWSアカウントを跨いだS3バケットからのCOPY処理についてまとめてみた

2016.05.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Amazon Redshiftにデータを取込む際は任意のS3バケットにファイルをアップロードしてRedshiftのCOPYコマンドで取込む、というのが最もポピュラーな方法となりますが、RedshiftクラスタS3のバケットの関係性については幾つか選択肢が存在します。ここでの『選択肢』とは、リージョンやアカウントが同じ場所にあるか、または異なっているかという部分の組み合わせです。

リージョンの違いによるものについてはRedshiftの関連機能追加もあり把握していましたが、アカウントを跨いだ形のものについては私個人としても曖昧な把握に留まっていましたので、改めて当エントリでその辺りを整理してみる事にしました。

目次

前提条件解説

今回検証するにあたって必要となるRedshiftクラスタはアカウントID:111111111111リージョン:N.Virginia(us-east-1)に配備されているものとし、そのクラスタに対してそれぞれのアカウント・リージョンに用意されたS3バケットのファイルをCOPY処理で取込む際の手順について見て行く形としたいと思います。

今回行う検証のメインとなる『異なるアカウントからの作業』を実現する為、アカウントID:222222222222を別途用意し、S3バケットも必要分用意する、という形としています。

想定したケースは以下4つ。

No. Redshift 配備アカウント Redshift 配備リージョン S3バケット 配備アカウント S3バケット 配備リージョン 備考
(1). 111111111111 N.Virginia (us-east-1) 111111111111 N.Virginia (us-east-1) 同一アカウント、同一リージョン
(2). 111111111111 N.Virginia (us-east-1) 111111111111 Tokyo (ap-northeast-1) 同一アカウント、異なるリージョン
(3). 111111111111 N.Virginia (us-east-1) 222222222222 N.Virginia (us-east-1) 異なるアカウント、同一リージョン
(4). 111111111111 N.Virginia (us-east-1) 222222222222 Tokyo (ap-northeast-1) 異なるアカウント、異なるリージョン

構成図としては以下の様な形となります。

redshift-cross-account-copy_00

アカウントID:111111111111配下で、RedshiftのCOPYを実践するIAMユーザーを作成します。作成したユーザーにはS3とRedshiftのReadOnlyアクセス権限を付与しました。この時のIAMユーザーのUser ARNはarn:aws:iam::111111111111:user/redshift-userとなります。

redshift-cross-account-copy_01

それぞれのアカウント、リージョンにアップロードされたS3バケット配下のファイルを取り込むRedshiftのテーブルは以下のものを用意しました。Redshift関連エントリを書く時に私が良く使っているいつものアレ(Tableauのデモ用サンプルデータ)です。

# CREATE TABLE public.copytest_orders (
    order_id INT encode lzo NOT NULL, /** オーダー ID */
    order_date DATE encode delta NOT NULL, /** 受注日 */
    priority VARCHAR(15) encode bytedict NOT NULL, /** 優先度 */
    quantity SMALLINT encode mostly8 NOT NULL, /** 数量 */
    sales BIGINT encode lzo NOT NULL, /** 売り上げ */
    discount_rate DOUBLE PRECISION, /** 割引率 */
    shipping_mode VARCHAR(20) encode bytedict NOT NULL, /** 出荷モード */
    profit INT encode lzo NOT NULL, /** 利益 */
    unit_price INT encode lzo NOT NULL, /** 単価 */
    ad_expences INT encode lzo NOT NULL, /** 宣伝費 */
    shipping_cost INT encode mostly16 NOT NULL, /** 発送費用 */
    customer_name VARCHAR(50) encode lzo NOT NULL, /** 顧客名 */
    prefecture VARCHAR(20) encode bytedict NOT NULL, /** 都道府県 */
    city VARCHAR(30) encode bytedict NOT NULL, /** 市町村 */
    area VARCHAR(15) encode bytedict NOT NULL, /** 地域 */
    shop_name VARCHAR(50) encode lzo NOT NULL, /** 店名 */
    customer_segment VARCHAR(50) encode bytedict NOT NULL, /** 顧客区分 */
    product_category VARCHAR(50) encode bytedict NOT NULL, /** 製品カテゴリー */
    product_sub_category VARCHAR(100) encode bytedict NOT NULL, /** 製品サブカテゴリー */
    product_id CHAR(15) encode lzo NOT NULL, /** プロダクト ID */
    product_name VARCHAR(300) encode lzo NOT NULL, /** 製品名 */
    product_description VARCHAR(500) encode lzo NOT NULL, /** 製品説明 */
    product_container VARCHAR(50) encode bytedict NOT NULL, /** 製品コンテナー */
    product_base_margin DOUBLE PRECISION, /** 製品ベースマージン */
    supplier VARCHAR(50) encode bytedict NOT NULL, /** サプライヤー */
    delivery_date DATE encode delta NOT NULL, /** 配達予定日 */
    shipment_date DATE encode delta NOT NULL, /** 発送日 */
    PRIMARY KEY(order_id)  
)
distkey(order_id)
# sortkey(order_date);
CREATE TABLE

ケース別実践内容(IAMユーザーの場合)

(1).同一アカウント、同一リージョンにあるS3からのCOPY処理

ここからはケース毎に準備・実行手順を見て行きたいと思います。

1つ目のケースはRedshiftでデータを取込む際の構成としては基本中の基本、同一アカウント&リージョンにRedshiftとS3が存在するものです。

Redshiftクラスタと同じリージョン(N.Virginia)にバケットを作成し、

redshift-cross-account-copy_02

取込むファイルをアップロード。

redshift-cross-account-copy_03

COPYコマンドによるデータの投入実施。これが基本の形となります。

# COPY public.copytest_orders FROM 's3://cm-same-account-bucket-virginia/orders.csv'
# CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
# DELIMITER ','
# CSV QUOTE AS '"'
# DATEFORMAT 'YYYY/MM/DD'
# IGNOREHEADER 1;
INFO:  Load into table 'copytest_orders' completed, 8369 record(s) loaded successfully.
COPY

(2).同一アカウント、異なるリージョンにあるS3からのCOPY処理

2つ目のケースは同一アカウント&リージョン違い。こちらについては、以前ご紹介した『クロスリージョンCOPY』を実現する為のオプションをCOPY実行時に付与するだけで対応可能です。

バケット作成はTokyoリージョンで。

redshift-cross-account-copy_04 redshift-cross-account-copy_05

REGION AS 'リージョン名'をオプションとして指定する事で無事取り込めました。

# COPY public.copytest_orders FROM 's3://cm-same-account-bucket-tokyo/orders.csv'
# CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
# DELIMITER ','
# CSV QUOTE AS '"'
# DATEFORMAT 'YYYY/MM/DD'
# IGNOREHEADER 1
# REGION AS 'ap-northeast-1';
INFO:  Load into table 'copytest_orders' completed, 8369 record(s) loaded successfully.
COPY

(3).異なるアカウント、同一リージョンにあるS3からのCOPY処理

ここからが本エントリの本題となります。取込むデータがRedshiftクラスタの存在するAWSアカウントとは別のアカウントに存在する場合です。Redshiftで指定するS3バケット・フォルダ配下が異なるAWSアカウントのものになる形です。

このケースの処理を実現する為には、対象となるS3バケットに『バケットポリシー』を設定します。まずは必要となるS3バケットを異なるAWSアカウントにて作成。リージョンはRedshiftと同じN.Virginiaとしています。作成したら必要なファイルもアップロードしておきます。

redshift-cross-account-copy_06

作成したバケットの[Add Bucket Policy]を選択し、

redshift-cross-account-copy_07

異なるアカウントからのアクセスを許可するバケットポリシーを貼り付け、保存します。

redshift-cross-account-copy_08

設定内容は以下の通り。Principal配下に記載している内容(アカウント111111111111配下のユーザー:redshift-user)に対して、Resource(cm-another-account-bucket-virginia)配下のアクション(s3:Get*, s3:List*)を許可(Allow)する、という内容となります。

{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Sid": "DelegateS3Access",
         "Effect": "Allow",
         "Principal": {
            "AWS": "arn:aws:iam::111111111111:user/redshift-user"
         },
         "Action": [
            "s3:Get*",
            "s3:List*"
         ],
         "Resource": [
            "arn:aws:s3:::cm-another-account-bucket-virginia",
            "arn:aws:s3:::cm-another-account-bucket-virginia/*"
         ]
      }
   ]
}

この形でCOPYを実行してみます。問題無く取り込めました!

# COPY public.copytest_orders FROM 's3://cm-another-account-bucket-virginia/orders.csv'
# CREDENTIALS 'aws_access_key_id=AKIAIQJQIHQCWWDVTK5Q;XXXXXXXXXX=YYYYYYYYYY'
# DELIMITER ','
# CSV QUOTE AS '"'
# DATEFORMAT 'YYYY/MM/DD'
# IGNOREHEADER 1;
INFO:  Load into table 'copytest_orders' completed, 8369 record(s) loaded successfully.
COPY

(4).異なるアカウント、異なるリージョンにあるS3からのCOPY処理

最後4つ目はケース(3)の変形Ver.となります。内容的にはケース(3)の対処を行いつつ、ケース(2)同様にオプション指定を追加する形で対応が可能となります。

異なるAWSアカウント配下にクラスタ配備リージョンとは異なるリージョンのS3バケットを作成し、ファイルをアップロード。

redshift-cross-account-copy_09

バケットポリシーも併せて設定。

redshift-cross-account-copy_10

COPY実行時のオプションもケース2同様設定する事でデータを取込む事が出来ました!

# COPY public.copytest_orders FROM 's3://cm-another-account-bucket-tokyo/orders.csv'
CREDENTIALS 'aws_access_key_id=XXXXXXXXXX;aws_secret_access_key=YYYYYYYYYY'
DELIMITER ','
CSV QUOTE AS '"'
DATEFORMAT 'YYYY/MM/DD'
IGNOREHEADER 1
REGION AS 'ap-northeast-1';
INFO:  Load into table 'copytest_orders' completed, 8369 record(s) loaded successfully.
COPY

ケース別実践内容(IAM Roleの場合)

上記までの内容は『IAMユーザー』でのアクセスを前提としたものとなりますが、先日Redshiftでも『IAM Role』を使ったアクセスが出来るようになりました。詳細については以下エントリをご参照ください。

当エントリでは併せて、『IAM Role』を使ったクロスアカウントのCOPY処理も試してみたいと思います。

上記エントリを参考に、S3へのReadOnlyAccess権限を持ったIAM Roleを作成します。Role ARNの内容はarn:aws:iam::111111111111:role/cm-redshift-roleです。

redshift-cross-account-copy_11

対象となるクラスタに上記作成のIAM Roleを付与。

redshift-cross-account-copy_12

別アカウントに移動し、S3バケットを作成。

redshift-cross-account-copy_13

バケットポリシーを上記作成バケットに付与。Principalの部分がroleのARNに変更しておきます。

{
   "Version": "2012-10-17",
   "Statement": [
      {
         "Sid": "DelegateS3Access",
         "Effect": "Allow",
         "Principal": {
            "AWS": "arn:aws:iam::111111111111:role/cm-redshift-role"
         },
         "Action": [
            "s3:Get*",
            "s3:List*"
         ],
         "Resource": [
            "arn:aws:s3:::cm-another-account-bucket-by-iamrole",
            "arn:aws:s3:::cm-another-account-bucket-by-iamrole/*"
         ]
      }
   ]
}

COPY処理実施。CREDENTIALSの部分を従来の形式では無く、IAM Roleを指定する方法に置き換えます。こちらも問題無く取込む事が出来ました!

# COPY public.copytest_orders FROM 's3://cm-another-account-bucket-by-iamrole/orders.csv'
# CREDENTIALS 'aws_iam_role=arn:aws:iam::111111111111:role/cm-redshift-role'
# DELIMITER ','
# CSV QUOTE AS '"'
# DATEFORMAT 'YYYY/MM/DD'
# IGNOREHEADER 1;
INFO:  Load into table 'copytest_orders' completed, 8369 record(s) loaded successfully.
COPY

まとめ

今回ご紹介した内容ではIAM User及びIAM Roleに割り当てる権限は予め用意されている設定(S3ReadOnlyAccess等)を活用しましたが、この部分をカスタムポリシーでS3権限付与したり、対象リソースや特定S3バケットのARNで制限を掛ける事でより完璧な構成を実現出来るかと思います。実案件に適用する際には是非ご検討ください。

Redshiftにデータを取込む際の状況としては、当エントリでご紹介しましたようにアカウントを跨いで行う必要が出てくるケースも出てくるかと思います。その際にこちらのエントリをご参考にして頂ければ幸いです。こちらからは以上です。