Amazon S3からAmazon RDS for PostgreSQLにデータをロードできるようになりました
Amazon S3から
- Amazon RDS for PostgreSQL
- Amazon Aurora with PostgreSQL
に直接データをロードできるようになったのでご紹介します。
他のDBエンジンについては、以下の記事を参照ください。
旧方式:S3 → サーバー → RDS 方式
S3 から RDS にデータをロードする場合、以前は S3 のデータを一度 EC2 などのサーバーに転送し、サーバー上から COPY コマンドを利用して RDS に取り込む必要がありました。
新方式:S3 → RDS 方式
今回の機能追加により、サーバーを介することなく、 S3 から直接データをロードできるようになりました。
対応バージョン
本機能は
- Amazon RDS for PostgreSQL 11.1 以上
- Amazon Aurora with PostgreSQL
でのみ利用可能です。
Amazon RDS for PostgreSQL の 9系・10系といった古いエンジンでは利用できません。
手順概要
RDS for PostgreSQL と Aurora PostgreSQL で操作方法は実質的に同じため、以下では RDS for PostgreSQL を例に手順を紹介します。
- RDS 向け IAM ロールの作成
- RDS PostgreSQL 11 を起動
- S3 連携のエクステンションを有効化
- S3 から RDS PostgreSQL にデータをロード
1. RDS 向け IAM ロールの作成
RDS インスタンスに IAM ロールをアタッチし、 S3 を操作できるようにします。 そのためのロールを作成します。
Permission について
S3 を参照できる IAM ポリシーを用意します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "s3integration", "Action": [ "s3:GetObject", "s3:ListBucket", "s3:PutObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::your-s3-bucket-arn", "arn:aws:s3:::your-s3-bucket-arn/*" ] } ] }
マネージドポリシーを利用してまるっと参照権限を与えたい場合、AmazonS3ReadOnlyAccess を利用してください。
動作検証では、このマネージドポリシーを利用しました。
Trust Relationship について
assume するのは RDS のため、Trusted entities は RDS です。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "rds.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
2. RDS PostgreSQL 11 を起動
インスタンスの起動
RDS PostgreSQL インスタンスを起動します。
本機能はバージョン 11.1 以降にしか対応していないため、DB engine version に「11.1-R1」を選択します。
インスタンスにIAMロールをアタッチ
起動完了後、インスタンス詳細ページの Manage IAM roles エリアで
- Add IAM roles to this instance に作成した IAM ロール
- Feature に s3import
を選択し、Add role します。
接続確認
RDS インスタンスに接続できることを確認します。
$ psql -h mydbinstance.XXX.us-west-2.rds.amazonaws.com -U USERNAME DBNAME
3. S3 連携のエクステンションを有効化
S3 と RDS PostgreSQL 間のデータ連携は aws_s3 エクステンション(とこれが依存する aws_commons )を介して行います。 このエクステンションは、デフォルトでは無効化されているため、明示的に有効化します。
# エクステンションのデフォルトでの有効状態を確認 dbname=> select * FROM pg_available_extensions where name like 'aws%'; name | default_version | installed_version | comment -------------+-----------------+-------------------+--------------------------------------------- aws_s3 | 1.0 | | AWS S3 extension for importing data from S3 aws_commons | 1.0 | | Common data types across AWS services (2 rows) # エクステンションを有効化 dbname=> CREATE EXTENSION aws_s3 CASCADE; NOTICE: installing required extension "aws_commons" CREATE EXTENSION # 有効後の状態を確認 dbname=> \dx List of installed extensions Name | Version | Schema | Description -------------+---------+------------+--------------------------------------------- aws_commons | 1.0 | public | Common data types across AWS services aws_s3 | 1.0 | public | AWS S3 extension for importing data from S3 plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (3 rows)
利用する関数について
S3 連携では、以下3関数を利用します。
- S3 から RDS にロード(
aws_s3.table_import_from_s3
) - S3 の URI を定義(
aws_commons.create_s3_uri
) - IAM クレデンシャルを定義 (
aws_commons.create_aws_credentials
) (RDS インスタンスが STS ベースで一時クレデンシャルを取得し、 S3 を操作する限りは不要)
利用するデータ型について
aws_commons
エクステンションはAWS連携に必要なデータタイプを定義しています。
具体的には、以下です。
- AWS クレデンシャル型(
_aws_credentials_1
) - S3 URI型(
_s3_uri_1
)
dbname=> \d aws_commons.*; Composite type "aws_commons._aws_credentials_1" Column | Type | Modifiers ---------------+------+----------- access_key | text | secret_key | text | session_token | text | Composite type "aws_commons._s3_uri_1" Column | Type | Modifiers -----------+------+----------- bucket | text | file_path | text | region | text |
S3 から RDS PostgreSQL にデータをロード(CSVファイル)
S3 にあるシンプルな CSV ファイルを RDS PostgresQL に取り込んでみます。
S3 に CSV の サンプルデータをアップロード
CSV データ sample.csv を作成し、S3 にアップロードします。
$ cat sample.csv 1,foo 2,bar 3,baz $ aws s3 cp sample.csv s3://BUCKET-NAME/
ロード先テーブルの作成
S3 ファイルをロードするテーブルを用意します。
dbname=> create table test(a int, b text); CREATE TABLE
S3 のデータを RDS PostgreSQL にロード
psql 上で aws_s3.table_import_from_s3
を呼び出します。。
dbname=> SELECT aws_s3.table_import_from_s3( 'test', '', '(format csv)', 'BUCKET-NAME', 'sample.csv', 'us-west-2' ); table_import_from_s3 ----------------------------------------------------------------------- 3 rows imported into relation "test" from file sample.csv of 21 bytes (1 row) dbname=> select * from test; a | b ---+------ 1 | foo 2 | bar 3 | baz (3 rows)
sample.csv の内容が確かに取り込まれています。
aws_s3.table_import_from_s3
関数の引数は以下の通りです。
dbname=> SELECT aws_s3.table_import_from_s3( 'テーブル名', 'カラムリスト', -- 空文字('')の場合は、テーブルのカラムと一致 'PostgreSQL COPYの引数・フォーマット', 'S3バケット名', 'S3キー', 'S3リージョン' );
S3 から RDS PostgreSQL にデータをロード(圧縮非CSVファイル)
次に、セパレーターが |
で、gzip 圧縮されたファイルを RDS PostgresQL に取り込んでみます。
S3 にサンプルデータをアップロード
基本的に、先程と同じですが、注意点が一点だけあります。
gzip 圧縮されている場合、S3オブジェクトのメタデータを
- Key: Content-Encoding
- Value: gzip
で登録する必要があります。
$ cat sample.csv 1|foo 2|bar 3|baz $ gzip sample.csv # S3メタデータで content-encoding : gzip を設定 $ aws s3 cp sample.csv.gz s3://BUCKET-NAME/ --content-encoding gzip upload: ./sample.csv.gz to s3://BUCKET-NAME/sample.csv.gz $ aws s3api head-object --bucket BUCKET-NAME --key sample.csv.gz { "AcceptRanges": "bytes", "LastModified": "Thu, 25 Apr 2019 09:09:36 GMT", "ContentLength": 47, "ETag": "\"0bb041b3cb08674dd97849559876eefe\"", "ContentEncoding": "gzip", "ContentType": "application/octet-stream", "Metadata": {} }
ロード先テーブルの作成
S3 ファイルをロードするテーブルを用意します。
dbname=> create table test_gzip(a int, b text); Query OK, 0 rows affected (0.05 sec)
S3 のデータを RDS PostgreSQL にロード
psql 上で aws_s3.table_import_from_s3
を呼び出します。。
デリミターが ,
から |
にかわっているため、PostgreSQL COPY
の引数を 'DELIMITER ''|'''
に変更します。
dbname=> SELECT aws_s3.table_import_from_s3( 'test_gzip', '', 'DELIMITER ''|''', 'BUCKET-NAME', 'sample.csv.gz', 'us-west-2' ); table_import_from_s3 ------------------------------------------------------------------------------- 3 rows imported into relation "test_gzip" from file sample.csv.gz of 47 bytes (1 row) dbname=> select * from test_gzip; a | b ---+----- 1 | foo 2 | bar 3 | baz (3 rows)
sample.csv.gz の内容が確かに取り込まれています。
S3 URI をショートカットしたい
上記サンプルでは、S3 URI を毎回アドホックに指定しました。
aws_commons.create_s3_uri
関数を使うと、URI に名前をつけて定義し、使い回すことが可能です。
gset
を利用し、SELECT
結果を psql 変数に格納して利用します。
dbname=> SELECT aws_commons.create_s3_uri( 'BUCKET-NAME', 'sample.csv', 'us-west-2' ) AS s3_uri \gset dbname=> \echo :s3_uri (BUCKET-NAME,sample.csv,us-west-2) dbname=> SELECT aws_s3.table_import_from_s3( 'test', '', '(format csv)', :'s3_uri' ); table_import_from_s3 ----------------------------------------------------------------------- 3 rows imported into relation "test" from file sample.csv of 18 bytes (1 row)
まとめ
S3 のデータを RDS PostgreSQL にロードする方法を紹介しました。
データベースと S3 の連携は
- PostgreSQL ベースの Redshift は最初期から
- Aurora MySQL は数年前から
- RDS Oracle は今年2月下旬から
利用可能でした。RDS PostgreSQL でもようやく利用できるようになりました。
現時点では、Aurora PostgreSQL と RDS for PostgreSQL 11.1 以上でしか動作しないため、すぐに本番投入とはいかないこともあるとは思いますが、PostgreSQL を中心とした分析系システムを構築・運営しているエンジニアにとっては、非常に嬉しい機能アップデートではないかと思います。
それでは。
関連情報
- Importing Amazon S3 Data into an RDS PostgreSQL DB Instance - Amazon Relational Database Service
- Migrating Data to Amazon Aurora with PostgreSQL Compatibility - Amazon Aurora
- Amazon RDS for PostgreSQL Now Supports Data Import from Amazon S3@2019/04/24
- Amazon Aurora with PostgreSQL Compatibility Supports Data Import from Amazon S3@2019/06/20