amareloのブログ(仮)

IT系勉強会で感じた想いや知見をメインに書いていきます。

JAWS-UG CLI専門支部 #154R S3入門 参加レポート

5/25 JAWS-UG CLI専門支部 #154R S3入門 に参加しましたので、レポートを書くのと同時に、ハンズオンでやったことの復習を書きます。

目次

イベントページ

jawsug-cli.connpass.com

今回のハンズオンでやったこと

以下の通りです。事前準備として、Cloud9上にハンズオン環境を構築しました。 ハンズオンでは、Cloud9のターミナルでコマンド実行します。

S3について

今回話に上がったポイントを以下に示します。

  • S3はオブジェクトストレージ
  • バケット」というオブジェクトコンテナにオブジェクト(ファイル)を格納する。
    • バケットは世界でユニーク(一意)である必要がある。
  • オブジェクトは、オブジェクトデータとメタデータで構成される。
  • オブジェクトは、バケット+キー+バージョンIDの組み合わせで、一意に識別される。
  • 耐久性はイレブンナイン(可用性は99.99%)
  • S3は結果整合性モデルが使われているため、データの更新と削除の場合は即時反映されない。
  • 仮想ホスティングとWebホスティングは違う。
    • 仮想ホスティングの場合は、ファイルフルパスでないとアクセスできない。
    • Webサイトとして公開したいならば、Webホスティングを設定すること。
  • グローバルサービスだが、バケット作成の際はリージョンを指定する必要がある。

また、開発者ガイドの「はじめに」を読むだけでも、S3への理解が深まるとのことです。

docs.aws.amazon.com

感想

この後、ハンズオンで実施した手順を自分の解釈も追記して書こうと思います。長くなるので、先に全体の感想を書きます。

SQSとSNSの時も参加しましたが、ブログには書きませんでした。しかし、CLIハンズオンガイドに、

  • 「完了すること」を最優先すること
  • 必ず復習すること
  • オリジナル手順をアウトプットすること

について書かれていました。今回からこの点に言及されたことにより、今回のハンズオンからはとにかく体感し、自分なりの解釈をインプットし、それをブログでアウトプットすることに努めようと思いました。

そのためか、余裕をもって完走出来ました。SQSとSNSの時も楽しかったものの、ついていくのがやっとでした。今回は、純粋にハンズオンは楽しいと思えて、前回以上に受講して良かったと思えました。

過去のSQS入門、SNS入門のハンズオンもやり直して、サービスの理解とコマンドの理解を深めたくなりました。

ハンズオンで実施した手順(再構成)

今回のハンズオンの手順は以下にあります。しかし、自分自身の理解のために、波田野さんの手順より細かく書こうと思います。くどいかもしれませんが、ご容赦ください。

※ハンズオン手順(波田野さん作成) prototype-handson-cli.s3-website-ap-northeast-1.amazonaws.com

事前準備

ハンズオンでは、IAMロールを作りIAMロール「AmazonS3FullAccess」を付与しました。これがないと、S3にバケットを作ることもオブジェクトを作ることもできません。

事前作業. Cloud9用ロールへのIAMポリシー追加 — ハンズオン(簡易版): S3入門 - ハイレベルコマンド活用

1.S3バケットの構築
①デフォルトリージョンの設定
  • AWSデフォルトリージョンを変数[AWS_DEFAULT_REGION]に格納します。
export AWS_DEFAULT_REGION='ap-northeast-1'
バケットプレフィックスを指定
S3_BUCKET_PREFIX='handson-cli-s3'
AWS_IDの取得と設定
  • AWS_IDを取得して変数[AWS_ID]に格納します。変数の値を出力して確認します。
AWS_ID=$( \
  aws sts get-caller-identity \
    --query 'Account' \
    --output text \
) \
  && echo ${AWS_ID}
バケット名設定
S3_BUCKET_NAME="${S3_BUCKET_PREFIX}-${AWS_ID}" \
  && echo ${S3_BUCKET_NAME}
⑤変数格納状況の確認
  • 変数が想定通りに格納されているか確認します。
  • ちゃんと格納されていれば、変数部分に想定している値が表示されます。
cat << END

  # AWS_DEFAULT_REGION:"ap-northeast-1"
    AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}"
  # S3_BUCKET_NAME:"handson-cli-s3-XXXXXXXXXXXX"
    S3_BUCKET_NAME="${S3_BUCKET_NAME}"

END
バケット構築

mb はバケット作成コマンドです。--region でバケットを作成するリージョンを指定します。

aws s3 mb s3://<バケット名> --region <リージョン>

変数が正しく格納されていることを確認したら、以下のコマンドを実行します。 - ①で指定したリージョンに、④で設定した変数のバケットを作成します。

aws s3 mb s3://${S3_BUCKET_NAME} \
  --region ${AWS_DEFAULT_REGION}
バケットの存在確認

aws s3 ls コマンドで、S3上に存在するバケットをリスト表示します。

  • S3バケットが存在することを確認するために、以下のコマンドを実行します。
aws s3 ls | grep ${S3_BUCKET_NAME}
2.S3オブジェクトの操作

バケット名変数[S3_BUCKET_NAME]の指定は省略します(1.②~④と同じため)。

(1)一時ファイル用ディレクトリの作成
①変数[DIR_TMP]にディレクトリを指定します。
DIR_TMP="${HOME}/environment/tmp-handson-cli-s3"
ディレクトリを作成します。
  • mkdirコマンドの- pオプション(--parentsオプション)は、ディレクトリがなくてもエラーを出さずにディレクトリを作成するオプションです。
mkdir -p ${HOME}/environment/tmp-handson-cli-s3
ディレクトリが存在することを確認します。
  • lsコマンドの -d オプション(--directory オプション)は、ディレクトリそのものの情報を表示するオプションです。
ls -d ${HOME}/environment/tmp-handson-cli-s3
④変数に正しい値がに格納させていることを確認します。
  • 合っていれば、正しいパラメータ値に値が入っています。
cat << END

  # 1. S3_BUCKET_NAME:"handson-cli-s3-XXXXXXXXXXXX"
       S3_BUCKET_NAME="${S3_BUCKET_NAME}"
  # 2. DIR_TMP:"${HOME}/environment/tmp-handson-cli-s3"
       DIR_TMP="${DIR_TMP}"

END
(2)ファイルアップロード
①一時ファイル用ディレクトリに移動します。
cd ${DIR_TMP}
②S3バケットにオブジェクトが存在しないことを確認します。

`aws s3 ls s3://<バケット名>/' でバケット内のオブジェクトをリスト表示します。

  • 以下の通りコマンド実行します。この時点では結果に何も出力されません。
aws s3 ls s3://${S3_BUCKET_NAME}/
③変数[S3_OBJECT_NAME]にアップロード用ファイル名を設定します。
S3_OBJECT_NAME='upload.txt'
④変数[FILE_UPLOAD]にファイルアップロード用フォルダパスを格納します。
FILE_UPLOAD="${DIR_TMP}/${S3_OBJECT_NAME}"  && echo ${FILE_UPLOAD}
⑤アップロード用のファイルを作成します。
  • touch コマンドは、ファイルのタイムスタンプを変更するコマンドですが、ファイルが存在しない場合は、空(0バイト)のファイルを作成します。
touch ${FILE_UPLOAD}
⑥作成したファイルをS3にアップロードします。

ローカルディレクトリからS3バケットにオブジェクト(ファイル)をアップロードする場合は、cpコマンドを使います。

aws s3 cp <ローカルディレクトリ名> s3://<バケット名>

以下の通りコマンドを実行して、⑤で作成したファイルをアップロードします。

aws s3 cp ${FILE_UPLOAD} s3://${S3_BUCKET_NAME}/
⑦S3バケットにファイルが作成(アップロード)されたことを確認します。

※コマンドだけでなく、マネジメントコンソールからもファイルが作成されたか確認します。

aws s3 ls s3://${S3_BUCKET_NAME}/
(3)オブジェクトダウンロード
①変数[S3_OBJECT_NAME]にダウンロードするS3オブジェクトを指定します。
S3_OBJECT_NAME='upload.txt'
②変数[FILE_DOWNLOAD]にダウンロードファイルのローカル保存名を指定します。
FILE_DOWNLOAD="${DIR_TMP}/download.txt" && echo ${FILE_DOWNLOAD}
③S3オブジェクトをダウンロードします。
  • オブジェクトをダウンロードする際も、cpコマンドを使います。
aws s3 cp S3://<バケット名>/<オブジェクト名> <ダウンロード先ディレクトリ>
  • 以下のコマンドを実行して、ファイルをダウンロードします。
aws s3 cp s3://${S3_BUCKET_NAME}/${S3_OBJECT_NAME} ${FILE_DOWNLOAD}
④ダウンロードしたファイルが存在することを確認します。
ls ${FILE_DOWNLOAD}
(4)S3オブジェクトの削除
①S3オブジェクトを削除します。
  • オブジェクトを削除する際は、rmコマンドを使います。
aws s3 rm s3://<バケット名>/<オブジェクト名>
  • 以下のコマンドを実行して、ファイルを削除します。
aws s3 rm s3://${S3_BUCKET_NAME}/${S3_OBJECT_NAME}
  • ファイルが削除されたことを以下のコマンド実行、またはマネジメントコンソールより確認します。
aws s3 ls s3://${S3_BUCKET_NAME}/
(5)ファイルのアップロード(バケット/ディレクトリ)
①変数[S3_OBJECT_PREFIX]に、ファイルのアップロード先ディレクトリを指定します。
S3_OBJECT_PREFIX='dir1'
②S3バケットにアップロードします。
  • 以下のコマンドを実行した際、S3_OBJECT_PREFIXで指定したフォルダがなければ、作成されます。
aws s3 cp ${FILE_UPLOAD} s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}/
(6)S3オブジェクトの移行
①移動元のS3バケット名とS3オブジェクトパス名を指定します。
  • S3オブジェクトを移動させるためには、mvコマンドを使います。
aws s3 mv <移動元オブジェクト名> <移動先オブジェクト名>
  • 以下のコマンドを実行して、移動元オブジェクトを指定します。
S3_BUCKET_NAME_SOURCE="${S3_BUCKET_NAME}"
S3_OBJECT_PREFIX_SOURCE='dir1/upload.txt'
②移動先のS3バケット名とS3オブジェクトパスを指定します。
S3_BUCKET_NAME_DESTINATION="${S3_BUCKET_NAME}"
S3_OBJECT_PREFIX_DESTINATION='dir2/moved.txt'
③以下のコマンドでS3オブジェクトを移動します。
aws s3 mv s3://${S3_BUCKET_NAME_SOURCE}/${S3_OBJECT_PREFIX_SOURCE} \
  s3://${S3_BUCKET_NAME_DESTINATION}/${S3_OBJECT_PREFIX_DESTINATION}
④移動元にS3オブジェクトが存在しないことを確認します。
! aws s3 ls s3://${S3_BUCKET_NAME_SOURCE}/${S3_OBJECT_PREFIX_SOURCE}
⑤移動先にS3オブジェクトが存在することを確認します。
aws s3 ls s3://${S3_BUCKET_NAME_DESTINATION}/${S3_OBJECT_PREFIX_DESTINATION}
(6)S3オブジェクトのパス単位削除
①変数[S3_OBJECT_PREFIX]に、削除するS3ディレクトリ名を格納します。
S3_OBJECT_PREFIX=dir2
②削除前に、削除するディレクトリが存在することを確認します。
aws s3 ls s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}
③以下のコマンドで、ディレクトリ配下のオブジェクトを含めてすべて削除します。
  • recursiveオプションをつけることで、再帰的にディレクトリ内のファイルを含めて削除します。
aws s3 rm \
  --recursive \
    s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}
④削除したディレクトリが存在しないことを確認します。何も表示されなければ完了です。
! aws s3 ls s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}
3.Gitリポジトリ取得~S3バケットへの転送

今回は事前に用意されていたGitリポジトリからコンテンツを取得しました。これは4.で使うものです。

①Gitリポジトリの取得元を指定します。
GIT_REPOSITORY_ORIGIN='https://github.com/opelab/jawsug-cli-sample-web.git'
リポジトリ保存用ディレクトリを指定します。
DIR_PARENT="${HOME}/environment/contents-handson-cli-s3"
リポジトリ保存用ディレクトリを作成します。
mkdir -p ${HOME}/environment/contents-handson-cli-s3
ディレクトリが作成されたことを確認します。
ls -d ${HOME}/environment/contents-handson-cli-s3
⑤Gitリポジトリディレクトリ名を指定します。
GIT_REPOSITORY_NAME='jawsug-cli-sample-web'
⑥Gitリポジトリを取得します。
cd ${DIR_PARENT} \
  && git clone ${GIT_REPOSITORY_ORIGIN}
⑦Gitリポジトリディレクトリが存在することを確認します。
ls -d ${DIR_PARENT}/${GIT_REPOSITORY_NAME}
⑧転送ファイル用ディレクトリを指定します。
DIR_S3_TRANSFER="${HOME}/environment/contents-handson-cli-s3/jawsug-cli-sample-web"
⑨転送除外オブジェクトを指定します(gitignoreなどgitを含むファイルとします)。
S3_SYNC_EXCLUDE=".git*"
⑩S3バケット上のディレクトリに転送します。
  • S3バケットとローカルを同期させたい場合は、syncコマンドを使います。
  • exclude オプションで動機対象外のオブジェクトを指定、aclオプションはアクセス公開範囲を指定します。
aws s3 sync <ローカルファイルのディレクトリ> s3://<バケット名> --exclude <同期対象外オブジェクト> --acl <公開範囲>
  • 以下のコマンドでオブジェクトを転送します。
dir_current=$(pwd)
cd ${DIR_S3_TRANSFER} \
  && aws s3 sync . "s3://${S3_BUCKET_NAME}/" \
    --exclude "${S3_SYNC_EXCLUDE}" \
    --acl public-read \
  && cd ${dir_current}
⑪S3バケットに転送オブジェクトが存在することを確認します。
aws s3 ls s3://${S3_BUCKET_NAME}/

結果、以下のように表示されます(例)。

2020-05-25 01:23:45        186 error.html
2020-05-25 01:23:45      55560 img.jpg
2020-05-25 01:23:45        255 index.html
⑫仮想ホスティングエンドポイントを取得します。
S3_BUCKET_ENDPOINT=" \
  ${S3_BUCKET_NAME}.s3.$( \
    aws s3api get-bucket-location \
      --bucket ${S3_BUCKET_NAME} \
      --output text \
  ).amazonaws.com" \
  && echo ${S3_BUCKET_ENDPOINT}
⑬オブジェクトにアクセスできることを確認します。
  • 変数[S3_OBJECT_NAME]にオブジェクト名を指定します。
S3_OBJECT_NAME='img.jpg'
  • オブジェクトURLを出力し、表示されたURLにアクセスできることを確認します。
URL_S3_OBJECT="${S3_BUCKET_ENDPOINT}/${S3_OBJECT_NAME}" \
  && echo ${URL_S3_OBJECT}
4.webサイトホスティングの設定

バケット名変数[S3_BUCKET_NAME]の指定は省略します(1.②~④と同じため)。

①インデックスドキュメント名を指定します。
S3_DOC_INDEX='index.html'
②エラードキュメント名を指定します。
S3_DOC_ERROR='error.html'
③Webサイトホスティング設定をします。
aws s3 website "s3://<バケット名>" --index-document <indexファイル名> --error-document <エラーページファイル名>
aws s3 website "s3://${S3_BUCKET_NAME}" \
  --index-document ${S3_DOC_INDEX} \
  --error-document ${S3_DOC_ERROR}
④Webサイトエンドポイントにアクセスできるか確認します。
  • 以下のコマンドを実行後、出力されたURLにアクセスできることを確認します。
S3_BUCKET_WEBSITE_ENDPOINT=" \
  ${S3_BUCKET_NAME}.s3-website-$( \
    aws s3api get-bucket-location \
      --bucket ${S3_BUCKET_NAME} \
      --output text \
  ).amazonaws.com" \
  && echo ${S3_BUCKET_WEBSITE_ENDPOINT}
⑤エラードキュメントにアクセスできるか確認します。
  • ④で出力されたURL末尾に適当な文字(/z/ など)を加えて実行します。
  • エラー画面が表示されることを確認します。
5.署名付きURLの利用

バケット名変数[S3_BUCKET_NAME]の指定は省略します(1.②~④と同じため)。

①署名対象S3オフジェクトを指定します(「img.jpg」とした場合)。
S3_OBJECT_NAME='img.jpg'
②署名付きURLの有効期限(秒)を指定します。
S3_PRESIGN_SECONDS='120'
③オブジェクトの署名付きURLを作成します。
  • 署名付きURLを発行するには、presign コマンドを使います。
aws s3 presign s3://<バケット名>/<オブジェクト名> --expires-in <有効期限(秒)>
  • 以下のコマンドを実行して、署名付きURLを発行します。
aws s3 presign s3://${S3_BUCKET_NAME}/${S3_OBJECT_NAME} \
  --expires-in ${S3_PRESIGN_SECONDS}
④結果に表示されたURLにアクセスできることを確認します。
⑤指定した有効期限が過ぎたら、URLにアクセスできなくなったことを確認します。
  • 「This XML file does not appear to have any style information associated with it. The document tree is shown below.」と表示されます。
6.後始末

使わないS3バケット、ハンズオン用ディレクトリを削除します。また、Cloud9ロールからS3フルアクセスポリシーをデタッチします。

後始末手順は以下の通りです。自分はこのブログを書いたり復習するために残していますので、まだ実施していません。

後始末1. S3バケットの破棄

後始末2. ハンズオン用ディレクトリの削除

事後作業. Cloud9用ロールからのポリシーのデタッチ

※Cloud9環境も不要ならば、事前準備の5以降(後始末も含みます)を実施します。

以上で終了です。

最後に

ハンズオンの復習をすることで、CLIについて、S3の動きについて、理解を深められたと実感できました。 今後のハンズオンは、必ず復習ありきで臨もうと思います。できる限りブログも書こうと思います。 かなり長いブログになってしまいましたが、最後まで読んでいただいた方は本当にありがとうございました!