michimani.net

[レポート] JAWS-UG CLI 専門支部 #173R S3基礎 (レプリケーション) に参加しました #jawsug_cli

2020-12-11

JAWS-UG CLI専門支部 #173R S3基礎 (レプリケーション) に参加したので、そのレポートです。と言っても耳だけ参加だったので、 S3 レプリケーションについての概要まとめとハンズオン + α の内容になってます。(いつもそんな感じか)

connpass のイベントページはこちら。

これまでの CLI 専門支部参加レポートはこちら。

目次

S3 のレプリケーションとは

そもそも S3 のレプリケーションの機能について理解が乏しかったので、公式ドキュメントを元に概要をまとめてみます。

概要

レプリケーションの利用用途

要件

その他、詳細な仕様については公式ドキュメントをどうぞ。

レプリケーション - Amazon Simple Storage Service

ハンズオン

今回のハンズオンでは、同一アカウントの同一リージョン内にあるバケット同士でレプリケーションをするという内容でしたが、ここでは別リージョンのバケットへのレプリケーション (CRR) としてやってみます。

なお、手元で実行する際は macOS 環境で、 AWS CLI のバージョンは 2.1.9 です。

aws --version
aws-cli/2.1.9 Python/3.7.4 Darwin/19.6.0 exe/x86_64 prompt/off

レプリケーション元とレプリケーション先の S3 バケット作成

レプリケーション元を 東京 ap-northeast-1 に、レプリケーション先を オレゴン us-west-2 に作成します。まずは必要な情報を変数に設定します。

$ AWS_ACCOUNT_ID=$( \
aws sts get-caller-identity \
--query 'Account' \
--output text )
$ S3_SRC_REGION="ap-northeast-1"
$ S3_DST_REGION="us-west-2"
$ S3_SRC_BUCKET_NAME="handson-cli-s3-replication-replication-source-${AWS_ACCOUNT_ID}"
$ S3_DST_BUCKET_NAME="handson-cli-s3-replication-replication-destination-${AWS_ACCOUNT_ID}"

それぞれのバケットを作成し、バージョニングを有効にします。

$ aws s3api create-bucket \
--bucket ${S3_SRC_BUCKET_NAME} \
--create-bucket-configuration "LocationConstraint=${S3_SRC_REGION}" \
--region ${S3_SRC_REGION} \
&& aws s3api put-bucket-versioning \
--bucket ${S3_SRC_BUCKET_NAME} \
--versioning-configuration Status=Enabled \
--region ${S3_SRC_REGION}

$ aws s3api create-bucket \
--bucket ${S3_DST_BUCKET_NAME} \
--create-bucket-configuration "LocationConstraint=${S3_DST_REGION}" \
--region ${S3_DST_REGION} \
&& aws s3api put-bucket-versioning \
--bucket ${S3_DST_BUCKET_NAME} \
--versioning-configuration Status=Enabled \
--region ${S3_DST_REGION}

それぞれのバケットの存在と、リージョン、バージョニングのステータスを確認します。

$ aws s3api get-bucket-location \
--bucket ${S3_SRC_BUCKET_NAME} \
--output text \
&& aws s3api get-bucket-versioning \
--bucket ${S3_SRC_BUCKET_NAME} \
--query "Status" \
--output text

ap-northeast-1
Enabled

$ aws s3api get-bucket-location \
--bucket ${S3_DST_BUCKET_NAME} \
--output text \
&& aws s3api get-bucket-versioning \
--bucket ${S3_DST_BUCKET_NAME} \
--query "Status" \
--output text

us-west-2
Enabled

ちなみに、 S3 バケットに対するコマンドには下記のようなものがあります。

$  aws s3api help | grep bucket
       o create-bucket
       o delete-bucket
       o delete-bucket-analytics-configuration
       o delete-bucket-cors
       o delete-bucket-encryption
       o delete-bucket-intelligent-tiering-configuration
       o delete-bucket-inventory-configuration
       o delete-bucket-lifecycle
       o delete-bucket-metrics-configuration
       o delete-bucket-ownership-controls
       o delete-bucket-policy
       o delete-bucket-replication
       o delete-bucket-tagging
       o delete-bucket-website
       o get-bucket-accelerate-configuration
       o get-bucket-acl
       o get-bucket-analytics-configuration
       o get-bucket-cors
       o get-bucket-encryption
       o get-bucket-intelligent-tiering-configuration
       o get-bucket-inventory-configuration
       o get-bucket-lifecycle-configuration
       o get-bucket-location
       o get-bucket-logging
       o get-bucket-metrics-configuration
       o get-bucket-notification-configuration
       o get-bucket-ownership-controls
       o get-bucket-policy
       o get-bucket-policy-status
       o get-bucket-replication
       o get-bucket-request-payment
       o get-bucket-tagging
       o get-bucket-versioning
       o get-bucket-website
       o head-bucket
       o list-bucket-analytics-configurations
       o list-bucket-intelligent-tiering-configurations
       o list-bucket-inventory-configurations
       o list-bucket-metrics-configurations
       o list-buckets
       o put-bucket-accelerate-configuration
       o put-bucket-acl
       o put-bucket-analytics-configuration
       o put-bucket-cors
       o put-bucket-encryption
       o put-bucket-intelligent-tiering-configuration
       o put-bucket-inventory-configuration
       o put-bucket-lifecycle-configuration
       o put-bucket-logging
       o put-bucket-metrics-configuration
       o put-bucket-notification-configuration
       o put-bucket-ownership-controls
       o put-bucket-policy
       o put-bucket-replication
       o put-bucket-request-payment
       o put-bucket-tagging
       o put-bucket-versioning
       o put-bucket-website

今回のように S3 バケットのリージョンとかバージョニングとかの情報をひとつのコマンドで取得したいと思うのは私だけでしょうか。他のリソースで言う describe-*** コマンドがあってそれで取得できればいいのになと思いました。

レプリケーション設定を追加

レプリケーション設定を追加するには s3api put-bucket-replication コマンドを使いますが、その際にレプリケーションの設定情報を下記のような JSON で指定します。

{
  "Role": "<IAM ロールの ARN>",
  "Rules": [
    {
      "Status": "Enabled",
      "Priority": 1,
      "DeleteMarkerReplication": { "Status": "Disabled" },
      "Filter" : { "Prefix": ""},
      "Destination": {
        "Bucket": "<レプリケーション先の S3 バケットの ARN>"
      }
    }
  ]
}

見てもらうと分かる通り S3 がユーザーに代わってオブジェクトをレプリケートするための IAM ロールが必要になるので、まずはそれを作ります。

IAM ロールの作成

順番としては、IAM ポリシーを作って、 IAM ロールを作ってアタッチする流れです。作成する IAM ロールには S3 が Assume Role できるように信頼ポリシーを付与します。

アタッチするポリシーの作成

下記のようなポリシードキュメントを policy.json として作成しておいて、 iam create-policy コマンドで --policy-document file://policy.json で指定して IAM ポリシーを作成します。(コマンド実行については新しいものではないので省略します)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObjectVersion"
        "s3:GetObjectVersionAcl",
        "s3:GetObjectVersionTagging",
      ],
      "Resource": [
        "arn:aws:s3:::handson-cli-s3-replication-replication-source-************/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetReplicationConfiguration"
      ],
      "Resource": [
        "arn:aws:s3:::handson-cli-s3-replication-replication-source-************"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ReplicateObject",
        "s3:ReplicateDelete",
        "s3:ReplicateTags"
      ],
      "Resource": "arn:aws:s3:::handson-cli-s3-replication-replication-destination-************/*"
    }
  ]
}

ハンズオン資料では下記の公式ドキュメントと異なる設定だったのですが、後述するおかしな現象が発生したため公式ドキュメント通りの内容にしました。

IAM ロールを作成、 IAM ポリシーをアタッチ

IAM ロール作成にあたって、下記のような信頼ポリシーを trust-policy.json として作成し、 iam create-role コマンドで --assume-role-policy-document file://trust-policy.json で指定して IAM ロールを作成します。(コマンド実行については新しいものではないのでry)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "s3.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}

IAM ロールを作成したら、先ほど作成した IAM ポリシーをアタッチします。コマンドは iam attach-role-policy でアタッチします。(コマンド実行についてはry)

レプリケーションの設定を追加

レプリケーションの設定情報も、下記のように事前に JSON ファイルとして作成しておきます。

$ cat << EOF > ./replication-doc.json
{
  "Role": "${S3_REPLICA_ROLE_ARN}",
  "Rules": [
    {
      "Status": "Enabled",
      "Priority": 1,
      "DeleteMarkerReplication": { "Status": "Disabled" },
      "Filter" : { "Prefix": ""},
      "Destination": {
        "Bucket": "arn:aws:s3:::${S3_DST_BUCKET_NAME}"
      }
    }
  ]
}
EOF

レプリケーションを S3 バケットに追加するには s3api put-bucket-replication コマンドを使用します。

$ aws s3api put-bucket-replication \
--bucket ${S3_SRC_BUCKET_NAME} \
--replication-configuration file://replication-doc.json

オブジェクトを Put して確認

適当なファイルを作成して、レプリケーション元の S3 バケットに Put し、レプリケーション先の S3 バケットで確認します。

$ echo "This is a sample file." > ./sample.txt

メタデータなども含めてレプリケートされるとのことなので、色々付与して Put します。

$ aws s3api put-object \
--bucket ${S3_SRC_BUCKET_NAME} \
--key sample.txt \
--body ./sample.txt \
--metadata custom-metadata-1=value1,custom-metadata-2=value2,Custom-Metadata-3=value3 \
--cache-control "public, max-age=1209600" \
--content-type "text/plain8" \
--tagging "TagKey1=TagValue1"

レプリケーションは通常 15 分以内に行われるため、それまではレプリケーション元のオブジェクトの ReplicationStatusPENDING となっています。

$ aws s3api head-object \
--bucket ${S3_SRC_BUCKET_NAME} \
--key sample2.txt
{
    "AcceptRanges": "bytes",
    "LastModified": "2020-12-10T14:26:18+00:00",
    "ContentLength": 26,
    "ETag": "\"6d8b1ff98614bae68f36f5d2986f9e3c\"",
    "VersionId": "L1CTlhLzrDmtq.Aupo_hzVb5uSBXmprQ",
    "CacheControl": "public, max-age=1209600",
    "ContentType": "text/plain8",
    "Metadata": {
        "custom-metadata-1": "value1",
        "custom-metadata-2": "value2",
        "custom-metadata-3": "value3"
    },
    "ReplicationStatus": "PENDING"
}

それぞれのオブジェクトの情報を s3api head-object コマンドで取得して差分を見てみます。

$ aws s3api head-object \
--bucket ${S3_SRC_BUCKET_NAME} \
--key sample.txt \
>| src-object.json \
&& aws s3api head-object \
--bucket ${S3_DST_BUCKET_NAME} \
--key sample.txt \
>| dst-object.json \
&& diff -u src-object.json dst-object.json

--- src-object.json	2020-12-10 23:51:04.000000000 +0900
+++ dst-object.json	2020-12-10 23:51:05.000000000 +0900
@@ -11,5 +11,5 @@
         "custom-metadata-2": "value2",
         "custom-metadata-3": "value3"
     },
-    "ReplicationStatus": "COMPLETED"
+    "ReplicationStatus": "REPLICA"
 }

ReplicationStatus 以外は一致しています。レプリケーション元のオブジェクトの ReplicationStatusCOMPLETED で、レプリケーション先のオブジェクトの ReplicationStatusREPLICA となっています。

知らんかったぞ、ではなく、ハマったぞそれ

この 「知らんかったぞ、それ」 コーナー密かな人気があるとのことなので今回もいくつか個人的に知らなかったことを取り上げたかったのですが、今回はレプリケーションの機能自体ほとんど知らなかったので、代わりにハンズオンでハマったポイントについて書きます。

ReplicationStatus が Failed なのにレプリケートされている

ハンズオンのとおりに進めていって最後にレプリケーション元と先のオブジェクトの情報を比較すると、おかしな現象が起きました。

$ aws s3api head-object \
--bucket ${S3_SRC_BUCKET_NAME} \
--key failed-sample.txt \
>| src-failed-object.json \
&& aws s3api head-object \
--bucket ${S3_DST_BUCKET_NAME} \
--key failed-sample.txt \
>| dst-failed-object.json \
&& diff -u src-failed-object.json dst-failed-object.json
--- src-failed-object.json	2020-12-11 00:04:18.000000000 +0900
+++ dst-failed-object.json	2020-12-11 00:04:19.000000000 +0900
@@ -11,5 +11,5 @@
         "custom-metadata-2": "value2",
         "custom-metadata-3": "value3"
     },
-    "ReplicationStatus": "FAILED"
+    "ReplicationStatus": "REPLICA"
 }

手順の途中にも書きましたが、ハンズオンの手順書と公式ドキュメントでは IAM ポリシーに下記のような差異がありました。

--- policy-handson.json	2020-12-10 23:57:25.000000000 +0900
+++ policy.json	2020-12-10 23:58:45.000000000 +0900
@@ -4,8 +4,9 @@
     {
       "Effect": "Allow",
       "Action": [
-        "s3:GetObjectVersionForReplication",
-        "s3:GetObjectVersionAcl"
+        "s3:GetObjectVersion",
+        "s3:GetObjectVersionAcl",
+        "s3:GetObjectVersionTagging"
       ],
       "Resource": [
         "arn:aws:s3:::handson-cli-s3-replication-replication-source-************/*"
@@ -26,8 +27,7 @@
       "Action": [
         "s3:ReplicateObject",
         "s3:ReplicateDelete",
-        "s3:ReplicateTags",
-        "s3:GetObjectVersionTagging"
+        "s3:ReplicateTags"
       ],
       "Resource": "arn:aws:s3:::handson-cli-s3-replication-replication-destination-************/*"
     }

違いとしては、レプリケーション元の S3 バケットに s3:GetObjectVersionTagging がなかったという点です。これが原因でなぜこのような状況が発生するのかはわからなかったのですが、ちょっとハマったポイントでした。

LT

今回は SAYJOY@gringriffin さんによる LT がありました。

以前に JAWS-UG 千葉支部で開催されたサーバーレスハンズオン (の一部) の内容を AWS CLI で構築した際の振り返りや気付きなどについて話されていました。CLI 専門支部のハンズオン資料を元に資料化をされている途中とのことなので、もしかしたらご自身のブログで公開されるかも?しれません。

(終わってから言うのもあれなんですが) 自分も資料が整えば LT しようかと思っていましたが、結局間に合わずでしたので供養ブログを書きました。

まとめ

JAWS-UG CLI専門支部 #173R S3基礎 (レプリケーション) に参加したので、そのレポートでした。

今回はリアルタイムでは耳だけ参加で、終わってからハンズオンをやってみました。S3 のレプリケーション機能は今まで使ったことがなかったのですが、思っていた以上に簡単であっさりしているなという印象です。異なるリージョン、異なるアカウント、異なるストレージクラスへのレプリケーションも可能ということで、あらゆるシチュエーションに対応できるのでは、と思いました。

年内のハンズオンは今回で最後だったようなのですが、来年以降もスケジュールは用意されているということなので引き続き参加していきたいと思います。もともと今年に入ってからオフラインで参加しようとしていたところで今のような状況になってしまいました。その後、すぐにオンラインでの開催していただけるようになったので、そこからはほぼ毎回参加してきました。おかげさまで抵抗なく、むしろ好んで AWS CLI を使うようになりました。開催していただいている波田野さんには感謝です。

そういえば、自分も AWS CLI を使ったハンズオン資料のようなものを作成中なので、近々公開できるかなと思ってます。


comments powered by Disqus