「S3バケットにオブジェクト有効期限を設定したらフォルダまで消えた」を理解する

「S3バケットにオブジェクト有効期限を設定したらフォルダまで消えたんだけど!?」と言われて、そういうものだしと思ったけど、どういう理屈でそうなるのかをちゃんと整理したことなかった。簡単にまとめれば…

* S3におけるフォルダは、キーが `/` でおわるオブジェクトをコンソールではフォルダっぽく見せてるだけ
* コンソールでフォルダ作成をすると、実際にはS3上で0バイトのオブジェクトが作成される
* オブジェクトなので、オブジェクトの有効期限をライフサイクルルールで指定すれば、それが適用される

…こんな感じになりそう。もしフォルダ（っぽいオブジェクト）には触れないライフサイクルルールを作りたければ、多少の配慮が必要になる。

# S3でのフォルダとはなにか

まず、僕の理解では、以下が大前提。

1. S3はオブジェクトストレージでありキーバリューストアである
2. 格納されているものは、実は「キーに対応した値」「キーに対応したメタデータ」だけである

S3に格納されたもの＝オブジェクトは、データの実体（ペイロード）を「値」に格納していて、バージョンとかライフサイクルルールとかといったプロパティを「メタデータ」に持っている（たぶん）。

AWS CLIやそのほかのAPIをたたくツールだと、オブジェクト（手元ではファイル）のアップロード時にキーとファイルを指定していて、キーはまるでリモートファイルパスだ。でも「実はそれは、本当にそういうテキスト値のキーなんだよ」といわれても、そういうものかといえるだろう。もしバケット直下の `photos/myphoto.jpg` に愛犬の写真画像ファイルを保存したなら、そこにファイル名とかフォルダパスというものはなくて、あくまで  `photos/myphoto.jpg` というキーにバイナリデータが格納されている。 

> 例えば、コンソールで `photos`という名前のフォルダを作成し、その中に `myphoto.jpg` という名前のオブジェクトを保存できます。このオブジェクトはキー名 `photos/myphoto.jpg` で保存され…
> （[フォルダを使用して Amazon S3 コンソールでオブジェクトを整理する - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/using-folders.html)）

コンソールで操作しても、同じだ、という説明だ。でもコンソールで操作していると「だってフォルダ作るじゃん」「表示されるじゃん」となるかもしれない（僕はそう思った）。これは、コンソールがキーを見て「これはフォルダのように表示する」「これはファイルのように表示する」と表示を分けているだけらしい。

> Amazon S3コンソールを使用してフォルダを作成すると、Amazon S3は、指定したフォルダ名に設定されたキーを持つ0バイトのオブジェクトを作成します。例えば、バケットに `photos` という名前のフォルダを作成した場合、Amazon S3コンソールは `photos/` キーを使用して0バイトのオブジェクトを作成します。コンソールは、フォルダの考え方をサポートするために、このオブジェクトを作成します。Amazon S3コンソールでは、キー名の最後 (末尾) の文字がスラッシュ (`/`) になっているすべてのオブジェクト ( `examplekeyname/` など) がフォルダとして扱われます。
> （[フォルダを使用して Amazon S3 コンソールでオブジェクトを整理する - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/using-folders.html)）

ちょっとまとめておこう。

|キー|実体|AWSマネジメントコンソールでの表示|
|---|---|---|
|末尾が `/` （ `photos/` など）|オブジェクト|**フォルダ**として表示される。|
|末尾が `/` 以外|オブジェクト|**ファイル**として表示される。|

# オブジェクト有効期限を設定したらフォルダまで消える

「現行バージョンを〇日経過したら有効期限切れにする」というライフサイクルルールを作って、バケットのバージョニングを無効にしていると、実際には指定日数たったオブジェクトは消えます。AWSマネジメントコンソール上では **フォルダに見えるものも、実体は0バイトのオブジェクト** なので、ルールの対象条件を満たすと、この処理が適用されます。フォルダに見えるオブジェクトが消える、つまり、AWSマネジメントコンソールで見えるフォルダが消える、ということです。

ライフサイクルルールには、対象となるオブジェクトを指定するフィルタを設定できます。もしフィルターが空だった場合、バケット内のすべてのオブジェクトにこのルールが適用されます。

> 空のフィルター を指定できます。その場合、ルールはオブジェクト内のすべてのバケットに適用されます。
> （[Filter - ライフサイクル設定の要素 - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/intro-lifecycle-rules.html#intro-lifecycle-rules-filter)）

「S3バケットにオブジェクト有効期限を設定したらフォルダまで消えたんだけど!?」は、削除用ライフサイクルルールがバケット内のオブジェクトすべてに対して設定されており、フォルダも実態はオブジェクトだから、これが適用されたということで説明完了になります。

# フォルダを消さないライフサイクルルールを考える

最後に、フォルダ（扱いのオブジェクト）を消さないライフサイクルルールを考えてみます。未検証です。

* **ライフサイクルルールをフォルダごとに作成し、フォルダのキーをプレフィックスに指定する**。そのフォルダ配下のオブジェクトだけにルールが適用されると思います。フォルダの分だけライフサイクルルールを作らないといけないのが難です。またサブフォルダは消えます。対策としてはフォルダツリー末端のリーフフォルダでルールを作ることですが、この場合途中のフォルダにファイルがあったとしても、ルールは適用されません。
* **プレフィックスは指定せず、オブジェクトサイズが1バイト以上を条件にする**。バケット内のすべてのファイル（扱いのオブジェクト）が対象になり、フォルダ（扱いのオブジェクト）は対象外にできそうです。ただ0バイトのファイル（扱いのオブジェクト）があった場合には、対象外になってしまいます。
* **プレフィックスは指定せず、特定のタグを条件にする**。たぶん一番厳密にやりたいことを実現できそうです。ただしファイル（扱いオブジェクト）には、必ずタグを付与することが運用条件になります。

2番目の「1バイト以上のオブジェクト」を条件にするのが、汎用性高くて楽そうかな。でもWindowsでは0バイトのファイルを使う話はあまり聞かないけど、Linuxでは `touch` してあらかじめパーミッション整えておくとかタイムスタンプ的に使うとか日常的な話で、Linux感覚で0バイトのオブジェクトを作り始めたら処理から外れてしまう。2番目に、1番目か3番目の運用になじみやすい方を組み合わせるのがいいかも知れない。

# 参照

* [フォルダを使用して Amazon S3 コンソールでオブジェクトを整理する - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/using-folders.html)
* [Filter - ライフサイクル設定の要素 - Amazon Simple Storage Service](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/intro-lifecycle-rules.html#intro-lifecycle-rules-filter)

仕組みの理解に当たっては、以下も参考にさせていただきました。

* [Amazon S3における「フォルダ」という幻想をぶち壊し、その実体を明らかにする | DevelopersIO](https://dev.classmethod.jp/articles/amazon-s3-folders/)

「メタデータ」「ペイロード」といったオブジェクトストレージ用語の再確認のために、以下も参考にしました。

* [オブジェクトストレージを定義付ける8つの属性：オブジェクトストレージを理解する（2） - TechTargetジャパン サーバ＆ストレージ](https://techtarget.itmedia.co.jp/tt/news/1209/06/news02.html)