【ハンズオン】「Amazon S3 マルチリージョンアクセスポイント(MRAP)の使用を開始する」をやってみる

# 1. はじめに
- 業務で以下の要件あり。
    - 自システムはAWS上にマルチリージョン(東阪)で構築済。
    - 対向システムから自システムのS3バケットにファイルを送信してほしい。
    - 同じファイルを東阪両方のS3バケットに保存したい。
- 上記の要件に対して、以下の理由から、S3マルチリージョンアクセスポイント(Multi Region Access Point, 以下MRAP)を使用することとした。
    - 対向システムに、東阪2つのS3バケットにそれぞれ送ってもらうことは困難（同じ処理を2回やってくださいは頼みづらいし、対向側の不備で東阪に別のファイルが来たり、片方だけ来たりしてもよくない）。
    - 対向システムには東京に送ってもらい、その後自システム内でクロスリージョンレプリケーションする方法もあるが、その場合東京のS3に障害があると受信できない。
    - 予め東京と大阪のS3バケットでMRAPを構成し、対向システムにはMRAPのエンドポイントを指定してファイル送信してもらうことで、片方のリージョンが障害中でも、どちらかのリージョンで受信することができる。
- ちょうどAWS公式のMRAPのハンズオンがあったので、一通り実施し、気になる点を追加で確認する。

# 2. ハンズオン概要
## コンテンツURL
- [Amazon S3 マルチリージョンアクセスポイントの使用を開始する](https://aws.amazon.com/jp/getting-started/hands-on/getting-started-with-amazon-s3-multi-region-access-points/?ref=docs_gateway/amazons3/MultiRegionAccessPoints.html)（AWS公式）

## 構成図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/bc1ed611-7dcf-b5ee-819d-3dd61f79e073.png)


- 東京/大阪にS3バケットを作成し、2つのS3バケットでMRAPを構成する。
- 以下の4経路でのアクセスを行う。
    - ①東京のCloudShell(インターネット経由)
    - ②大阪のCloudShell(インターネット経由)
    - ③東京の同一アカウント内のEC2インスタンス(VPCエンドポイント経由)
    - ④東京の別アカウントのEC2インスタンス(インターネット経由)

## ハンズオンでやること
- S3バケットを東阪にそれぞれ作成し、MRAPを構成する。
- S3バケット間のレプリケーション、フェイルオーバーの機能を確認する。
- クライアントから、MRAPエイリアスを指定してS3バケットにアクセスする。
- クライアントからVPCエンドポイント経由でアクセスする。
- 使用状況をモニタリングする。

## 自分で追加確認
- MRAPの設定で、2つのS3バケットがActive/Activeの場合、クライアントに近いほうのS3バケットにファイル送信されること。
- IAMポリシー/ロールを適切に設定し、別アカウントのEC2インスタンスからもMRAP経由でファイル送信できること。

## 個人的所感
- 難易度：　★☆☆☆☆
    - 所要時間：　70～90分と書いてあるが、実際には追加確認も含め5時間くらいかかった。
    - 書いてある内容自体はほぼ躓くことなく進めることができた。

- 満足度：　★★★★☆
    - MRAPを使うのは初めてだったが、主要な機能がハンズオンに網羅されており、これをやってMRAPを一通り語れるレベルになった気がする。

# 3. 手順
:::note
基本的に手順書通りに実施して、ハマりポイントや追加で自分で確認したことを中心に記載する。
:::

## 1 Create Amazon S3 Buckets
- 東京リージョンと大阪リージョンにそれぞれS3バケットを作成する。
- 手順書通りの設定で、S3バケットを作成する。
    - Block Public Access：有効
    - Bucket Versioning：無効
    - Encryption：SSE-S3

## 2 Create S3 Multi-Region Access Point
- 手順書通りの設定で、MRAPを作成する。
    - マルチリージョンアクセスポイント名：mksamba-qiita
    - バケット：先に作成した2つのバケット(東京1つ、大阪1つ)
    - パブリックアクセスを全てブロック：有効
- MRAPの作成が成功すると、エイリアス名（「x54biui6jbhs8.mrap」のようなフォーマットのもの）が生成される。

### 【追加】初期設定時点での動作確認
- 手順書にはないが、初期設定時点でMRAPのエイリアスに対してアクセスして、ファイル送信ができることを確認する。
- 東京リージョンのCloudShellを使用し、以下コマンドを実行する。(CloudShell実行ユーザにはS3FullAccessの権限がある前提)

```
$ aws s3api put-object --bucket arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap --key file1.txt --body file1.txt
# --bucketで、バケット名の代わりにMRAPのARNを指定
# --key でS3バケットに保存する際のオブジェクト名を指定
# --body でCloudShell内のローカルのファイル名を指定
```
- 東京リージョンのS3バケットにfile1.txtが送信される。(まだレプリケーション設定はしていないので、大阪リージョンには保存されない)
- 同様に大阪リージョンのCloudShellを使用し、同じコマンドでfile2.txtを送信すると、大阪リージョンのS3バケットのみに保存される。（初期設定時は2つのS3バケットがActive/Activeになっていることから、クライアントに近いほうのS3バケットが選択され、そちらのバケットのみにファイルが保存される）

## 3 Configure S3 Replication
- 手順書通りの設定で、S3バケット間の双方向レプリケーションを設定する。
    - Templete：Replicate objects among all specified bucktes (双方向レプリケーション)
    - Enable Bucket Versioning: YES(レプリケーションを使う場合、強制的に有効化させられる)
    - Replication Scope：Apply to all objects in the bucket (バケット内のオブジェクト全てが対象)
    - Replication Time Control(RTC)：有効
    - Replication metrics and notifications：有効（上のRTCを有効にすると自動で有効になる）
    - Delete marker replication：有効
    - Replica modification sync：有効

- バージョンニングが強制有効化となる画面は以下。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/d37f33cf-f1c1-dbf7-8e7a-dd6b4d0ec1c5.png)

- デフォルトだと「Delete marker replication」は無効になっている。無効の場合、レプリ元バケットでオブジェクトを削除してもレプリ先バケットのオブジェクトは削除されない。（有効にすると、元バケットでオブジェクトを削除すると先バケットでも削除される。）

- 設定後はレプリケーションが双方向矢印で表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/764e80cc-a253-f93e-07f8-7a3f9cfc27b6.png)

- 設定完了後、再度東京のCloudShellにて、MRAPのエンドポイントを用いてファイル送信を行うと、結果として東京/大阪の両方にファイルが作成される。(まず東京側にファイルが作成され、それが大阪へレプリケーションされる。今回は1分以内くらいに大阪にレプリケーションされていた)

## 4 Failover Configuration
- 手順書通り以下の設定を行う。
    - 初期状態では2つのバケットがActive/Activeになっている。この時、MRAPを指定してファイル送信した場合、クライアントに近いほうのバケットにファイルが保存される。（双方向レプリケーションも有効のため、ファイルはもう一方のバケットにレプリケーションされる）
    - 片方のバケット（今回は大阪側）をPassiveに変更する。そうするとMRAPを指定してファイル送信した場合、通常時はActiveな側のバケットのみが選択される。（大阪のCloudShellからファイル送信しても東京のバケットに保存される。その後レプリケーションは行われる。）
    - フェイルオーバーを行うと、ActiveとPassiveが反転する。
- 手順にはないが、今回は自分の要件に併せて、次のセクションに進む前にActive/Activeの設定に戻した。

## 5 IAM Permissions

- この項ではアクセス権（バケットポリシー）の設定を行うが、今回はIAMロールを用いた検証を行うため、2つのS3バケット、及びMRAPに対してのバケットポリシーは未設定のままとした。
- （今回は設定しなかったが）東阪それぞれのS3バケットへのバケットポリシーの設定例は以下（設定の意味としては、MRAPを経由してのアクセスを許可する）。

```
{
    "Version": "2012-10-17",
    "Statement" : [
    {
        "Effect": "Allow",
        "Principal" : { "AWS": "*" },
        "Action" : "*",
        "Resource" : [ 
            "<BucketARN>", 
            "<BucketARN>/*"
        ],
        "Condition": {
            "StringEquals" : { "s3:DataAccessPointArn" : "<MultiRegionAccessPoint_ARN>"}
        }
    }]
}
```
- （今回は設定しなかったが）MRAPに対してもバケットポリシーのようなものを設定することができる。（設定の意味としては特定のIAMユーザに対して、MRAPへのListBucketとGetObjectのみを許可）

```
{
  "Version":"2012-10-17",
  "Statement": [
    {
       "Effect": "Allow",
       "Principal": {
          "AWS":"arn:aws:iam::xxxxxxxxxxxx:user/IAMuserid"
       },
       "Action":[
          "s3:ListBucket",
          "s3:GetObject"
       ],
       "Resource":[ 
          "MultiRegionAccessPoint_ARN",
          "MultiRegionAccessPoint_ARN/object/*"
       ]
     }
  ]
}
```

## 6 Use Your Multi-Region Access Point
- 手順書通りにS3コマンドを実施し、MRAP経由での操作ができることを確認する。AWS CLIの実行環境は東京のCloudShellとする。
```
# CLIバージョン確認
$ aws --version
aws-cli/2.17.14 Python/3.11.9 Linux/6.1.96-102.177.amzn2023.x86_64 exec-env/CloudShell exe/x86_64.amzn.2023

# オブジェクトリスト取得
$ aws s3 ls arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap
2024-07-24 08:09:50          2 file1.txt
2024-07-24 09:18:10          2 file3.txt

# S3バケットのファイルをローカルにダウンロード
$ aws s3 cp s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file3.txt file3.txt
download: s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file3.txt to ./file3.txt

# ローカルのファイルをS3バケットへアップロード
# 手順書だと送信先の設定が「ARN」になっているが、「s3://ARN」とする必要がある。
# 「s3://」を付けないと「Error: Invalid argument type」になる。
$ aws s3 cp file4.txt s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file4.txt

# メタデータの取得
$ aws s3api head-object --bucket arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap --key file4.txt
{
    "AcceptRanges": "bytes",
    "LastModified": "2024-07-24T14:07:48+00:00",
    "ContentLength": 0,
    "ETag": "\"d41d8cd98f55b204e9811118ecf8427e\"",
    "VersionId": "WquKXXnZYuLLRaaa.aqBCHW.ntoOgHkQ",
    "ContentType": "text/plain",
    "ServerSideEncryption": "AES256",
    "Metadata": {},
    "ReplicationStatus": "COMPLETED"
}
```

## 7 VPC Endpoints
- 手順書では一部手順が省略されているが、VPC/EC2インスタンスを準備し、VPCにVPCエンドポイントを作成し、VPCエンドポイント経由でMRAPへアクセスする。
- VPC、プライベートサブネットを作成し、プライベートサブネットにEC2インスタンスを作成する。
- EC2インスタンスにはS3FullAccessの権限を持つIAMロールを付与する。
- 手順書の通り、「com.amazonaws.s3-global.accesspoint」へのVPCエンドポイント（インターフェースエンドポイント）を作成する。
- EC2インスタンスから、VPCエンドポイント経由でMRAPにアクセスできることを確認する。

```
#インターネット経由でアクセス
#プライベートサブネットであり、EIPもNatGatewayもないためアクセス不可
$ aws s3 ls arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap
⇒応答なし

#VPCエンドポイント経由でアクセス
$ aws s3 ls arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap --endpoint-url https:/accesspoint.vpce-xxxxxxxxxxxxxxxx-xxxxxxxx.accesspoint.s3-global.ap-northeast-1.vpce.amazonaws.com
2024-07-24 08:09:50          2 file1.txt
2024-07-24 09:18:10          2 file3.txt
2024-07-24 14:07:48          0 file4.txt
```

- MRAPに対するVPCエンドポイント経由でのアクセスの手順はこちら。
    - [S3 インターフェイスエンドポイントからバケット、アクセスポイント、および Amazon S3 コントロール API オペレーションにアクセスする](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/privatelink-interface-endpoints.html#accessing-bucket-and-aps-from-interface-endpoints)

## 8 Monitor S3 Replication and Requests

- 手順書通りにスクリプトを実行した後、メトリクスを確認する。
- 東京のCloudShellにて、20秒おきに1MBのファイルをアップロード/ダウンロードするスクリプト（手順書記載）を実行する。
- MRAPの管理画面の、「レプリケーションメトリクス」にて、レプリケーションの処理時間を確認する。毎回30秒以内にはレプリケーションできている様子。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/17eedbd5-98c2-ec30-b83d-4d4a4b94d5ab.png)

- 手順書通り、東京/大阪のS3バケットにて、「リクエストメトリクス」の設定の追加を行う。S3バケットに対するリクエスト数の確認ができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/ce729b2f-30f4-615e-cdf2-96a2ff4dc326.png)

## 【追加】別AWSアカウントからのMRAPへのアクセス確認
- 手順書にはないが、別AWSアカウントからMRAPにアクセスできることを追加確認する。
- そのためには様々なやり方があるが、今回はアカウント1(S3バケット所有アカウント)にてIAMポリシー/ロールを作成し、そのロールをアカウント2(ファイル送信元アカウント)に渡す方式で検証した。

- IAMポリシーを作成する。
    - MRAP及び東阪のバケットに対するアクセス権を付与する必要がある。
    - 東阪のバケットに対し5項記載のバケットポリシー（MRAP経由のアクセスを許可する設定）を追加している場合は、本IAMポリシーではMRAPへのアクセスのみを許可すればよい。

``` mksamba-mrap-qiita-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::[Tokyoのバケット名]",
                "arn:aws:s3:::[Tokyoのバケット名]/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::[Osakaのバケット名]",
                "arn:aws:s3:::[Osakaのバケット名]/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap"
                "arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/object/*"
            ]
        }
    ]
}
```

- 上記のIAMポリシーを付けたIAMロール(mksamba-mrap-qiita-role)を作成する。その信頼ポリシー(以下)にはファイル送信元のAWSアカウント(zzzzzzzzzzzz)を指定する。

``` mksamba-mrap-trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::zzzzzzzzzzzz:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {}
        }
    ]
}
```

- アカウント2内のアクセス元となるEC2インスタンス(条件としてインターネット接続可、かつAssumeRoleが可能な権限あり)にて、上記IAMロールを用いてMRAPにアクセスする。
    - 参考：AssumeRoleのやり方：[「シェルスクリプトでAssumeRoleするときの工夫」](https://cross-black777.hatenablog.com/entry/2017/12/18/225100)

```
# AssumeRoleをせずにアクセスする場合
$ aws s3 cp file4.txt s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file4.txt
upload failed: ./file4.txt to s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file4.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

# AssumeRoleを行ってからアクセスする場合
$ result=`aws sts assume-role --role-arn "arn:aws:iam::xxxxxxxxxxxx:role/mksamba-mrap-qiita-role" --role-session-name AWSCLI-Session`
$ access_key_id=`echo ${result} | jq -r '.Credentials.AccessKeyId'`
$ secret_access_key=`echo ${result} | jq -r '.Credentials.SecretAccessKey'`
$ session_token=`echo ${result} | jq -r '.Credentials.SessionToken'`
$ export AWS_ACCESS_KEY_ID=${access_key_id}
$ export AWS_SECRET_ACCESS_KEY=${secret_access_key}
$ export AWS_SESSION_TOKEN=${session_token}
# aws s3 cp file4.txt s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file4.txt
upload: ./file4.txt to s3://arn:aws:s3::xxxxxxxxxxxx:accesspoint/x54biui6jbhs8.mrap/file4.txt
```

# 4. まとめ
- ちょうどよいハンズオンがあってよかった。バケットポリシーやIAMロールによるアクセス制限について、別途もう少し深堀したい。