michimani.net

[レポート] JAWS-UG CLI 専門支部 #169R S3基礎 バージョニング に参加しました #jawsug_cli

2020-10-08

JAWS-UG CLI 専門支部 #169R S3基礎 バージョニング に参加したので、そのレポートです。

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

前回、前々回のレポートはこちら。

目次

S3 の全体像とサービス概要

S3 の全体像とサービス概要については 前回 のレポートを参照してください。今回はその差分のみ。

ハンズオン

今回のハンズオンでは、バージョニングを有効にした S3 バケットに対してオブジェクトをアップロード・更新し、特定バージョンの取得や削除を行いました。

今回も、ハンズオンの詳細な手順についてはイベントページの資料 及び下記のハンズオン資料におまかせするとして、個人的に気になった部分のコマンドについて書いておきます。

1.3. IAMポリシーの作成

IAM ポリシーを作成する際にパスを使用することで、その後の管理がやりやすくなります。

$ aws iam create-policy help
...
SYNOPSIS
            create-policy
          --policy-name <value>
          [--path <value>]
          --policy-document <value>
          [--description <value>]
          [--cli-input-json | --cli-input-yaml]
          [--generate-cli-skeleton <value>]
          [--cli-auto-prompt <value>]

iam create-policy コマンドの --path オプションを使って、次のように実行します。

$ aws iam create-policy \
--policy-name "michimani-sample-policy" \
--path "/michimani/sample/" \
--policy-document file://policy

パスを使うことで、 list-policies コマンドの --path-prefix で結果を絞ることができます。

$ aws iam list-policies \
--path-prefix "/michimani/sample/"
{
    "Policies": [
        {
            "PolicyName": "michimani-sample-policy",
            "PolicyId": "ANPA2FQKQ3TXXXXXXXXXX",
            "Arn": "arn:aws:iam::1234XXXXXXXX:policy/michimani/sample/michimani-sample-policy",
            "Path": "/michimani/sample/",
            "DefaultVersionId": "v1",
            "AttachmentCount": 0,
            "PermissionsBoundaryUsageCount": 0,
            "IsAttachable": true,
            "CreateDate": "2020-10-08T11:34:10+00:00",
            "UpdateDate": "2020-10-08T11:34:10+00:00"
        }
    ]
}

2. S3バケットのバージョニング有効化

s3api put-bucket-versioning コマンドを使用します。

$ aws s3api put-bucket-versioning help
...
SYNOPSIS
            put-bucket-versioning
          --bucket <value>
          [--content-md5 <value>]
          [--mfa <value>]
          --versioning-configuration <value>
          [--expected-bucket-owner <value>]
          [--cli-input-json | --cli-input-yaml]
          [--generate-cli-skeleton <value>]
          [--cli-auto-prompt <value>]

--versioning-configuration オプションでバージョニングを有効にします。

$ aws s3api put-bucket-versioning \
--bucket ${S3_BUCKET_NAME} \
--versioning-configuration Status=Enabled

バージョニングのステータスを確認するには s3api get-bucket-versioning コマンドを使って、出力結果から Status の部分を抜き出します。

$ aws s3api get-bucket-versioning \
--bucket ${S3_BUCKET_NAME} \
--query 'Status' \
--output text

Enabled

無効化する場合は Status=Enabled として実行します。 (ハンズオンの手順 4)

$ aws s3api put-bucket-versioning \
--bucket ${S3_BUCKET_NAME} \
--versioning-configuration Status=Suspended

なお、バージョニングの設定を一度も触っていない場合、 get-buket-versioning では出力結果を得られず、 Status だけ抜出層としても None になります。

$ aws s3api get-bucket-versioning \
--bucket ${OTHER_S3_BUCKET_NAME} \
--query 'Status' \
--output text

None

なので、上記の無効化は、 無効化 というよりも 一時停止 (= Suspended) というイメージです。

3.1. ファイルの作成

バージョニングを有効にしたバケットに対してファイルをアップロードします。アップロードには s3 cp コマンドを使用します。

$ aws s3 cp sample_file.txt s3://${S3_BUCKET_NAME}

ちなみに、 sample_file.txt の中身は下記の内容です。

$ cat sample_file.txt
This is a sample file. (v1)

マネジメントコンソールで確認すると、オブジェクトの詳細画面にバージョンを指定するプルダウンが表示されています。

S3 versioned object 1

プルダウンからバージョンを指定すると、 バージョン ID が表示されます。

S3 versioned object 1

バージョン ID を構成する文字列にはピリオド . が含まれる場合もあります。

S3 オブジェクトを更新する場合、特に追加のパラメータなどは必要なく、先ほどと同じ s3 cp コマンドを使います。 sample_file.txt の中身の v1 の部分を変更して何度かアップロードを実行し、作成されたバージョンを確認してみます。オブジェクトのバージョン一覧を取得するには、 s3api list-object-versions コマンドを使用します。

$ aws s3api list-object-versions help
...
SYNOPSIS
            list-object-versions
          --bucket <value>
          [--delimiter <value>]
          [--encoding-type <value>]
          [--prefix <value>]
          [--expected-bucket-owner <value>]
          [--cli-input-json | --cli-input-yaml]
          [--starting-token <value>]
          [--page-size <value>]
          [--max-items <value>]
          [--generate-cli-skeleton <value>]
          [--cli-auto-prompt <value>]

バケット名とオブジェクトキーを指定して実行。今回は、各バージョンのバージョン ID と最終更新日時を、最終更新日時の降順で出力してみます。

$ aws s3api list-object-versions \
--bucket ${S3_BUCKET_NAME} \
--prefix sample_file.txt \
--query 'reverse(sort_by(Versions,&LastModified))[].{LM:LastModified,VID:VersionId}'
[
    {
        "LM": "2020-10-08T12:02:16+00:00",
        "VID": "PWdCD9sEmem67CqNHA2cBMUSQCY6t5Sv"
    },
    {
        "LM": "2020-10-08T12:02:09+00:00",
        "VID": "b4234JLABtdRGlQ9AGreIuSsqoaf964n"
    },
    {
        "LM": "2020-10-08T12:01:59+00:00",
        "VID": "jfbONeI0pJ1h9unJgRrvzmIFBvUBa0xv"
    },
    {
        "LM": "2020-10-08T12:01:49+00:00",
        "VID": "baLmE30tSBAuiZ8lhQ3.LcyAf3P2fv7N"
    },
    {
        "LM": "2020-10-08T11:56:26+00:00",
        "VID": "sRfncD5dSroDAQYq4s3gAinHWQpQp4VW"
    }
]

3.5 S3オブジェクトの特定バージョンのダウンロード

オブジェクトのダウンロードには s3api get-object コマンドを使用します。

$ s3api get-object help
...
SYNOPSIS
            get-object
          --bucket <value>
          [--if-match <value>]
          [--if-modified-since <value>]
          [--if-none-match <value>]
          [--if-unmodified-since <value>]
          --key <value>
          [--range <value>]
          [--response-cache-control <value>]
          [--response-content-disposition <value>]
          [--response-content-encoding <value>]
          [--response-content-language <value>]
          [--response-content-type <value>]
          [--response-expires <value>]
          [--version-id <value>]
          [--sse-customer-algorithm <value>]
          [--sse-customer-key <value>]
          [--sse-customer-key-md5 <value>]
          [--request-payer <value>]
          [--part-number <value>]
          [--expected-bucket-owner <value>]
          <outfile>

オプションがたくさんありますが、今回使用するのは --version-id オプションです。

--version-id で指定するのは s3api list-object-versions で確認できた VersionId ですが、今回は最新のものから数えて 3 番目にあたるバージョンのオブジェクトをダウンロードしてみます。対象のバージョン ID を次のようにして取得します。

$ TARGET_VERSION_ID=$(\
aws s3api list-object-versions \
--bucket ${S3_BUCKET_NAME} \
--prefix sample_file.txt \
--query 'reverse(sort_by(Versions,&LastModified))[2].VersionId' \
--output text) \
&& echo $TARGET_VERSION_ID
jfbONeI0pJ1h9unJgRrvzmIFBvUBa0xv

バージョン ID が取得できたので、そのバージョンを指定してオブジェクトをダウンロードします。3 番目ということで、オブジェクトの中身は This is a sample file. (v3) になっているはずなので、ダウンロード後に中身を出力するところまでやってみます。

$ aws s3api get-object \
--bucket ${S3_BUCKET_NAME} \
--key sample_file.txt \
--version-id ${TARGET_VERSION_ID} \
./out/sample_file_v3.txt > /dev/null \
&& cat ./out/sample_file_v3.txt
This is a sample file. (v3)

想定通りの内容でした。

3.6 S3オブジェクトの特定バージョンを削除

特定バージョンの削除には s3api delete-object--version-id オプションと合わせて使用します。

$ aws s3api delete-object help
...
SYNOPSIS
            delete-object
          --bucket <value>
          --key <value>
          [--mfa <value>]
          [--version-id <value>]
          [--request-payer <value>]
          [--bypass-governance-retention | --no-bypass-governance-retention]
          [--expected-bucket-owner <value>]
          [--cli-input-json | --cli-input-yaml]
          [--generate-cli-skeleton <value>]
          [--cli-auto-prompt <value>]

今回は、先ほど取得したバージョンを削除します。

$ aws s3api delete-object \
--bucket ${S3_BUCKET_NAME} \
--key sample_file.txt \
--version-id ${TARGET_VERSION_ID}
{
    "VersionId": "jfbONeI0pJ1h9unJgRrvzmIFBvUBa0xv"
}

続けて、あらためて最新のものから 3 番目にあたるバージョンのオブジェクトをダウンロードしてみます。今度は中身が This is a sample file. (v2) になるはずです。

$ TARGET_VERSION_ID=$(\
aws s3api list-object-versions \
--bucket ${S3_BUCKET_NAME} \
--prefix sample_file.txt \
--query 'reverse(sort_by(Versions,&LastModified))[2].VersionId' \
--output text) \
&& echo $TARGET_VERSION_ID
baLmE30tSBAuiZ8lhQ3.LcyAf3P2fv7N
$ aws s3api get-object \
--bucket ${S3_BUCKET_NAME} \
--key sample_file.txt \
--version-id ${TARGET_VERSION_ID} \
./out/sample_file_v2.txt > /dev/null \
&& cat ./out/sample_file_v2.txt
This is a sample file. (v2)

想定通りの内容でした。

知らんかったぞ、それ

今回に限らずハンズオンでは新しく知ることがたくさんあるので、この 知らんかったぞ、それ 項目では新しく知ったことを自分なりに深堀りしてみたいと思います。(恒例化したい)

というわけで、今回もいくつか取り上げてみます。

クレデンシャルファイルに任意のファイルを指定する

環境変数 AWS_SHARED_CREDENTIALS_FILE に任意のファイルを指定することで、 ~/.aws/credentials 以外のファイルをクレデンシャルファイルとして指定できます。

今回のようにハンズオン用のプロファイルを作成する際に ~/.aws/credentials を編集すると、ハンズオン後の掃除がしにくくなり、事故の原因にもなります。ハンズオン用のクレデンシャルファイルを作って AWS_SHARED_CREDENTIALS_FILE で指定すれば、 ~/.aws/credentials は汚れずに済むというわけです。

シェルの for 文

AWS CLI というかシェルの使い方の話ですが。

今回のハンズオンでは、シェルの for 文と CLI コマンドを組み合わせて、すべてのバージョンを削除していました。今までシェルの for 文を使ったことがなかったのと、 CLI コマンドとの組み合わせに感動しました。…と言っても自分で良い例が思いつかないので、ハンズオンと同じくすべてのバージョンを削除してみます。

削除前に、対象のバージョンの件数を確認しておきます。

$ aws s3api list-object-versions \
--bucket ${S3_BUCKET_NAME} \
--prefix sample_file.txt \
--query 'length(Versions)' \
--output text
4

for 文で削除します。

$ for i in $(
  aws s3api list-object-versions \
  --bucket ${S3_BUCKET_NAME} \
  --prefix sample_file.txt \
  --query 'Versions[].VersionId' \
  --output text
); do
  aws s3api delete-object \
  --bucket ${S3_BUCKET_NAME} \
  --key sample_file.txt \
  --version-id ${i}
done
{
    "VersionId": "PWdCD9sEmem67CqNHA2cBMUSQCY6t5Sv"
}
{
    "VersionId": "b4234JLABtdRGlQ9AGreIuSsqoaf964n"
}
{
    "VersionId": "baLmE30tSBAuiZ8lhQ3.LcyAf3P2fv7N"
}
{
    "VersionId": "sRfncD5dSroDAQYq4s3gAinHWQpQp4VW"
}

s3api list-object-versions コマンドの出力結果から --query オプションで各バージョンの VersionId を抜き出して、その値を s3api delete-object に渡しています。出力としては、 s3api delete-object で削除されたバージョンの VersionId が出力されます。

念のため削除後の件数を確認しておきます。

$ aws s3api list-object-versions \
--bucket ${S3_BUCKET_NAME} \
--prefix sample_file.txt \
--query 'length(Versions)' \
--output text

In function length(), invalid type for value: None, expected one of: ['string', 'array', 'object'], received: "null"

まとめ

JAWS-UG CLI 専門支部 #169R S3基礎 バージョニング の参加レポートでした。

S3 のバージョニングって、そういえば普段の業務や個人的な利用でも有効にしたことがなかったので、今回その挙動について試すことができてよかったです。S3 のバージョニングは差分を保持する形ではなく、別のオブジェクトとして保持するので、大量にバージョンを生成する際にはストレージコストに注意する必要がありそうです。

今回もシェルについて新たに for 文の知識を得たので、どんどん使っていってシェル芸を上達させたいと思います。


comments powered by Disqus