amareloのブログ(仮)

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

JAWS-UG CLI専門支部 #168R S3基礎 Webサイト&ログ 参加レポート

9/24、JAWS-UG CLI専門支部にてS3でWebサイトホスティングするハンズオンを受講しました。ここのところ仕事と重なってなかなか参加できませんでしたが、久々の参加レポートを書いていきます。

目次

イベントページ

jawsug-cli.connpass.com

S3概要

S3の正確な情報を知るには、やはり公式ドキュメントを読むのが一番だと思いますが、今回学んだことのポイントは、以下2点です。

  • S3のCLIコマンドの種類

    • ハイレベルコマンド(s3):オブジェクトやバケットの作成、操作、削除などを簡素化するUNIXベースのコマンド。
    • ローレベルコマンド(s3api):APIへの直接アクセスを許可し、ハイレベルコマンドではできないアクセス制御、Webサイト公開設定などを行う。
  • アクセス制御

    • 特定の人やリソースからのアクセスはIAMで制御する。
    • 不特定多数の人やITリソースからのアクセスは、パブリックアクセスブロック、バケットポリシ、オブジェクトACLで制御する。

S3のCLIコマンドには2種類あることは気になっていましたが、今回初めて知りました。AWS CLIユーザーガイドにも書いてあったこと、復習で読み直して知りました。まだまだ公式ドキュメントの読み込みが足りないですね(汗)気になったらすぐに知らべる癖をつけねば…

ハンズオン

手順と構成図

手順一式はこちらです。また、今回のハンズオン構成図は以下の通りです。

http://prototype-handson-cli.s3-website-ap-northeast-1.amazonaws.com/handson_light-aws_service/handson_light-aws_service-s3_website_logging/_images/handson-basic-website_logging.png

コマンド

構成図上、保守担当者想定のIAMユーザを作成する必要がありますが、IAMユーザ、IAMグループ、IAMポリシ作成に使ったコマンドは割愛します。S3に関するコマンドのみ書きます。

バケットの作成

s3api create-bucketバケット作成をします。バケット名とバケットの作成ロケーションを指定します。なお、先にログ用バケットを作成し、その後コンテンツバケットを作成します。

aws s3api create-bucket \
--bucket ${S3_BUCKET_NAME} \
--create-bucket-configuration "LocationConstraint=${S3_BUCKET_LOCATION}"

以下の通り結果が表示されたら作成成功です。

{
  "Location": "http://handson-cli-s3-website-logging-website-XXXXXXXXXXXX-log.s3.amazonaws.com/"
}

バケットの存在確認は、s3api list-buckets で行います。

aws s3api list-buckets \
--query "Buckets[?Name == \`${S3_BUCKET_NAME}\`].Name" \
--output text

コマンド実行すると、以下のように表示されます。

handson-cli-s3-website-logging-website-XXXXXXXXXXXX-log

ちなみに、オプションをつけずにs3api list-buckets を実行すると、JSON形式で以下のように表示されます。 queryオプションのJMESPathは、Buckets内のNameに${S3_BUCKET_NAME}が含まれているものを抽出するという意味です(と読み取りましたが、間違っていたらご指摘ください)。

{
    "Buckets": [
        {
            "Name": "handson-cli-s3-website-logging-website-XXXXXXXXXXX-log",
            "CreationDate": "2020-09-26T02:08:56.000Z"
        }
    ],
    "Owner": {
        "DisplayName": "XXXXXXXXXXX",
        "ID": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
}

また、作成バケットのリージョンを確認します。

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

東京リージョンに作成されている場合は、ap-northeast-1と表示されます。

ログバケットACL(アクセス権限)更新

s3api put-bucket-aclACLの設定を行います。ログ配信グループに「オブジェクトの書き込み」と「バケットのアクセス権限の読み取り」の権限を付与します。URIは、S3で定義されているログ配信グループのものです(詳しくはこちらに記載があります)。コマンド実行後にエラーが出力されなければOKです。

aws s3api put-bucket-acl \
--bucket ${S3_BUCKET_NAME} \
--grant-write 'URI="http://acs.amazonaws.com/groups/s3/LogDelivery"' \
--grant-read-acp  'URI="http://acs.amazonaws.com/groups/s3/LogDelivery"'

s3api get-bucket-acl にて、ログバケットACLが、ログ配信グループからのオブジェクト書き込み(WRITE)とアクセス権限の読み取り(READ_ACP)を許可していることを確認します。許可されている場合は、WRITE READ_ACPと表示されます。

aws s3api get-bucket-acl \
--bucket ${S3_BUCKET_NAME} \
--query 'Grants[?Grantee.URI == `http://acs.amazonaws.com/groups/s3/LogDelivery`].Permission' \
--output text
コンテンツバケットの設定

ログ設定を s3api put-bucket-logging で設定します。コマンド実行後にエラーが出力されなければOKです。

aws s3api put-bucket-logging \
--bucket ${S3_BUCKET_NAME} \
--bucket-logging-status file://${FILE_S3_BUCKET_LOGGING_DOC}

バケットログ設定が有効になっていることを s3api get-bucket-logging で確認します。

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

設定されている場合は、コマンド実行後以下の通り表示されます。

{
  "LoggingEnabled": {
    "TargetPrefix": "Logs/",
    "TargetBucket": "handson-cli-s3-website-logging-website-XXXXXXXXXXXX-log"
  }
}

s3api put-bucket-policyバケットポリシを適用します。あらかじめ作成したポリシファイルを読み込みます。コマンド実行後にエラーが出力されなければOKです。

aws s3api put-bucket-policy \
--bucket ${S3_BUCKET_NAME} \
--policy file://${FILE_S3_BUCKET_POLICY_DOC}

バケットポリシが存在することを確認します。

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

コマンド実行後に以下のように表示されたら成功です。

{
  "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AddPerm\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::handson-cli-s3-website-logging-website-XXXXXXXXXXXX/*\"}]}"
}

s3api put-bucket-website でWebサイトホスティング設定をします。あらかじめ作成した設定ドキュメントを読み込みます。コマンド実行後にエラーが出力されなければOKです。

aws s3api put-bucket-website \
--bucket ${S3_BUCKET_NAME} \
--website-configuration file://${FILE_S3_BUCKET_WEBSITE_DOC}

なお、設定ファイル(${FILE_S3_BUCKET_WEBSITE_DOC})は以下のJSONを読み込ませます。エラードキュメントファイルとインデックスドキュメントの設定です。

{
  "ErrorDocument": {
    "Key": "error.html"
  },
  "IndexDocument": {
    "Suffix": "index.html"
  }
}

s3api get-bucket-website でWebサイトホスティングの設定が存在することを確認します。

aws s3api get-bucket-website \
--bucket ${S3_BUCKET_NAME} \
--query "length(@)"

先のJSONを読み込ませて、エラードキュメントとインデックスドキュメントを設定しました。そのため、コマンド実行後に設定数が2 と表示されればOKです。

Webコンテンツの公開

コンテンツバケットにアクセス確認します。コマンド実行後に0が表示されればOKです。

aws s3 ls s3://${S3_BUCKET_NAME} \
> /dev/null 2>&1; echo $?

s3 syncを使い、あらかじめ準備したインデックスドキュメントとエラードキュメントが格納されているドキュメントに移動し、ドキュメントをコンテンツバケットに転送します。なお、--excludeで‘git を転送対象外にしています。

dir_current=$(pwd)
cd ${DIR_S3_SYNC} \
&& aws s3 sync . "s3://${S3_BUCKET_NAME}/" \
--exclude "${S3_SYNC_EXCLUDE}" \
&& cd ${dir_current}

ドキュメントがアップされたことを、aws s3 ls s3://${S3_BUCKET_NAME}で確認します。コマンド実行後、例えば以下のように転送したドキュメントが表示されればOKです。

2020-09-25 01:23:45        186 error.html
2020-09-25 01:23:45      55560 img.jpg
2020-09-25 01:23:45        255 index.html

curlコマンドでWebサイトにアクセスできることを確認します。

curl ${S3_BUCKET_WEBSITE_ENDPOINT}

インデックスドキュメントのHTMLが表示されればOKです。

次にステータスコード200が返ってくることを確認します。

curl -LI -Ss \
-o /dev/null \
-w '%{http_code}\n' \
${S3_BUCKET_WEBSITE_ENDPOINT}
ログファイルの確認

以下の通り、ログバケットにログプレフィックスのファイルが作られていることを確認します。

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

ファイルが作成されている場合、コマンド実行後に以下のように出力されます。

2020-09-26 05:18:26       2935 2020-09-26-05-18-25-A44181ED2D12C6DA
2020-09-26 05:18:27       1468 2020-09-26-05-18-26-4CA7D54903B72B4F
2020-09-26 05:18:49        734 2020-09-26-05-18-48-13AABCAA53BD5E8E
2020-09-26 05:18:57       1416 2020-09-26-05-18-56-A8F517B37FCB32B4
2020-09-26 05:19:02       2109 2020-09-26-05-19-01-C7FFC74DA7AEB437
2020-09-26 05:19:08       3550 2020-09-26-05-19-07-02E2F4A93A96D79B
S3バケットの破棄

コンテンツバケットとログバケットを以下のコマンドで削除します。

まずは、s3 rm--recursiveオプションをつけてバケット内のオブジェクトを削除します。コマンド実行後に削除されたオブジェクトが表示されます。

aws s3 rm s3://${S3_BUCKET_NAME} \
 --recursive

s3api list-objects-v2でオブジェクトが存在しないことを確認します。コマンド実行後にエラーが出力されなければOKです。

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

s3api delete-bucketバケットを削除します。コマンド実行後にエラーが出力されなければOKです。

aws s3api delete-bucket \
--bucket ${S3_BUCKET_NAME}

s3api list-bucketsバケットが存在しないことを確認します。コマンド実行後にエラーが出力されなければOKです。

aws s3api list-buckets \
--query "Buckets[?Name == \`${S3_BUCKET_NAME}\`].Name" \
--output text

ハンズオンは以上です。

LT

今回はLTが2本ありました。

AWS CLIで月160万円の負債を解消した話 大内さん

資料はこちらです。

3日間の新人研修でかかるAWS費用(EIP+EC2+RDS(約2,000円/月))をAWS CLIで解消したというお話でした。800人分の研修用環境を後片付けするのは大変ですし、削除漏れがあると膨大な費用になります。それをAWS CLIでShellを組んで実行するだけで最小限に抑えたことには驚きでした!目に見える結果を技術を駆使して出したことに敬意を抱くばかりでした。

CDKについて 佐藤さん

突発のLTのため資料はありませんでしたが、CDKについてのお話でした。プログラミングでAWSに環境構築できるのは面白そうだと思っていましたが、プログラミングの素養がほとんどなくCDK途中で挫折してしまいました。CLIで理解を深めた上でCDKもいずれ再挑戦してみたいと思います。

最後に

Webサイト公開のように見えやすい結果のあるハンズオンだと、「ハンズオンやり切った!」という実感がとても強いです。今回のハンズオンも楽しみながら参加できて勉強になりました。そういえば、LTのあるCLI専門支部ハンズオンに参加したのは初めてでした。今回の大内さんを聞き、自分が学んだことを組織や顧客のプラスになることに使う、そんな仕事を実現したいと思いました。また、今回のハンズオンを終えて、自分もどこかでLT登壇したいと思いました。今年の初めに技術ネタで登壇したいとブログに書いておきながら、やれずに今年も残り3か月…いいかげん挑戦しようと思います。少しでも良い話ができるよう、気合入れなおして今後のCLI専門支部ハンズオンにも臨みたいです!