michimani.net

[レポート] JAWS-UG CLI 専門支部 #172R S3基礎 (オブジェクト) に参加しました #jawsug_cli

2020-11-26

JAWS-UG CLI 専門支部 #172R S3基礎 (オブジェクト) に参加したので、そのレポートです。

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

これまでの CLI 専門支部参加レポートはこちら。

目次

S3 の全体像、オブジェクトの操作

2020/11/26 時点での AWS CLI のバージョン

これまでにもレポートを書いてきましたが、その当時の AWS CLI バージョンを書いておこうと思います。今回のハンズオンがあった 2020/11/26 時点での v1 および v2 それぞれの最新バージョンは下記のとおりでした。

今回は特に最新の機能を使うわけではないので、 v2 を使用していきます。ここ数日は re:Invent 前のアップデートが怒涛の勢いで出てきていますが、新しい API に関しては (後に v2 でも使えるものの) v1 でのみ対応している場合があるので、最新の機能を CLI で試す場合には v1 を使ったほうがよさそうです。

ハンズオン

今回のハンズオンでは、新たに作成した S3 バケットに対してオブジェクトを追加し、そのオブジェクトに対してコピーや属性の更新などを実行した後、削除するところまでを CLI で操作しました。

今回も、ハンズオンの詳細な手順についてはイベントページの資料におまかせするとして、手順の中で気になったところを抜粋して確認してみます。

2.2 S3 オブジェクトの新規アップロード

オブジェクトのアップロードには s3api put-object コマンドを使います。オプションはたくさんありますが、必須なのは --bucket--key のみです。

$ aws s3api put-object help
...
SYNOPSIS
            put-object
          [--acl <value>]
          [--body <value>]
          --bucket <value>
          [--cache-control <value>]
          [--content-disposition <value>]
          [--content-encoding <value>]
          [--content-language <value>]
          [--content-length <value>]
          [--content-md5 <value>]
          [--content-type <value>]
          [--expires <value>]
          [--grant-full-control <value>]
          [--grant-read <value>]
          [--grant-read-acp <value>]
          [--grant-write-acp <value>]
          --key <value>
          [--metadata <value>]
          [--server-side-encryption <value>]
          [--storage-class <value>]
          [--website-redirect-location <value>]
          [--sse-customer-algorithm <value>]
          [--sse-customer-key <value>]
          [--sse-customer-key-md5 <value>]
          [--ssekms-key-id <value>]
          [--ssekms-encryption-context <value>]
          [--request-payer <value>]
          [--tagging <value>]
          [--object-lock-mode <value>]
          [--object-lock-retain-until-date <value>]
          [--object-lock-legal-hold-status <value>]
          [--expected-bucket-owner <value>]
          [--cli-input-json | --cli-input-yaml]
          [--generate-cli-skeleton <value>]

各オプションの詳細については下記の公式ドキュメントを参照してください。

ここでは新たにオブジェクトをアップロードするので、 --body オプションでローカルにあるファイルを指定します。

$ cat alpha.txt

This is A text.

$ aws s3api put-object \
--bucket ${S3_BUCKET_NAME} \
--key alpha/message_a.txt \
--body ./alpha.txt

{
    "ETag": "\"75721060f35a9f9f6bdaf9985394e9a8\""
}

同様に

$ cat beta.txt

This is B text.

$ aws s3api put-object
–bucket ${S3_BUCKET_NAME}
–key beta/message_b.txt
–body ./beta.txt

{ “ETag”: “"424e9ac2402771285730c91435c5a23d"” }

正しくアップロードできたか確認します。ここでは簡潔に s3 ls コマンドで確認します。

$ aws s3 ls s3://${S3_BUCKET_NAME}/alpha/

2020-11-26 22:49:09         16 message_a.txt

$ aws s3 ls s3://${S3_BUCKET_NAME}/beta/

2020-11-26 22:49:17         17 message_b.txt

2.4 S3 バケットのオブジェクト一覧取得

S3 バケット内のオブジェクト一覧を取得するコマンドとしては list-objectslist-objects-v2 が存在しますが、ドキュメントでは Warning として次のように書かれています。

This API has been revised. We recommend that you use the newer version, ListObjectsV2 , when developing applications. For backward compatibility, Amazon S3 continues to support ListObjects .

list-objects — AWS CLI 2.1.4 Command Reference

ということで、 list-objects-v2 を使うようにしましょう。

$ aws s3api list-objects-v2 \
--bucket ${S3_BUCKET_NAME}

{
    "Contents": [
        {
            "Key": "alpha/message_a.txt",
            "LastModified": "2020-11-26T13:49:09+00:00",
            "ETag": "\"75721060f35a9f9f6bdaf9985394e9a8\"",
            "Size": 16,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "beta/message_b.txt",
            "LastModified": "2020-11-26T13:49:17+00:00",
            "ETag": "\"424e9ac2402771285730c91435c5a23d\"",
            "Size": 17,
            "StorageClass": "STANDARD"
        }
    ]
}

2.7 S3 オブジェクトの更新

S3 オブジェクトの更新には、新規アップロードの時と同じ put-object コマンドを使います。先程も書いたように、必須のオプションは --bucket--key です。オブジェクト自体を更新しない場合は --body の指定は不要で、更新したい属性に対応するオプションを指定すれば OK です。例えば、オブジェクトにメタデータを追加する場合は --metadata オプションを使って下記のように実行します。

$ aws s3api put-object \
--bucket ${S3_BUCKET_NAME} \
--key alpha/message_a.txt \
--metadata secretLevel=1,Category="sample text"

{
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}

確認してみます。

$ aws s3api head-object \
--bucket ${S3_BUCKET_NAME} \
--key alpha/message_a.txt

{
    "AcceptRanges": "bytes",
    "LastModified": "2020-11-26T13:54:44+00:00",
    "ContentLength": 0,
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "Metadata": {
        "category": "sample text",
        "secretlevel": "1"
    }
}

ここで注意したいのが、メタデータのキーは大文字/小文字が区別されないことです。 put-object コマンドでは secretLevelCategory を指定しましたが、実際には categorysecretlevel が設定されています。 ただし、 SDK for Go でメタデータにアクセスする際、レスポンスのキー名はキャメルケースになるのが個人的にはハマりポイントかなと思います。

メタデータ以外の例として、ストレージクラスを変更してみます。

$ aws s3api put-object \
--bucket ${S3_BUCKET_NAME} \
--key beta/message_b.txt \
--storage-class ONEZONE_IA

{
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}

確認しておきます。

$ aws s3api list-objects-v2 \
--bucket ${S3_BUCKET_NAME}

{
    "Contents": [
        {
            "Key": "alpha/message_a.txt",
            "LastModified": "2020-11-26T13:58:42+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "STANDARD"
        },
        {
            "Key": "beta/message_b.txt",
            "LastModified": "2020-11-26T14:08:24+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "ONEZONE_IA"
        }
    ]
}

知らんかったぞ、それ

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

というわけで、前回と前々回はこのコーナーがなかったのですが、今回はいくつか取り上げてみます。

S3 バケットのロケーション (リージョン) だけ取得する

かなりの小ネタっぽい内容ですが、対象のバケットのリージョンだけを取得するコマンド (API) が存在していることを初めて知りました。

$ aws s3api get-bucket-location \
--bucket ${S3_BUCKET_NAME}

{
    "LocationConstraint": "ap-northeast-1"
}

head-object でストレージクラスの確認

オブジェクトのストレージクラスを確認するには list-object-v2 コマンドを使うしか無いと思っていたのですが、 head-object コマンドでも取得できるようでした。ただし条件があります。まずは beta/message_b.txt の情報を取得してみます。

$ aws s3api head-object \
--bucket ${S3_BUCKET_NAME} \
--key beta/message_b.txt

{
    "AcceptRanges": "bytes",
    "LastModified": "2020-11-26T14:08:24+00:00",
    "ContentLength": 0,
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "Metadata": {},
    "StorageClass": "ONEZONE_IA"
}

取得できてますね。では alpha/message_a.txt も取得してみます。

$ aws s3api head-object \
--bucket ${S3_BUCKET_NAME} \
--key alpha/message_a.txt
{
    "AcceptRanges": "bytes",
    "LastModified": "2020-11-26T13:58:42+00:00",
    "ContentLength": 0,
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
    "ContentType": "binary/octet-stream",
    "Metadata": {
        "category": "sample text",
        "secretlevel": "1"
    }
}

はい。 StorageClass の属性がありません。
これはどういうことかというと、どうやらストレージクラスが S3 標準 (STANDARD) の場合は head-object のレスポンス内に情報が含まれないようです。

個人的には、 head-object で取得できないと思っていたので嬉しい誤算でしたが、基本的に扱うのは標準クラスのオブジェクトなので、やっぱりちょっと残念だなという思いです。

delete-object と delete-objects

S3 オブジェクトを削除するコマンドとして delete-objectdelete-objects が存在しています。コマンド名だけ見ると delete-objects のほうは複数のオブジェクトをまとめて削除できそうなので便利かなと思うんですが、実際は対象のオブジェクトをすべて列挙する必要があるため逆に使いづらいようです。

$ aws s3api delete-objects help
...
OPTIONS
       ...
       --delete (structure)
          Container for the request.

          Objects -> (list)
              The objects to delete.

              (structure)
                 Object Identifier is unique value to identify objects.

                 Key -> (string)
                     Key name of the object to delete.

                 VersionId -> (string)
                     VersionId for the  specific  version  of  the  object  to
                     delete.

          Quiet -> (boolean)
              Element  to enable quiet mode for the request. When you add this
              element, you must set its value to true.

       Shorthand Syntax:

          Objects=[{Key=string,VersionId=string},{Key=string,VersionId=string}],Quiet=boolean

       JSON Syntax:

          {
            "Objects": [
              {
                "Key": "string",
                "VersionId": "string"
              }
              ...
            ],
            "Quiet": true|false
          }

削除対象は --delete オプションで指定するのですが、 Shorthand Syntax で指定するにしても

Objects=[{Key=string,VersionId=string},{Key=string,VersionId=string}],Quiet=boolean

のような形で指定する必要があり、この文字列を生成するのはなかなか面倒です。

であれば、削除対象のオブジェクトを list-objects-v2 コマンドで取得して、それらに対して delete-object を使ったほうが楽そうです。実際には次のような形で実行します。

$ for i in $(
  aws s3api list-objects-v2 \
  --bucket ${S3_BUCKET_NAME} \
  --query 'Contents[].Key' \
  --output text
); do
  aws s3api delete-object \
  --bucket ${S3_BUCKET_NAME} \
  --key ${i}
done

これで list-objects-v2 で取得できるオブジェクトをすべて削除することができます。

まとめ

JAWS-UG CLI 専門支部 #172R S3基礎 (オブジェクト) に参加したので、そのレポートでした。

今回はオブジェクトに対する操作を、ハイレベルコマンドの aws s3 ではなく API と対になっている aws s3api で実行しました。 s3 コマンドでは cpls といったサブコマンドで操作できるので、単にオブジェクトの移動や削除を行う際には非常に簡単です。一方 s3api コマンドでは、 API と対になったサブコマンドを使用して操作するため、 JSON 形式で生の情報を取得することができます。そのため、実行結果から必要な部分を --query で抜き出したり、他のコマンドに渡したりしやすくなります。
それ以前に、やはり API ベースで何が行われているのかがわかるのが s3api コマンドの良いところだと思います。

次回のレプリケーションで S3 については一旦区切りかなと思いますが、だいぶ S3 に関する知識がついてきたような気がします。そんな中で感じるのが、やっぱり S3 は Simple じゃないってことですね。


comments powered by Disqus