クロスアカウントで s3 バケットを非同期コピーするコンテナを定時実行する

# 1. はじめに
* こんな感じで S3 バケットのバックアップを定期的に取りたい。 
 * バックアップ元の S3 バケットとバックアップ先の S3 バケットを異なるAWSアカウントにしたい。
 * バックアップ元の S3 でオブジェクトファイルが削除されても、バックアップ先のオブジェクトファイルでは削除したくない。 
 * バックアップバッチ用のEC2（常時起動）ではなく、コンテナ（バックアップ処理時間だけ起動）で実行したい。

* [S3 の レプリケーション機能](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/replication.html)で実現できるかどうか少し調べてみたけど、次の理由で今回は見送りした。
 * バックアップ元の S3 バケットにはすでにオブジェクトが入っている。
     * [既存のオブジェクトをレプリケーションする場合は、AWS サポートへ対応を依頼しないといけない。](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/replication-what-is-isnot-replicated.html)
     * レプリケーションがいつ開始されるのか不明（参考:[いつの間に！？既存オブジェクトを S3 レプリケーション出来るようになっていた！](https://dev.classmethod.jp/articles/s3-replication-for-existing-objects/)）。

# 2. 構成図
![20200409-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/d6e996ed-3857-d137-92d4-9ad9bb79ac07.png)


# 3. 概要
## 3.1. バックアップ元側の AWS アカウント
* AWSアカウント
 * example-1

* アカウントID
 * 111111111111

* バックアップ元S3バケット
 * バケット名: original-111111111111
 * 「パブリックアクセスを全てブロックする」設定をチェックする。
 * バージョニング設定は任意（停止でもよい）。

* Role
 * Containerに割り当てる。
 * バックアップ元S3バケット original-111111111111 に対して、 「s3:ListBucket」 と 「s3:GetObject」 を許可する。
 * バックアック先S3バケット backup-222222222222 に対して、 「s3:ListBucket」 と 「s3:PutObject」 を許可する。
 
* ECR
 * リポジトリ名: infra-batch-repository
 * 作業PCで作成したバックアップバッチ用のコンテナイメージを登録する。

* ECS
 * ECSクラスター名: infra-batch
 * 起動タイプ: Fargate

* Fargate, Container
 * CloudWatchイベントのトリガで ECR からイメージを取得して、パブリックサブネット内にコンテナを作成して、s3 sync を実行する。
 * s3 sync を実行する Container は外部ネットワークへ出ていける環境で起動する。
     * パブリックサブネット
     * Security group の アウトバウンド はすべてを許可する
     * パブリック IP の自動割り当ては 「ENABLED」 にする

## 3.2. バックアップ先側の AWS アカウント
* AWSアカウント名
 * example-2

* アカウントID
 * 222222222222

* バックアップ先S3バケット
 * バケット名: backup-222222222222
 * 「パブリックアクセスを全てブロックする」設定をチェックする
 * バージョニング設定は任意（停止でもよい）
 * バケットポリシーで、AWSアカウント example-1(111111111111) の Role にアクセス権限を付与する。
 * バックアップオブジェクトは S3 スタンダード-IA クラスに保存する（s3 sycn 実行時に 「--storage-class STANDARD_IA」 を指定）。

# 4. 設定


## 4.1. Container に割り当てる Role を作成 (バックアップ元側の AWS アカウントでの作業)
* IAMポリシー作成
 * 名前: s3-sync-backup

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::original-111111111111",
                "arn:aws:s3:::original-111111111111/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::backup-222222222222",
                "arn:aws:s3:::backup-222222222222/*"
            ]
        }
    ]
}
```

* IAMロール作成
 * 名前: s3-sync-backup
 * 信頼されたエンティティ: ecs-tasks.amazonaws.com
 * アタッチするポリシー: s3-sync-backup

## 4.2. バックアップ先 S3 バケットの バケットポリシー (バックアップ先側の AWS アカウントでの作業)
* AWSアカウント example-2 (222222222222) の S3 バケット backup-222222222222 への 「s3:ListBucket」 と 「s3:PutObject」 の許可権限を、AWSアカウント example-1 (111111111111) の Role s3-sync-backup に付与する。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/s3-sync-backup"
            },
            "Action": [
                "s3:ListBucket",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::backup-222222222222",
                "arn:aws:s3:::backup-222222222222/*"
            ]
        }
    ]
}
```

## 4.3. ECR リポジトリ作成 (バックアップ元側の AWS アカウントでの作業)
* 詳しい設定方法は割愛。
* ここでは、「infra-batch-repository」というリポジトリを作成して利用する。


## 4.4. コンテナイメージを作成して、ECRへプッシュする （作業PCでの実施）
* Dockerfile
 * 最近リリースされた 「[AWS CLI v2 Docker image](https://aws.amazon.com/jp/blogs/developer/aws-cli-v2-docker-image/)」 を使う。 

```dockerfile
FROM amazon/aws-cli:latest
ENTRYPOINT ["aws"]
CMD ["s3","sync","--dryrun","--storage-class STANDARD_IA","s3://original-111111111111","s3://backup-222222222222"]
```
* タグを付けてビルドする

```
$ docker build -t infra-batch-repository:s3-sync-backup .
```

* ECRにプッシュするためのタグを付ける

```
$ docker tag infra-batch-repository:s3-sync-backup 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/infra-batch-repository:s3-sync-backup
```

* AWS ECR レジストリ用の docker login 認証コマンド文字列(12時間有効な認証トークン)を取得する

```
$ aws ecr get-login --no-include-email
$ docker login -u AWS -p 認証トークン
```

* イメージを ECR へ プッシュする

```
$ docker push 111111111111.dkr.ecr.ap-northeast-1.amazonaws.com/infra-batch-repository:s3-sync-backup
```


## 4.5. ECSクラスター作成 (バックアップ元側の AWS アカウントでの作業)
* 「AWS Fargate を使用」 を利用する。
* クラスター名
 * infra-batch


## 4.6. タスク定義を作成
![20200409-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/522ebedf-cc17-8d96-d4b5-1f98c95be0f4.png)
↓
![20200409-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/1c3f8e39-0885-68a9-64fa-33856f34f489.png)
↓
![20200409-0４.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/5d18922a-9b7c-9268-6d29-e0e9304a9b4d.png)
↓
![20200409-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/ca060949-dcaf-22e3-c5a0-955ca920dfaa.png)
↓
![20200409-06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/cb85a763-88ca-3595-7f1c-109b4bde8e7c.png)
↓
![20200409-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/ca7aae1e-a608-119e-c75b-3d528de8aa64.png)
↓
確認
![20200409-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/8157b24a-c0b4-6eb1-ce2b-1fe3dfa2efcf.png)

## 4.7. タスクスケジュールを作成
![20200408-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/7ee4a432-5f81-537f-7983-85d04a59876e.png)
↓
![20200409-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/98149858-8727-e792-0b4c-ecbb718d1c58.png)
![20200409-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/4279c16e-c562-78bb-af14-43becf0fd825.png)
![20200409-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/41ddc2a3-61c0-fd7d-f0a2-1a191f446f0d.png)
Cron式の設定は UTC なので、 cron(0 19 * * ? *) は日本時間で 毎日04:00
サブネットは、任意のパブリックサブネットを指定する
セキュリティグループは、アウトバウンドをすべて許可の設定になっている任意のものを指定する。
パブリック IP の自動割り当ては「ENABLED」にする。
↓
確認
![20200409-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/f7af5121-2534-19c3-b213-b4fefc3b227d.png)
※CloudWatch のイベントルールや、Amazon EventBridge のイベントルールにも設定が反映される。

# 5. タスク実行のログ
* 実行ログは CloudWatch のロググループの /ecs/s3-sync-backup に出力される
 * ログの例<br />
![20200409-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/2b24f54d-5469-a548-bafa-d3eede7dcc91.png)


# 6. 注意点
* s3 sync の動作確認は、--dryrun オプションを付けて実行し、実行ログから挙動が正しいか確認する。
 * [--dryrun](https://docs.aws.amazon.com/cli/latest/reference/s3/sync.html): Displays the operations that would be performed using the specified command without actually running them.

* バックアップ元のS3バケットのオブジェクト数やサイズが多い場合は、最初の s3 sync (フルサイズバックアップ）は作業PC上から s3 sync を実行した方がよい。

 ```
$ s3 sync --dryrun --storage-class STANDARD_IA s3://original-111111111111 s3://backup-222222222222
<ログ確認>
$ s3 sync --storage-class STANDARD_IA s3://original-111111111111 s3://backup-222222222222
```