Amazon S3 バケットに複数のライフサイクルルールを設定したときの挙動を確認してみた

2023.01.18

いわさです。

Amazon S3 ではライフサイクルルールを構成することでオブジェクトのストレージクラスの移行や古いオブジェクトの削除などを自動化することが出来ます。

上記記事でも言及されていますが、このライフサイクルルールは複数設定することが可能です。
さらに、その複数のルールは単一ルールではエラーとなるような競合したルールを組み合わせることも出来たりします。

公式ドキュメントには重複・競合したルールは以下のように「最も安価なパスが選択される」とされています。

  • 重複するプレフィックスまたはアクションを指定する S3 ライフサイクル設定を指定する場合があります。
  • 一般的に、S3 ライフサイクルはコストに合わせて最適化されます。たとえば、2 つの有効期限ポリシーが重複している場合は、短い有効期限ポリシーが適用されるため、データが予想よりも長く保存されることはありません。同様に、2 つの移行ポリシーが重複している場合は、S3 ライフサイクルによってオブジェクトが低コストのストレージクラスに移行されます。
  • いずれの場合でも、S3 ライフサイクルによって、最も安価なパスが選択されます。この一般的なルールの例外として、S3 Intelligent-Tiering ストレージクラスの場合があります。S3 Intelligent-Tiering は、S3 Glacier Flexible Retrieval および S3 Glacier Deep Archive ストレージクラスを除くどのストレージクラスよりも S3 ライフサイクルで優先されます。

なるほどという感じですが、実際に確認してみたいなと思い、さらにライフサイクルルールの検証は待機期間が多くなりがちなので、いくつかの複数ルールを構成したときの処理結果を観察してみました。

ファイル作成とライフサイクル設定

以下のような同一のオブジェクト構成のバケットを用意しました。

% tree .
.
├── piyo1
│   ├── hoge1.txt
│   └── hoge2.txt
└── piyo2
    ├── hoge1.txt
    └── hoge2.txt

% aws s3 sync ./ s3://hoge1021fuga1 --profile hoge
upload: piyo1/hoge2.txt to s3://hoge1021fuga1/piyo1/hoge2.txt   
upload: piyo2/hoge1.txt to s3://hoge1021fuga1/piyo2/hoge1.txt    
upload: piyo1/hoge1.txt to s3://hoge1021fuga1/piyo1/hoge1.txt    
upload: piyo2/hoge2.txt to s3://hoge1021fuga1/piyo2/hoge2.txt     

:

% aws s3 sync ./ s3://hoge1021fuga6 --profile hoge
upload: piyo1/hoge1.txt to s3://hoge1021fuga6/piyo1/hoge1.txt   
upload: piyo2/hoge1.txt to s3://hoge1021fuga6/piyo2/hoge1.txt    
upload: piyo2/hoge2.txt to s3://hoge1021fuga6/piyo2/hoge2.txt     
upload: piyo1/hoge2.txt to s3://hoge1021fuga6/piyo1/hoge2.txt

バケットのバージョニングを有効にした後、さらに2度オブジェクトの更新を行うと以下のように旧バージョンが作成されます。

% aws s3api list-object-versions --bucket hoge1021fuga1 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'

"piyo1/hoge1.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo1/hoge1.txt","2022-10-23T01:50:25+00:00","STANDARD",false
"piyo1/hoge1.txt","2022-10-23T01:49:57+00:00","STANDARD",false

"piyo1/hoge2.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:25+00:00","STANDARD",false
"piyo1/hoge2.txt","2022-10-23T01:49:57+00:00","STANDARD",false

"piyo2/hoge1.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:25+00:00","STANDARD",false
"piyo2/hoge1.txt","2022-10-23T01:49:57+00:00","STANDARD",false

"piyo2/hoge2.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:25+00:00","STANDARD",false
"piyo2/hoge2.txt","2022-10-23T01:49:57+00:00","STANDARD",false

そこに以下のような複数ライフサイクルルールを組み合わせて設定しておきます。

  • 無効ルールと有効ルール
  • プレフィックスの異なる削除ルール
  • 移行ルールと削除ルール
  • 異なる日数で異なるストレージクラス
  • 世代の異なる旧バージョン削除

数日経過させて結果を見てみます。

ルール詳細と結果 ※折りたたんでいます

無効ルールと有効ルール

無効ルールと有効ルール

ルール

こちらは、無効な Glacier Deep Archive へ移行するルールと、有効な Glacier 移行ルールの組み合わせです。
これはシンプルですね。おそらく無効なルールは無視されて有効な Glacier への移行のみが実行されるでしょう。

% aws s3api get-bucket-lifecycle-configuration --bucket hoge1021fuga1 --profile hoge
{
    "Rules": [
        {
            "ID": "無効ルール",
            "Filter": {},
            "Status": "Disabled",
            "NoncurrentVersionTransitions": [
                {
                    "NoncurrentDays": 0,
                    "StorageClass": "DEEP_ARCHIVE"
                }
            ]
        },
        {
            "ID": "有効ルール",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionTransitions": [
                {
                    "NoncurrentDays": 0,
                    "StorageClass": "GLACIER"
                }
            ]
        }
    ]
}
2 日後の朝

1 日後に移行はされてそうなのですが、チェックを忘れていました。
ただ、無効ルールが動いていないことは確認出来ました。有効ルールの Glacier 移行のみが動いていますね。

% aws s3api list-object-versions --bucket hoge1021fuga1 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo1/hoge1.txt","2022-10-23T01:50:25+00:00","GLACIER",false
"piyo1/hoge1.txt","2022-10-23T01:49:57+00:00","GLACIER",false
"piyo1/hoge2.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:25+00:00","GLACIER",false
"piyo1/hoge2.txt","2022-10-23T01:49:57+00:00","GLACIER",false
"piyo2/hoge1.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:25+00:00","GLACIER",false
"piyo2/hoge1.txt","2022-10-23T01:49:57+00:00","GLACIER",false
"piyo2/hoge2.txt","2022-10-23T01:50:48+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:25+00:00","GLACIER",false
"piyo2/hoge2.txt","2022-10-23T01:49:57+00:00","GLACIER",false
プレフィックスの異なる削除ルール

プレフィックスの異なる削除ルール

ルール

こちらはプレフィックス違いで削除ルールが設定されています。
プレフィックスが違うのでそもそも競合したルールではないのですが複数ルールということで一応確認してみました。

% aws s3api get-bucket-lifecycle-configuration --bucket hoge1021fuga2 --profile hoge
{
    "Rules": [
        {
            "ID": "削除ルール1",
            "Filter": {
                "Prefix": "piyo1/"
            },
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 1
            }
        },
        {
            "ID": "削除ルール2",
            "Filter": {
                "Prefix": "piyo2/"
            },
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 1
            }
        }
    ]
}
2 日後の朝

こちらはどちらのプレフィックスもライフサイクルに基づいて古いバージョンが削除されていることが確認出来ました。

% aws s3api list-object-versions --bucket hoge1021fuga2 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:49+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:49+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:49+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:49+00:00","STANDARD",true
移行ルールと削除ルール

移行ルールと削除ルール

ルール

こちらは旧バージョンを即 Glacier へ移行するルールと、数日猶予を持って旧バージョンを削除するルールです。
削除のほうがコスト的には安いと思いますが、日数的には Glacier のほうが安いです。この場合はどうなるのでしょうか。

% aws s3api get-bucket-lifecycle-configuration --bucket hoge1021fuga3 --profile hoge
{
    "Rules": [
        {
            "ID": "移行ルール",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionTransitions": [
                {
                    "NoncurrentDays": 0,
                    "StorageClass": "GLACIER"
                }
            ]
        },
        {
            "ID": "削除ルール",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 2
            }
        }
    ]
}
2 日後の朝

朝時点では旧バージョンの Glacier 移行のみ実行されていました。

% aws s3api list-object-versions --bucket hoge1021fuga3 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo1/hoge1.txt","2022-10-23T01:50:27+00:00","GLACIER",false
"piyo1/hoge1.txt","2022-10-23T01:49:59+00:00","GLACIER",false
"piyo1/hoge2.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:27+00:00","GLACIER",false
"piyo1/hoge2.txt","2022-10-23T01:49:59+00:00","GLACIER",false
"piyo2/hoge1.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:27+00:00","GLACIER",false
"piyo2/hoge1.txt","2022-10-23T01:49:59+00:00","GLACIER",false
"piyo2/hoge2.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:27+00:00","GLACIER",false
"piyo2/hoge2.txt","2022-10-23T01:49:59+00:00","GLACIER",false
3 日後

その後削除ルールによって古いバージョンが削除されていました。

% aws s3api list-object-versions --bucket hoge1021fuga3 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:50+00:00","STANDARD",true

どちらかのルールだけ使われるというわけではなく、コストが安くなるように削除される前は Glacier へ、削除出来る日数になったら削除。という動きをしています。
一見、ドキュメントのとおり安くなる組み合わせで動いているように見受けられますね。

ただし、Glacier の場合は最小ストレージ期間 90 日と最小オブジェクトサイズ 32 KB という概念があります。
このテキストファイルは約 11 バイトほどでした。
2 日後に削除されることを考えると STANDARD のままで 2 日待ってから削除してくれたほうが安そうですが、そこまでは考慮はしないようだということがわかりました。

異なる日数で異なるストレージクラス

異なる日数で異なるストレージクラス

ルール

次は旧バージョンを 2 日後に Glacier へ、1 日後に Glacier Deep Archive へ移行するというものです。
これは...STANDARD -> Deep Archive -> Glacier という移行になるのか、Deep Archive に留まるのか気になります。

% aws s3api get-bucket-lifecycle-configuration --bucket hoge1021fuga4 --profile hoge
{
    "Rules": [
        {
            "ID": "旧バージョン2日後にGlacierへ",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionTransitions": [
                {
                    "NoncurrentDays": 2,
                    "StorageClass": "GLACIER"
                }
            ]
        },
        {
            "ID": "旧バージョン1日後にGlacier Deep Archiveへ",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionTransitions": [
                {
                    "NoncurrentDays": 1,
                    "StorageClass": "DEEP_ARCHIVE"
                }
            ]
        }
    ]
}
2 日後

まずは期待どおり、旧バージョンが Deep Archive 層に移動しました。

% aws s3api list-object-versions --bucket hoge1021fuga4 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo1/hoge1.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo1/hoge1.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
"piyo1/hoge2.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo1/hoge2.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
"piyo2/hoge1.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo2/hoge1.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
"piyo2/hoge2.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo2/hoge2.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
3 日後

その後しばらく経過しても Glacier 層に移動することはなかったですね。

% aws s3api list-object-versions --bucket hoge1021fuga4 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:50+00:00","STANDARD",true
"piyo1/hoge1.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo1/hoge1.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
"piyo1/hoge2.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo1/hoge2.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
"piyo2/hoge1.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo2/hoge1.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false
"piyo2/hoge2.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:28+00:00","DEEP_ARCHIVE",false
"piyo2/hoge2.txt","2022-10-23T01:50:00+00:00","DEEP_ARCHIVE",false

これ、後から気がついたのですが取り出しが発生しないクラスでも試してみるべきでした。
そのあたりが関係していて移動出来ていない可能性があるなと。

こちらは別途実験してみます。

世代の異なる旧バージョン削除

世代の異なる旧バージョン削除

ルール

こちらはちょっと複雑なのですが、旧バージョン削除は残すバージョン数を指定することが出来ます。

ひとつめが 1 日後に旧バージョンを 1 つだけ残して削除というルールと、2 日後に全ての旧バージョンを削除するというルールです。
これは、1 つの旧バージョンが一時的に残って最終的には全旧バージョンが削除されそうです。

ふたつめが 1 日後に全旧バージョンを削除し、2 日後に最新以外の旧バージョンを削除するというルールです。
これは最初から 2 日後に最新以外の旧バージョンを削除するルールは無視されそうです。

% aws s3api get-bucket-lifecycle-configuration --bucket hoge1021fuga5 --profile hoge
{
    "Rules": [
        {
            "ID": "最新以外の旧バージョンは1日後に削除",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 1,
                "NewerNoncurrentVersions": 1
            }
        },
        {
            "ID": "すべての旧バージョンは2日後に削除",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 2
            }
        }
    ]
}

% aws s3api get-bucket-lifecycle-configuration --bucket hoge1021fuga6 --profile hoge
{
    "Rules": [
        {
            "ID": "1日後に全旧バージョンを削除",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 1
            }
        },
        {
            "ID": "2日後に最新以外の旧バージョンを削除",
            "Filter": {},
            "Status": "Enabled",
            "NoncurrentVersionExpiration": {
                "NoncurrentDays": 2,
                "NewerNoncurrentVersions": 1
            }
        }
    ]
}
2 日後

1 つ目のルールも 2 つ目のルールも期待どおり動作していますね。
1 日後には指定された旧バージョンが削除されていることが確認出来ました。

% aws s3api list-object-versions --bucket hoge1021fuga5 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo1/hoge1.txt","2022-10-23T01:50:29+00:00","STANDARD",false
"piyo1/hoge2.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:29+00:00","STANDARD",false
"piyo2/hoge1.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:29+00:00","STANDARD",false
"piyo2/hoge2.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:29+00:00","STANDARD",false

% aws s3api list-object-versions --bucket hoge1021fuga6 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:53+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:52+00:00","STANDARD",true
3 日後

次の日にはルール1で指定した全旧バージョン削除も実行されています。
最新の旧バージョンだけは削除を少し猶予したい。という場合に複数ルールの組み合わせは活用出来そうです。単一ルールだと実現出来ないのでこれは良さそうですね。

% aws s3api list-object-versions --bucket hoge1021fuga5 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:51+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:52+00:00","STANDARD",true

% aws s3api list-object-versions --bucket hoge1021fuga6 --profile hoge | jq -r '.Versions[] | [.Key, .LastModified, .StorageClass, .IsLatest] | @csv'
"piyo1/hoge1.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo1/hoge2.txt","2022-10-23T01:50:53+00:00","STANDARD",true
"piyo2/hoge1.txt","2022-10-23T01:50:52+00:00","STANDARD",true
"piyo2/hoge2.txt","2022-10-23T01:50:52+00:00","STANDARD",true

さいごに

今回は先日 Amazon S3 バケットに複数のライフサイクルルールを設定したのでその時の挙動をまとめてみました。

まず最初の感想としては、もうちょっとテストパターン良い組み合わせなかったのか?です。
ファイルの日付を見て頂くとわかるのですが、実験したのは 3 ヶ月前くらいなんですよね。なぜこの組み合わせにしたのか最早覚えていないです。

ただ今回振り返ってみて、ドキュメントに記載のとおり複数ルールの場合でも安くなるようにうまいことルールが実行されるという点は確認出来ました。
加えて、アーカイブ系のストレージの最小ストレージ期間や最小オブジェクトサイズまでは考慮されていなさそうだということもわかりました。

得るものがゼロでは無かったです。