DataSyncでアカウント&リージョンまたぎS3間データ転送をやってみた

2023.06.28

DataSyncはオンプレとAWSをまたいでデータを転送することができるサービスです。 ただこれはAWS内だけでの転送にも使うことができます。

また通常はデータ転送を実行するエージェントというものをEC2インスタンスなどに用意しておかなければならないのですが、 S3同士などであれば、ユーザはエージェントを用意する必要がなく、 AWSが用意してくれたエージェントを使ってデータの転送を行うことができます!

今回、アカウントとリージョンをまたいだS3間のファイル転送をやってみる機会があったので、 そのやり方について記録を残しておこうと思います。

今回やりたいこと

アカウントとリージョンをまたいだS3のファイル転送をやってみます。

  • 転送元(SRC)
    • アカウントAのap-northeast-1のバケット: xxxx-dev-datasource
  • 転送先(DST)
    • アカウントBのus-east-1のバケット: cm-hirano-datasync-receive

「転送元」「転送先」と書くと結構見間違えるので、 以降は「SRC」(source)と「DST」(destination)と記載します。

やってみた

このドキュメントページに、アカウント跨ぎでのやり方が書かれています。

チュートリアル:Amazon S3 から Amazon S3 へデータを転送するAWS アカウント

(タイトルからして機械翻訳の日本語訳がちょっと変なので英語版を見た方が賢明です) クロスリージョンの場合にも対応しているので、基本的にはこれに従って進めていけばOKです!

前提として、SRC・DSTともにバケットは作成済みで、 SRCにはいくつかのファイルが配置されているとします。

SRC側にサービスロールを作る

SRC側にDataSyncのサービスロールを作っていきます。 サービスロールとは、DataSyncというサービスが何かを行うときの権限を規定するものです。 バッチジョブなど人間の手を離れて実行されるようなサービスには必ずサービスロールをアタッチして、権限を決めてあげる必要があります。

手順は基本的に先述のドキュメント通りなので、 要点だけを記載していきます。

サービスの種類を選択する画面で「DataSync」と入れると 「DataSync - S3 Location」というものも候補として出てきますが、 ここでは「DataSync」の方を選びます。

そのあとについては特に変更すべき点はありません。 このまま先に進めばIAMロール作成は完了です。

サービスロールにアタッチするIAMポリシーを作成する

サービスロールにIAMポリシーを追加します。 インラインポリシーの追加でドキュメントにあるJSONをちょっと直してあげればOKです。

(既にいくつかポリシー追加済みの画像になっていますが、追加前の状況だと思ってください)

ポリシーの内容はこんな感じです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::cm-hirano-datasync-receive"
        },
        {
            "Action": [
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:ListMultipartUploadParts",
                "s3:PutObject",
                "s3:GetObjectTagging",
                "s3:PutObjectTagging"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::cm-hirano-datasync-receive/*"
        }
    ]
}

どちらも、ResourceとしてDSTのバケットを指定して、 サービスロールがそのバケットにアクセスできるような権限を設定していることがわかります。

サービスロールは、このように「やってもいいこと」を明示的に指定してあげないとほとんど何もできないようになっています。 これは先ほども書いたように、サービスロールは人間の手を離れたところで勝手に実行される処理の権限を決定しているので、 きちんと許可されたことしかできないようになっていると理解するのが良いと思います。

DSTバケットのACL無効化設定(不要だった)

DSTバケットに対してACLを無効化をします。

ただ私が見た環境では最初から無効化されていました。 こちらが推奨とも書いてありますし、このまま触る必要はありません。

DSTバケットのバケットポリシー変更

次にDSTバケットのバケットポリシーを設定していきます。

DSTバケットは、DataSyncによってファイルを書き込まれる側ですので、 DataSyncのサービスロールからの書き込み設定を受け入れるようなバケットポリシーを書く必要がある、 という概念が核となります。

実際のバケットポリシーはこんな感じになりました。

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "DataSyncCreateS3LocationAndTaskAccess",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/cm-hirano-datasync-service-role"
            },
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads",
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:ListMultipartUploadParts",
                "s3:PutObject",
                "s3:GetObjectTagging",
                "s3:PutObjectTagging"
            ],
            "Resource": [
                "arn:aws:s3:::cm-hirano-datasync-receive",
                "arn:aws:s3:::cm-hirano-datasync-receive/*"
            ]
        },
        {
            "Sid": "DataSyncCreateS3Location",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/cm-hirano.shigetoshi"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::cm-hirano-datasync-receive"
        }
    ]
}

これもほぼドキュメントの通りです。 ただここで一点気をつけなければならないのが、後半のDataSyncCreateS3LocationのPrincipalに設定するのは SRC側アカウントの作業者のIAMユーザまたはIAMロールであるという点です。

この設定は、DataSyncがデータをコピーする時の話ではなく、 DataSyncが正しく動作するために必要なS3ロケーションというものを作ることを許可するというものです。 S3ロケーションの作成はDataSyncというサービスが行うのではなく、 準備をする作業者が行うので、ここで作業者のIAMユーザ/ロールからのアクセスを受け付けるようにします。

この部分はDataSyncの本質ではなく、あくまでもS3ロケーションを作成する瞬間にのみ必要な権限だと考えられます。 よってS3ロケーションの作成さえできてしまえば、おそらくこの許可を消しても問題なくDataSyncの処理は動作するかと思います(未検証)。 具体的な作業者のIAMユーザ/ロールをバケットポリシーに書いてしまうことは一般論として推奨されないので、 長期に渡ってDataSyncを使っていくのであればこの部分はもう少し良い形で実現できる方法を模索した方が良さそうです。

S3ロケーションを作る

次にS3ロケーションを作成していきます。

S3ロケーションは2つ作成する必要があります。 ドキュメントにはDST側のロケーション作成しか言及されていないように見えるのですが、 SRC側のロケーションも必要となります。

SRC側

SRC側のアカウントに作っていきます。 こちらはマネジメントコンソールから簡単に作ることができます。 DataSyncの画面にいき、「ロケーション」->「ロケーションを作成する」と進みます。

中身はこんな感じです。

アクセスするロールは、最初に作ったDataSyncのサービスロースとなります。 このサービスロールがこのロケーションを通じてS3にアクセスする、と考えれば良さそうです。

ちなみに今回の作業の中で、SRCのバケット名を指定する必要があるのはここだけです。

DST側

DST側のロケーションは、マネジメントコンソールでは作ることはできません。 クロスアカウントのロケーション作成はAWS CUIを使う必要があると書かれています。

SRC側でのアカウントで、先ほどのバケットポリシーでS3ロケーションの作成を許可したユーザが実行する必要があります。

# SRC側のアカウントで実行
$ aws datasync create-location-s3 --s3-bucket-arn arn:aws:s3:::cm-hirano-datasync-receive --s3-config '{"BucketAccessRoleArn":"arn:aws:iam::123456789012:role/cm-hirano-datasync-service-role"}' --region us-east-1
{
    "LocationArn": "arn:aws:datasync:us-east-1:123456789012:location/loc-039437ec4a194824c"
}

今回はSRCとDSTが別リージョンになっていますので、--regionの指定も必要です。 ここで作成されるS3ロケーションは、SRC側アカウントのus-east-1リージョン(DSTバケットがあるリージョン)に配置されます。 DST側のロケーションなのですが、作られるのはSRC側のアカウント内になるようです。 この辺はなんかちょっと複雑ですね。

なお、先ほどのバケットポリシーでS3ロケーション作成の許可をきちんと設定していない場合、 以下のようなエラーが発生します。

$ aws datasync create-location-s3 --s3-bucket-arn arn:aws:s3:::cm-hirano-datasync-receive --s3-config '{"BucketAccessRoleArn":"arn:aws:iam::123456789012:role/cm-hirano-datasync-service-role"}' --region us-east-1
An error occurred (InvalidRequestException) when calling the CreateLocationS3 operation: DataSync location access test failed: could not perform s3:HeadBucket on bucket cm-hirano-datasync-receive. Access denied. Ensure bucket access role has s3:ListBucket permission.

私は最初、S3ロケーション作成を許可するPrincipalとしてDataSyncのサービスロールを指定してしまっていて、 このエラーが出てしまいました...。

タスクを作る

最後にタスクというものを作ります。 タスクとは、どこからどこにデータを転送するのかを指定しておく注文書のようなものです。 タスクを一度作成したら、自分の好きなタイミングでそれを実行させたり、 または日次などのタイミングで自動実行させたりなどもできます。

タスクは転送先のリージョンに作る必要があると記載されていますので今回はus-east-1に作ります。 ただし、アカウントはSRC側のアカウントとなりますので間違えないようにしましょう。

タスクを作成する際にはSRC側のロケーション、DST側のロケーションの順でロケーションを指定します。 どちらも既に作成済みなので、「既存のロケーション」から適切に選べばOKです。

ロケーションの選択が終わるとタスクの細かい設定画面が出てきます。 今回は特に設定をいじりませんので、そのままで進みたいと思います。 私の場合はCloudWatch LogグループとしてDataSyncのロググループがなかったので「自動生成」を押してから次へ進みました。

これでめでたくタスク作成完了です!!!(?)

実は足りなかった設定

実はここまでの設定ではタスクが作成できません。 タスク作成を完了させようとするとこんなエラーがでました。

どうやらDataSyncのサービスロールがSRCバケットのオブジェクトにアクセスができないようです。 そういえばこれを許可するような設定はした覚えがありません。 サービスロールとSRCバケットは同一アカウントにあるため、 基本的にはバケットポリシーでの明示的なアクセス許可は不要です。 サービスロールがSRCバケットにアクセスすることを単に自主規制しているような状態ですので、 サービスロールに当該バケットへのアクセス許可をしてあげれば良いです。

今回は検証だったこともあり、 私はAmazonS3ReadOnlyAccessをつけてしまいました。 実際に運用する場合はバケットを限定してアクセスを許可した方が良いでしょう。

今度こそめでたくタスクが作成できました!!

タスクを実行してみる

「デフォルトから開始する」でデータ転送処理を開始します。

処理が開始されると「処理中」との表示になります。 今回テストした環境では転送対象のファイルは2つだけだったのですが、 処理完了まで体感で数分くらいかかりました。

DataSyncは大量のデータを転送するのが主な用途ですから、 それを効率的に転送するために、最初に多少のオーバーヘッドがあることは当然といえます。

無事ファイルが転送されていることも確認できました!!

まとめ

DataSyncを使って、クロスアカウントかつクロスリージョンのS3間ファイル転送を行ってみました! ほぼドキュメント通りではありますが、 実際にやってみるとつまずいた部分もあったので、 同じことをやろうとしている方の参考になれば幸いです!