処方箋データ基盤チームでエンジニアをしている岩佐 (孝浩) です。 カケハシには「岩佐」さんが複数名在籍しており、社内では「わささん」と呼ばれています。
私が所属する処方箋データ基盤チームは、日本全国の薬局から送信される処方箋データを S3 に保存しています。 今後ストレージコストの増加が予測されるため、S3 ライフサイクル・ルールを利用してストレージクラスを変更することにしました。 この S3 バケットには EventBridge 通知が設定されており、ふと、「ストレージクラスの変更でイベントが発生するのでは」と気になりました。
結論として、ストレージクラスを変更すると、CopyObject
イベントが発生します。
S3 バケットにイベント通知を設定している場合、このイベントを意識していないと、予期せぬ結果を引き起こす可能性があり、注意が必要です。
この投稿では、S3 バケットに EventBridge 通知が設定されている場合の動作検証と対応方法について、備忘録も兼ねて紹介します。
AWS リソース作成
まずは、検証用の AWS リソースを作成します。
S3 バケット作成
S3 バケットを作成し、EventBridge 通知を有効化します。
# S3 バケット名 bucket=storage-class-event-test-$(uuidgen | tr -d - | tr '[:upper:]' '[:lower:]') # S3 バケット作成 aws s3api create-bucket \ --bucket $bucket \ --region ap-northeast-1 \ --create-bucket-configuration LocationConstraint=ap-northeast-1 # EventBridge 通知有効化 aws s3api put-bucket-notification-configuration \ --bucket $bucket \ --notification-configuration='{ "EventBridgeConfiguration": {} }'
検証用オブジェクトのアップロード
検証用オブジェクトとして、任意のオブジェクトをアップロードします。 この投稿では、カケハシのテックブログのページをアップロードしています。
# Download curl -o example.html https://kakehashi-dev.hatenablog.com/entry/2024/11/14/100000 # サイズ確認 ls -lah example.html # -rw-r--r--. 1 cloudshell-user cloudshell-user 133K Nov 24 08:06 example.html # Upload aws s3 cp example.html s3://$bucket/example.html
ライフサイクル・ルールを利用してストレージクラスを変更する場合、128KB 未満のオブジェクトはストレージクラスが変更されませんので、ご注意ください。
Objects smaller than 128 KB will not transition by default to any storage class
EventBridge Rule 作成
EventBridge Rule を作成し、ターゲットとして CloudWatch Logs を設定します。
# Event Pattern pattern=$(echo '{ "source": ["aws.s3"], "detail-type": ["Object Created"], "detail": { "bucket": { "name": ["<BUCKET>"] } } }' | sed -e "s/<BUCKET>/$bucket/" | jq -r .) # Event Rule 作成 rule_name=storage-class-event-test-rule aws events put-rule \ --name $rule_name \ --event-pattern "$pattern" # CloudWatch Logs 作成 log_group_name=/aws/events/$rule_name aws logs create-log-group \ --log-group-name $log_group_name # CloudWatch Logs ARN 取得 log_group_arn=$( aws logs describe-log-groups \ --log-group-name-prefix $log_group_name \ | jq -r '.logGroups[0].arn' ) # Event Target 作成 aws events put-targets \ --rule $rule_name \ --targets "Id"="cw-logs","Arn"="$log_group_arn"
ここで設定している Event Pattern が注意すべきポイントで、後ほど動作検証します。
以下のキャプチャのとおり、Management Console で作成した場合、この Event Pattern を設定しているケースが多いと推測されるので、必要に応じて設定の見直しをお勧めします。
動作検証1回目
ストレージクラス変更
この投稿では、AWS CLI で既存オブジェクトのストレージクラスを変更します。 その他の方法については、公式ドキュメントをご参照ください。
# Standard IA に変更 aws s3 cp s3://$bucket/example.html s3://$bucket/example.html \ --storage-class STANDARD_IA
CloudWatch Logs 確認
EventBridge Rule のターゲットに設定した CloudWatch Logs を確認します。
log_stream_name=$( aws logs describe-log-streams \ --log-group-name $log_group_name \ --order-by LastEventTime \ --descending \ | jq -r '.logStreams[0].logStreamName' ) aws logs get-log-events \ --log-group-name $log_group_name \ --log-stream-name $log_stream_name \ | jq '.events[].message | fromjson'
以下のようなイベントが記録されているはずです。
{ "version": "0", "id": "12bcd22e-81e4-bba5-2037-95ab37991eaa", "detail-type": "Object Created", "source": "aws.s3", "account": "123456789012", "time": "2024-11-24T08:23:39Z", "region": "ap-northeast-1", "resources": [ "arn:aws:s3:::storage-class-event-test-<UUID>" ], "detail": { "version": "0", "bucket": { "name": "storage-class-event-test-<UUID>" }, "object": { "key": "example.html", "size": 136162, "etag": "2fb8b1f49c6b495c614f1f7a7efb8835", "sequencer": "006742E28B93AB9A39" }, "request-id": "8TP3RRRX0ZYF0HJP", "requester": "123456789012", "source-ip-address": "xxx.xxx.xxx.xxx", "reason": "CopyObject" } }
動作検証結果
上で確認したとおり、現在設定されている Event Pattern では、ストレージクラスの変更時に発生する CopyObject
イベントも処理してしまいます。
次の条件に該当する場合、Event Pattern の見直しが必要です。
- ストレージクラスの変更時に発生するイベントを処理した場合、データの不整合が生じる。(冪等性を考慮した設計になっていない)
- 膨大な数のオブジェクトが、ストレージクラスの変更対象になる。
Object Created - CopyObject
イベントを処理する必要が無い。
Event Pattern 再設定による対応
ストレージクラスの変更時に発生する Object Created - CopyObject
イベントを処理しないように、Event Pattern に reason
を追加します。
@@ -10,6 +10,9 @@ "name": [ "storage-class-event-test-<UUID>" ] - } + }, + "reason": [ + "PutObject" + ] } }
# Event Pattern pattern=$(echo '{ "source": ["aws.s3"], "detail-type": ["Object Created"], "detail": { "bucket": { "name": ["<BUCKET>"] }, "reason": [ "PutObject" ] } }' | sed -e "s/<BUCKET>/$bucket/" | jq -r .) aws events put-rule \ --name $rule_name \ --event-pattern "$pattern"
動作検証2回目
CloudWatch Logs 再作成
ストレージクラスの変更が EventBridge Rule で処理されていないことを確認するため、事前に CloudWatch Logs を再作成しておきます。
# 削除 aws logs delete-log-group \ --log-group-name $log_group_name # 作成 aws logs create-log-group \ --log-group-name $log_group_name
ストレージクラス変更
Standard IA
に変更したオブジェクトを Standard
に戻します。
# Standard に変更 aws s3 cp s3://$bucket/example.html s3://$bucket/example.html \ --storage-class STANDARD
CloudWatch Logs 確認
EventBridge Rule のターゲットに設定した CloudWatch Logs を確認します。
"logStreams": []
が表示されるはずです。
aws logs describe-log-streams \ --log-group-name $log_group_name \ --order-by LastEventTime \ --descending { "logStreams": [] }
(補足) アップロード確認
念のため、オブジェクトのアップロード (PutObject
) が EventBridge Rule で処理されることを確認しておきます。
# Upload aws s3 cp example.html s3://$bucket/example-new.html # CloudWatch Logs 確認 log_stream_name=$( aws logs describe-log-streams \ --log-group-name $log_group_name \ --order-by LastEventTime \ --descending \ | jq -r '.logStreams[0].logStreamName' ) aws logs get-log-events \ --log-group-name $log_group_name \ --log-stream-name $log_stream_name \ | jq '.events[].message | fromjson'
"reason": "PutObject"
のイベントは、EventBridge Rule で処理されていることが確認できます。
{ "version": "0", "id": "f54bb5b7-e02e-da2a-b89c-6fb667139950", "detail-type": "Object Created", "source": "aws.s3", "account": "123456789012", "time": "2024-11-24T09:59:39Z", "region": "ap-northeast-1", "resources": [ "arn:aws:s3:::storage-class-event-test-<UUID>" ], "detail": { "version": "0", "bucket": { "name": "storage-class-event-test-<UUID>" }, "object": { "key": "example-new.html", "size": 136162, "etag": "2fb8b1f49c6b495c614f1f7a7efb8835", "sequencer": "006742F90B0B6E1613" }, "request-id": "MM7A7QD53JB1WFC5", "requester": "123456789012", "source-ip-address": "xxx.xxx.xxx.xxx", "reason": "PutObject" } }
まとめ
S3 バケットにイベント通知を設定している場合、ストレージクラスを変更すると、予期せぬイベントを処理してしまう可能性があります。
次の条件に該当する場合、Event Pattern に reason
が含まれているか確認することをお勧めします。
- ストレージクラスの変更時に発生するイベントを処理した場合、データの不整合が生じる。(冪等性を考慮した設計になっていない)
- 膨大な数のオブジェクトが、ストレージクラスの変更対象になる。
Object Created - CopyObject
イベントを処理する必要が無い。
以上、お役に立てれば幸いです。