S3で誤って削除してしまったオブジェクトを過去VersionからAWS CLIで復旧

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

西澤です。構築中のプロジェクトでS3のライフサイクル設定を誤って設定してしまいました。幸いVersioningを有効にしてあるS3バケットだったので、問題なく復旧することができました。今回は、その復旧作業についてまとめておきます。

S3バケットのVersioning機能

S3にはVersiong機能があります。これを有効にすると上書きしたオブジェクトや削除したオブジェクトを元通りに復旧することができます(ストレージ利用料金は、差分管理とはならず、全てのバージョンを含めたサイズが課金対象となります)。AWS Management Consoleから状況を確認する場合には、バージョン(Versions)を非表示(Show)にします。

s3_delete_marker

上記画像は、Key=aというオブジェクトを以下のように操作した例です。

  1. put
  2. delete
  3. put
  4. put(上書き)
  5. delete

ここで、Delete Markerというものの存在に気付きます。こちらは削除フラグのようなもので、論理的削除の管理に使われています。最後の操作がdeleteなのであれば、この最後のDelete Markerを削除(Deleteのdeleteってややこしいですが)することで、その1つ前の世代のオブジェクトが復旧されます。人間が1件復旧するだけなら、GUIからやるのが安全・安心・確実です。ただし、今回はまとめて処理する必要がありました。

Delete MarkerをAWS CLIから確認する

このDelete Markerをみんな大好きAWS CLIから確認してみます。

$ BUCKET=hogehoge
$ aws s3api list-object-versions --bucket $BUCKET
{
    "DeleteMarkers": [
        {
            "Owner": {
                "DisplayName": "ownername", 
                "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            }, 
            "IsLatest": true, 
            "VersionId": "cmyaD0g1PGsvjBRuptGj.RHSJ7zMZxG9", 
            "Key": "a", 
            "LastModified": "2016-02-26T09:03:06.000Z"
        }, 
        {
            "Owner": {
                "DisplayName": "ownername", 
                "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            }, 
            "IsLatest": false, 
            "VersionId": "647vSuNURZfNR8IqM9QnINp43._mzfs1", 
            "Key": "a", 
            "LastModified": "2016-02-26T09:02:00.000Z"
        }
    ], 
    "Versions": [
        {
            "LastModified": "2016-02-26T09:02:56.000Z", 
            "VersionId": "EqhCt0tr2lCsHaBFhm7uKgN72u54L1iU", 
            "ETag": "\"f4288da1c441491df98df891e8406cd1\"", 
            "StorageClass": "STANDARD", 
            "Key": "a", 
            "Owner": {
                "DisplayName": "ownername", 
                "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            }, 
            "IsLatest": false, 
            "Size": 7
        }, 
        {
            "LastModified": "2016-02-26T09:02:10.000Z", 
            "VersionId": ".DHLk.Ifd551Lk8SdI39_1XiPCM9CgGf", 
            "ETag": "\"5c9597f3c8245907ea71a89d9d39d08e\"", 
            "StorageClass": "STANDARD", 
            "Key": "a", 
            "Owner": {
                "DisplayName": "ownername", 
                "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            }, 
            "IsLatest": false, 
            "Size": 4
        }, 
        {
            "LastModified": "2016-02-26T09:01:51.000Z", 
            "VersionId": "IOfZSo2O9N.MI_UKBFFWuNP45vLVmj.W", 
            "ETag": "\"5c9597f3c8245907ea71a89d9d39d08e\"", 
            "StorageClass": "STANDARD", 
            "Key": "a", 
            "Owner": {
                "DisplayName": "ownername", 
                "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
            }, 
            "IsLatest": false, 
            "Size": 4
        }
    ]
}

これを見ると、DeleteMarkersVersionsという2つのKeyでそれぞれ別に管理されていることがわかります。そして、現行世代(Current Version)は、IsLatestというKeyで管理されているようです。ということで、IsLatest=trueDelete Markerを削除してあげれば、誤って削除したオブジェクトを復旧できそうです。

Delete MarkerをAWS CLIから削除

ということで、AWS CLIからこの条件を満たすDelete MarkerのVersionId指定して削除することで、オブジェクトを復旧することができます。

$ BUCKET=hogehoge
$ KEY=a
$ VERID=$(aws s3api list-object-versions \
>  --bucket ${BUCKET} \
>  --query "DeleteMarkers[?Key==\`${KEY}\`]|[?IsLatest==\`true\`].VersionId" \
>  --output text) && echo ${VERID}
cmyaD0g1PGsvjBRuptGj.RHSJ7zMZxG9

$ aws s3api delete-object \
>  --bucket ${BUCKET} \
>  --version-id ${VERID} \
>  --key ${KEY}
{
    "VersionId": "cmyaD0g1PGsvjBRuptGj.RHSJ7zMZxG9", 
    "DeleteMarker": true
}

Delete Marker(削除フラグ)が、DeleteMarker=trueってよくわからないですが、成功したようです。

念の為、--queryオプションについて補足しておきます。

  • [?Key==\`${KEY}\`]
    • KEYが一致している
  • |
    • and条件で絞り込み
  • [?IsLatest==\`true\`]
    • IsLatestがtrue

AWS Management Consoleから復旧確認

それでは、GUIからもオブジェクトが復旧されたことを確認してみましょう。

s3_delete_marker2

過去バージョンを隠して表示してみます。

s3_delete_marker3

復旧されたことを確認できました。

誤って削除してしまったオブジェクトをAWS CLIでまとめて復旧

これを応用することで、まとめてオブジェクトを復旧することができました。他のsdkを使った方がスマートかもしれませんが、bashの配列でがんばってみました。

$ aws s3api list-object-versions --bucket $BUCKET \
>  --bucket ${BUCKET} \
>  --query "DeleteMarkers[?IsLatest==\`true\`].[Key,VersionId]" \
>  --output text | while read line
>  do
>    array=($line)
>    KEY=${array[0]}
>    VERID=${array[1]}
>    aws s3api delete-object \
>      --bucket ${BUCKET} \
>      --version-id ${VERID} \
>      --key ${KEY}
>  done

list-object-versionsで取得したKeyとVerionIdをbash配列で処理する方法で復旧に成功しました。もう少し、条件を追加したい場合には、query条件を工夫してみてください。削除オペレーションですので、作業は十分にご注意の上、自己責任でご対応をお願します。

削除したオブジェクトをさらに削除可能

こちらの動作を確認していたところ、おかしなことに気付きました。VersioningされているKeyは何度も削除することができます。これまで書いた通り、Delete Markerは論理削除フラグでしかないのですが、それもバージョン管理される為、同一のKeyにいくつもDelete Markerを追加することができることがわかりました。通常の操作ではこのようなことは起きないと思いますが、複数の人またはアプリケーションから同時に操作されるとこのような現象が起きることもあるかもしれません。

s3_delete_marker4

もちろんこの場合は、復旧したいオブジェクトが見えるようになるまで、Delete Markerを削除しないと、オブジェクトは元通り見えるようにはなりません。

まとめ

今回は自分の不注意により、お客様にご迷惑をおかけしてしまいました。転んだけどただで起きるのはあれなのと、自戒の念を込めて、こちらにまとめさせていただきました。

どこかの誰かの参考になれば嬉しいです。