AWS関連

【AWS】CLIを使ってS3操作

今回は『AWS-CLIでS3を触ってみる!JAWS-UG CLI専門支部のハンズオン』に参加した学びについて整理します。

今回参加したハンズオンについて

JAWS-UG CLI専門支部とは?

Japan Amazon Web ServicesのユーザグループのCLI(コマンドラインインタフェース)専門支部。 「AWS CLIを使ってAWSを乗りこなそう!」ということで日々勉強会を開催されているようです。 今回参加した会を含め、現在はコロナの影響でオンラインハンズオンの形式で開催されています。

主催者の波田野さん

オンラインハンズオンは、波田野さん(@tcsh)が主催されており、ハンズオン内容はもちろんですが、余談のお話にも学びが多くてビビります。AWSに興味がある私としてはずっと聞いていられます(笑)。 また、ハンズオン手順書がめちゃくちゃ洗練されていて(しかもsphinx というツールで自動化されているとか。)、コミュニケーション能力というか、テキストコミュニケーション能力と言うか、ハンズオンコミュニケーション能力というか、高すぎて正直ビビります。(つまり手順書がわかりやすいということ) 主催していただき感謝しかないです。

CLIハンズオンガイド

ハンズオンにおける共通資料として、CLIハンズオンガイドというものが設置されています。ハンズオン手順書に関する説明や、学習におけるポイントなどが記載されているのですが、一番重要な点としては以下。

必ず復習すること。ハンズオンによる学習で最も重要なのは、この「復習」であり、復習をやる人とやらない人の差はとてつもなく大きいです。

 

波田野さんがそうおっしゃっているので、素直に復習することにします。

参加すべきconnpassグループ

JAWS-UG CLI専門支部connpassグループは「こちら」。 AWS勉強している方(特に私のような初心者)は秒で参加しましょう。

まとめ

各コマンドに関する細かい学びについては「学びメモ」として記載しています。 ハンズオンへの参加と復習、全体を通して良かった点としては、

  • CLIでS3の基本的な操作ができるようになった
  • マネジメントコンソールと同等の操作がCLIで可能であることを確認できた
  • このハンズオン有益すぎ+復習で理解深まった

逆に反省点・今後の課題としては、

  • 復習するの遅すぎ(次回以降はがんばろう)
  • 実務の作業でも「CLIでの実行方法」を意識していこう

といったところです。

ハンズオン概要

 

使用したコマンド一覧は以下の通り。
※実際のハンズオンで使用した資料はハンズオン(簡易版): S3入門 をご確認ください。

  • 1.S3バケットの構築
    • s3 mb(S3バケット構築)
    • s3 ls(S3バケットの存在確認)
  • 2.S3オブジェクトの操作全般
    • s3 ls (S3オブジェクト不存在の確認)
    • s3 cp (アップロード: ローカル -> S3バケット)
    • s3 cp (ダウンロード: S3バケット -> ローカル)
    • s3 rm (S3オブジェクトの削除)
    • s3 cp (アップロード: ローカル -> S3バケット/パス)
    • s3 mv (S3オブジェクトの移動)
    • s3 rm (S3オブジェクトをパス単位で削除)
  • 3.S3を仮想ホスティングとして使う
    • s3 sync(S3バケットへのオブジェクト転送)
    • s3api get-bucket-location(エンドポイントを取得)
  • 4.S3をWebサイトホスティングとして使う
    • s3 website(Webサイトホスティングの設定)
  • 5.S3で署名付きURLを使う
    • s3 presign(署名付きURLの作成)
  • 6.S3バケットの破棄
    • s3 rb(s3バケットの破棄)

(準備) 作業環境

Cloud9環境を準備

ハンズオンではCloud9環境のターミナルをCLI実行に使用するため、事前に作成が必要。

*学びメモ

  • AWS-CLIを使うにあたり、タブ補完の設定はマスト。
    • AWS-CLIの機能コンプリーターがシェルに対応しているとのこと。
    • ~/.bashrc に以下を追記。
    complete -C aws_completer aws

 

ロールへのIAMポリシー追加

ハンズオンで利用するCloud9環境に対して、権限を付与します。 Cloud9環境にアタッチされているIAMロールに対して、S3リソースを操作するためのIAMポリシーのアタッチを行います。

操作は簡単。※ここはマネジメントコンソールから実施。

IAM> 左ペインで”ロール”をクリック> Cloud9用に作成したロールを選択> ”ポリシーをアタッチします”をクリック> "AmazonS3FullAccess"をチェックして”ポリシーのアタッチ”をクリック

1.S3バケットの構築

s3 mb(S3バケットの構築)

◎事前準備

【リージョン名の指定】
export AWS_DEFAULT_REGION='ap-northeast-1'
【S3バケット名の指定】(プレフィックスの設定)
S3_BUCKET_PREFIX='handson-cli-s3'
【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}

◎バケット構築

【バケットを構築】
aws s3 mb s3://${S3_BUCKET_NAME} \
--region ${AWS_DEFAULT_REGION}
【作成したバケットが存在することを確認】
aws s3 ls | grep ${S3_BUCKET_NAME}

※マネジメントコンソールの表示確認

*学びメモ

  • mbコマンドにてS3バケットの作成が可能
  • S3はオブジェクトストレージである
  • リージョンの指定は明示的に行ったほうがよい(基本はデフォルトリージョンが使われる)
  • バケット命名規則のオススメは「バケット名+AWS ID」
    • バケット名は世界中で一意にする必要がある
    • 米国政府、中国政府は別の括りになっている
  • プレフィックスとは、接頭辞のこと

 

2.S3オブジェクトの操作

ローカル環境での準備

【一時ファイル用ディレクトリを指定】
DIR_TMP="${HOME}/environment/tmp-handson-cli-s3"

【ローカル環境にてディレクトリ作成】
mkdir -p ${DIR_TMP}

【一時ファイル用ディレクトリに移動】
cd ${DIR_TMP}
pwd を実行して以下の通り出力されたらOK /home/ec2-user/environment/tmp-handson-cli-s3

s3 ls (S3オブジェクト不存在の確認)

現状の確認として、S3バケット内にオブジェクトが存在しないことを確認する

【S3バケットの中身を確認(何も出力されない)】
aws s3 ls s3://${S3_BUCKET_NAME}/

※マネジメントコンソールの表示確認

s3 cp (アップロード: ローカル -> S3バケット)

ローカル環境でファイルを作成し、S3バケットにアップロード(コピー)する

◎事前準備

【オブジェクト名の指定】
S3_OBJECT_NAME='upload.txt'

【アップロードファイルパスを設定】
FILE_UPLOAD="${DIR_TMP}/${S3_OBJECT_NAME}" \
  && echo ${FILE_UPLOAD}

【アップロード用ファイルを作成】
touch ${FILE_UPLOAD}
ls を実行して以下の通り出力されたらOK upload.txt

◎S3バケットへのアップロード

【ローカル環境で作成したファイルをS3バケットにアップロード】
aws s3 cp ${FILE_UPLOAD} s3://${S3_BUCKET_NAME}/
【S3バケットの中身を確認】
aws s3 ls s3://${S3_BUCKET_NAME}/

※マネジメントコンソールの表示確認

*学びメモ

  • cpコマンドでローカルからS3バケットへのアップロード(コピー)が可能
  • 例:aws s3 cp <コピー元ファイルのパス> S3://<S3バケット名>
  • CLIでバケットを指定するときは、先頭に「s3://」をつける

s3 cp (ダウンロード: S3バケット -> ローカル)

先ほどアップロードしたファイルをローカル環境にダウンロードする

◎事前準備

【ダウンロードするファイルのローカル環境でのファイル名を指定】
FILE_DOWNLOAD="${DIR_TMP}/download.txt"

◎S3バケットからのダウンロード

【先ほどアップロードしたファイルを別名でローカル環境にダウンロード】
aws s3 cp s3://${S3_BUCKET_NAME}/${S3_OBJECT_NAME} ${FILE_DOWNLOAD}

【ローカル環境にてダウンロードが成功していることを確認(ファイルの存在確認)】
ls ${FILE_DOWNLOAD}

ローカル環境にdownload.txt が存在することを確認できたらOK

*学びメモ

  • cpコマンドでS3バケットからローカルへのダウンロード(コピー)が可能
  • 例:aws s3 cp S3://<S3バケット名>/<オブジェクト名> <保存するファイルのパス>
  • S3バケット内のオブジェクトを再帰的にダウンロード(コピー)する場合は、recursive オプションを使う(以下参照)

「s3バケットごとダウンロードすることも可能なの?」という疑問が湧いたので、下記操作①〜④を実行してみた。 適当に実行した①〜③は失敗に終わったが、調べると④の方法で実行可能であることがわかった。

①cp コマンドでバケット名とローカルディレクトリ名を指定して実行してみる(→何も起こらなかった)
# ダウンロード先のディレクトリ名を指定
DIR_DOWNLOAD="${HOME}/${S3_BUCKET_NAME}_download"
# s3バケットを別名でダウンロード
aws s3 cp s3://${S3_BUCKET_NAME} ${DIR_DOWNLOAD}
# バケットがディレクトリとしてダウンロードできているか確認(ディレクトリが存在しない:失敗)
ls ${HOME}

②ローカルディレクトリは事前作成が必要?と思い手動で作成しリトライ(→①同様、何も起こらない)
# ダウンロード先のディレクトリを手動で作成
mkdir ${DIR_DOWNLOAD}
# 改めてs3バケットをダウンロード
aws s3 cp s3://${S3_BUCKET_NAME} ${DIR_DOWNLOAD}
# ダウンロードできているか確認(できてない:失敗)
ls ${DIR_DOWNLOAD}

③バケット名を指定する場合"/"で終わる必要り?と思い下記コマンドでリトライ(→薄々気づいてはいたが、もちろん何も起こらない)
# バケット指定の最後に"/"を付けて実行
aws s3 cp s3://${S3_BUCKET_NAME}/ ${DIR_DOWNLOAD}
# ダウンロードできているか確認(できてない:失敗)
ls ${DIR_DOWNLOAD}

④調べるとrecursive オプションを付けることで再帰的にcp を実行してくれる模様(→成功)
# 先ほど作成したダウンロード用ディレクトリを削除
rm -r ${DIR_DOWNLOAD}
# recursive オプションありで実行
aws s3 cp s3://${S3_BUCKET_NAME}/ ${DIR_DOWNLOAD} --recursive
# ダウンロードできているか確認(ダウンロードできている:成功)
ls ${DIR_DOWNLOAD}
# 片付け忘れるので、ディレクトリ削除
rm -r ${DIR_DOWNLOAD}

s3 rm (S3オブジェクトの削除)

S3バケットにアップロードしたS3オブジェクト(upload.txt)を削除する

◎S3バケット上のS3オブジェクトを削除

【S3オブジェクトを指定して削除】
aws s3 rm s3://${S3_BUCKET_NAME}/${S3_OBJECT_NAME}
【S3バケットの中身を確認】
aws s3 ls s3://${S3_BUCKET_NAME}/

※マネジメントコンソールの表示確認

s3 cp (アップロード: ローカル -> S3バケット/パス)

ローカル環境に作成したファイルをS3バケットに対して”パスを指定して”アップロード(コピー)する

◎事前準備

【ファイルアップロード先となるS3オブジェクトパス名を設定】
S3_OBJECT_PREFIX='dir1'

◎S3バケットへのアップロード

【ローカル環境で作成したファイルをS3バケットに(パスを指定して)アップロード】
aws s3 cp ${FILE_UPLOAD} s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}/
【S3バケットの中身を確認】
aws s3 ls s3://${S3_BUCKET_NAME}/

※マネジメントコンソールの表示確認(”dir1/upload.txt” の存在を確認)

*学びメモ

  • cpコマンドでローカルからS3バケットへのアップロード(コピー)する際、オブジェクト指定が可能
  • 例:aws s3 cp <コピー元ファイルのパス> S3://<S3オブジェクトパス名>

s3 mv (S3オブジェクトの移動)

S3バケット内でS3オブジェクトのパスを変更する(ディレクトリを移動させたように見える)

◎事前準備

【移動元のS3バケット名とS3オブジェクトパス名を指定】
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オブジェクトの移動

【S3バケット内で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オブジェクトが存在することを確認(”moved.txt”が出力される)】
aws s3 ls s3://${S3_BUCKET_NAME_DESTINATION}/${S3_OBJECT_PREFIX_DESTINATION}
【S3バケットの中身を確認(”dir2”ディレクトリのみが存在する)】
aws s3 ls s3://${S3_BUCKET_NAME}/

※マネジメントコンソールの表示確認(”dir2/moved.txt” の存在を確認)

*学びメモ

  • mvコマンドでS3バケット内の移動が可能
  • S3バケット内のファイル移動なので、ローカル環境を経由することはない

s3 rm (S3オブジェクトをパス単位で削除)

パスを指定してS3オブジェクトを削除する

◎事前準備

【削除するS3オブジェクトパスを指定】
S3_OBJECT_PREFIX='dir2'

【削除対象のS3オブジェクトが存在することを確認】
aws s3 ls s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}

◎S3オブジェクトの削除

【S3オブジェクトを削除】
aws s3 rm \
--recursive s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}
【削除対象のS3オブジェクトが存在しないことを確認(何も出力されない)】
aws s3 ls s3://${S3_BUCKET_NAME}/${S3_OBJECT_PREFIX}

S3オブジェクトdir2 が存在しないことを確認できたらOK

◎事後作業(ローカルの片付けなど)

【一時ファイル用ディレクトリから他のディレクトリに移動】
cd ${HOME}/environment/

【一時ファイル用ディレクトリを削除】
rm -Rf ${HOME}/environment/tmp-handson-cli-s3

【S3バケット内にオブジェクトが存在しないことを確認(何も出力されない)】
aws s3 ls s3://${S3_BUCKET_NAME}/

S3バケット内にオブジェクトが存在することを確認できたらOK

*学びメモ

  • rmコマンドにてS3オブジェクトの削除が可能
  • S3バケット内のオブジェクト削除を再帰的に行う場合は、recursive オプションを使う(cpコマンドと同様)

3.S3バケットとの同期

サンプルファイルをGitリポジトリから取得し、S3バケットにオブジェクトを転送(sync)する。

Gitリポジトリからサンプルファイルの取得

◎事前準備

【Gitリポジトリの取得元を指定】
GIT_REPOSITORY_ORIGIN='https://github.com/opelab/jawsug-cli-sample-web.git'

【リポジトリ保存用の(親)ディレクトリパスを指定】
DIR_PARENT="${HOME}/environment/contents-handson-cli-s3"

【リポジトリ保存用の(親)ディレクトリを作成】
mkdir -p ${DIR_PARENT}

【Gitリポジトリのディレクトリ名を指定】
GIT_REPOSITORY_NAME='jawsug-cli-sample-web'

◎Gitリポジトリの取得

【サンプルファイルをGitリポジトリから取得】
cd ${DIR_PARENT} \
  && git clone ${GIT_REPOSITORY_ORIGIN}

【cloneできていることを確認】
ls -d ${DIR_PARENT}/${GIT_REPOSITORY_NAME}

ディレクトリjawsug-cli-sample-web が存在することを確認できたらOK

sync(S3バケットへのオブジェクト転送[同期])

◎事前準備

【同期対象とするディレクトリを指定】
DIR_S3_SYNC="${DIR_PARENT}/${GIT_REPOSITORY_NAME}"

【同期対象とするディレクトリが存在することを確認】
ls -d ${DIR_S3_SYNC}

【同期対象外とするディレクトリ/ファイルを指定する(ここでは".git"ファイルを指定)】
S3_SYNC_EXCLUDE=".git*"

◎オブジェクトへの同期

【ローカルファイルをS3バケットに同期させる
cd ${DIR_S3_SYNC} \
  && aws s3 sync . "s3://${S3_BUCKET_NAME}/" \
    --exclude "${S3_SYNC_EXCLUDE}" \
    --acl public-read
【S3バケットに転送対象オブジェクトが存在することを確認
aws s3 ls s3://${S3_BUCKET_NAME}/

※マネジメントコンソールの表示確認

s3api get-bucket-location(仮想ホスティングエンドポイント取得)

◎仮想ホスティングエンドポイントにオブジェクトパスを追加してURL作成

【s3バケットのエンドポイントを取得する
S3_BUCKET_ENDPOINT=" \
  ${S3_BUCKET_NAME}.s3.$( \
    aws s3api get-bucket-location \
      --bucket ${S3_BUCKET_NAME} \
      --output text \
  ).amazonaws.com"
【表示対象とするファイルを変数に格納(Gitリポジトリから取得したファイルの一つ)】
S3_OBJECT_NAME='img.jpg'
【s3バケットのエンドポイントと表示対象ファイル名を結合させ、URLを作成する】
URL_S3_OBJECT="http://${S3_BUCKET_ENDPOINT}/${S3_OBJECT_NAME}" \
  && echo ${URL_S3_OBJECT}

出力されたURLにブラウザでアクセスすると、イメージが表示される。

*学びメモ

  • sync コマンドでローカルディレクトリをS3バケットに転送(同期)することが可能
  • 例:aws s3 sync <ローカルの対象ディレクトリパス> <S3バケットパス>
    • あくまで同期(転送)させるためのコマンド。マウントするものではない。
  • excludeオプションで除外ファイルの指定が可能
  • aclオプションで公開レベルを指定する(指定するvalue については下記、公式の記述を参照)
    • Only accepts values of private, public-read, public-read-write, authenticated-read, aws-exec-read, bucket-owner-read, bucket-owner-full-control and log-delivery-write.
    • 例外を指定する場合には--include を使用する
  • 削除も考慮する場合はdeleteオプションを利用する
  • s3api get-bucket-locationコマンドでエンドポイントの取得が可能

4.webサイトホスティングの設定

s3 website(Webサイトホスティングの設定)

◎事前準備

【インデックスドキュメント名を指定】
S3_DOC_INDEX='index.html'

【エラードキュメント名を指定】
S3_DOC_ERROR='error.html'

◎Webサイトホスティングの設定

【Webサイトホスティングの設定を行う】
(インデックスドキュメント、エラードキュメントを併せて指定)
aws s3 website "s3://${S3_BUCKET_NAME}" \
--index-document ${S3_DOC_INDEX} \
--error-document ${S3_DOC_ERROR}
【s3バケットのエンドポイントを取得する】
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にブラウザでアクセスすると、index.html が表示される。

※マネジメントコンソールの表示確認(Static website hosting にインデックス/エラードキュメントが設定されていることを確認)

*学びメモ

  • websiteコマンドにより、Webサイトホスティング設定が可能
  • index-document, error-documentオプションによりインデックス/エラードキュメントを指定
  • エンドポイントの形式は、使用しているリージョン応じて異なるとのこと。詳細は公式ドキュメントを参照。
    • s3-website ダッシュ (-) リージョンhttp://bucket-name.s3-website-Region.amazonaws.com
    • s3-website ドット (.) リージョンhttp://bucket-name.s3-website.Region.amazonaws.com

5.署名付きURLの利用

s3 presign(署名付きURLの作成)

◎事前準備

【署名の対象となるS3オブジェクト名を指定】
S3_OBJECT_NAME='img.jpg'

【署名付きURLの有効期限(秒)を指定】
S3_PRESIGN_SECONDS='120'

◎署名付きURLの作成

【S3バケット上のオブジェクトに対して署名付きURLを作成】
aws s3 presign s3://${S3_BUCKET_NAME}/${S3_OBJECT_NAME} \
--expires-in ${S3_PRESIGN_SECONDS}

出力されたURLにブラウザでアクセスする。

*学びメモ

  • s3 presignコマンドではS3オブジェクトを指定して署名付きURLの作成が可能
  • 有効期限(秒)はexpires-inオプションにて指定する

片付け

s3 rb(s3バケットの破棄)

【S3バケットの削除】
aws s3 rb s3://${S3_BUCKET_NAME} \
--force
【S3バケットが存在しないことを確認】
aws s3 ls s3://${S3_BUCKET_NAME}/

その他、下記操作を行って片付け完了

  • ハンズオン用のローカルディレクトリの削除
    • rm -Rf ${HOME}/environment/contents-handson-cli-s3
  • IAMロールのデタッチ

*学びメモ

  • rbコマンドにてS3バケットの破棄(削除)が可能
  • S3バケット内にオブジェクトが残っている場合は、forceオプションが必要となる(無い場合エラー)

その他メモ

S3のエンドポイントには複数の考え方がある件について話題になていたが、これについてはまだ調べきれてないので宿題とする。

参考:AWSサービスエンドポイントAmazon Simple Storage Service エンドポイントとクォータ

最後まで読んでいただきありがとうございます。