AWSの部屋

AWS学習者向けのブログです

スポンサーリンク

S3 で静的Webサイトをホスティングしてみる

はじめに

筆者はAWS認定ソリューションアーキテクト-プロフェッショナル合格に向けて勉強中ですが、座学はあまり性に合わずできるだけ手を動かして勉強したいため、本ブログを通じて AWS の様々なサービスを使ってみようと思っています。本日は S3 で静的Webサイトをホスティングしてみようと思います。

S3 とは

Amazon Simple Storage Service (Amazon S3) は、業界をリードするスケーラビリティ、データ可用性、セキュリティ、およびパフォーマンスを提供するオブジェクトストレージサービスです。(出典:Amazon S3とは

今回作ってみる Webサイト

以下をサンプルとして使ってみようと思います。

developers.arcgis.com

ArcGIS に関しては筆者の別ブログをご参照ください。

構成

手順

  1. S3にバケットを作成
  2. バケットポリシーを編集
  3. 静的ウェブサイトホスティングを有効にする
  4. index.html を作成
  5. index.html をバケットにアップロード
  6. 動作確認

1.S3にバケットを作成

以下のバケットを作成します。

「パブリックアクセスをすべてブロック」のチェックを外してバケットを作成してください。

2.バケットポリシーを編集

バケットポリシーを以下のように編集します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadForGetObjects",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::バケット名/*"
        }
    ]
}

3.静的ウェブサイトホスティングを有効にする

バケットのプロパティから静的ウェブサイトホスティングを有効にします。

4.index.html を作成

以下のように index.html を作成します。

<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <title>
      Highlight feature with effects and blending | Sample | ArcGIS API for
      JavaScript 4.22
    </title>
    <style>
      html,
      body,
      #viewDiv {
        padding: 0;
        margin: 0;
        height: 100%;
        width: 100%;
      }
      #messageDiv {
        padding-left: 10px;
        padding-right: 10px;
      }
    </style>

    <link
      rel="stylesheet"
      href="https://js.arcgis.com/4.22/esri/themes/light/main.css"
    />
    <script src="https://js.arcgis.com/4.22/"></script>

    <script>
      require([
        "esri/WebMap",
        "esri/views/MapView",
        "esri/layers/VectorTileLayer",
        "esri/Graphic",
        "esri/layers/FeatureLayer",
        "esri/layers/GraphicsLayer",
        "esri/layers/GroupLayer"
      ], (
        WebMap,
        MapView,
        VectorTileLayer,
        Graphic,
        FeatureLayer,
        GraphicsLayer,
        GroupLayer
      ) => {
        const map = new WebMap({
          basemap: {
            portalItem: {
              id: "f35ef07c9ed24020aadd65c8a65d3754" // modern antique vector tiles
            }
          }
        });
        const vtLayer = new VectorTileLayer({
          portalItem: {
            id: "2afe5b807fa74006be6363fd243ffb30" // gray vector tiles canvas
          }
        });
        const countries = new FeatureLayer({
          portalItem: {
            id: "53a1e68de7e4499cad77c80daba46a94" // boundaries of countries
          }
        });
        // This group layer groups the gray canvas vector tiles and
        // countries feature layer.
        // With destination-over blendMode, the background layer covers
        // the top layer. The top layer is put behind the destination layer.
        // So when the app starts, the basemap layer will be shown over this layer
        const groupLayer = new GroupLayer({
          layers: [vtLayer, countries],
          blendMode: "destination-over"
        });
        map.add(groupLayer);
        const view = new MapView({
          container: "viewDiv",
          map: map,
          zoom: 6,
          center: [2, 46],
          constraints: {
            snapToZoom: false,
            minScale: 147914381
          }
        });
        let layerView, animation;
        // countryGraphicsLayer is added to the view's basemap.
        // It will contain black polygon covering the extent of the world
        // the country graphic will also be added to this layer when user clicks a country.
        // With destination-in blend mode, the contents of background layer is
        // kept where it overlaps with top layer. Everything else is made transparent.
        // In this case, the countryGraphicsLayer will be displayed underneath
        // modern antique vector tiles basemap.
        // The bloom effect will add a glow around the selected country.
        const countryGraphicsLayer = new GraphicsLayer({
          blendMode: "destination-in",
          effect: "bloom(200%)"
        });
        map.loadAll().then(async () => {
          addWorld();
          map.basemap.baseLayers.getItemAt(1).blendMode = "multiply";
          // add the buffer graphicslayer to the basemap
          map.basemap.baseLayers.add(countryGraphicsLayer);
          // get a reference ot the countries featurelayer's layerview
          // layerview will be queried to get the intersecting country
          // when user clicks on the map
          layerView = await view.whenLayerView(countries);
        });
        view.ui.add("messageDiv", "top-right");
        const symbol = {
          type: "simple-fill",
          color: "white",
          outline: null
        };
        // listen to the view's click event
        view.on("click", async (event) => {
          // query the countries featurelayer for a country that intersects the point
          // user clicked on
          const {
            features: [feature]
          } = await layerView.queryFeatures({
            geometry: view.toMap(event),
            returnGeometry: true,
            maxAllowableOffset: 10000,
            outFields: ["*"]
          });
          countryGraphicsLayer.graphics.removeAll();
          animation && animation.remove();
          let world = addWorld();
          // add the clicked country feature to the graphicslayer
          if (feature) {
            feature.symbol = symbol;
            countryGraphicsLayer.graphics.add(feature);
            // add a fade animation to show the highlight effect
            // for the selected country
            animation = fadeWorld(world);
            // zoom to the highlighted country
            view.goTo(
              {
                target: view.toMap(event),
                extent: feature.geometry.extent.clone().expand(1.8)
              },
              { duration: 1000 }
            );
          }
        });
        function addWorld(world) {
          world = new Graphic({
            geometry: {
              type: "extent",
              xmin: -180,
              xmax: 180,
              ymin: -90,
              ymax: 90
            },
            symbol: {
              type: "simple-fill",
              color: "rgba(0, 0, 0, 1)",
              outline: null
            }
          });
          countryGraphicsLayer.graphics.add(world);
          return world;
        }
        // add a fading animation when user clicks on a country
        function fadeWorld(world) {
          let timer;
          // requestAnimationFrame method specifies "frame" function
          // to perform an animation where the opacity of the world polygon graphic
          // decreased by 0.1 until it is 0 or completely transparent
          // then the animation is cancelled
          function frame() {
            const symbol = world.symbol.clone();
            symbol.color.a = Math.max(0, symbol.color.a - 0.1);
            world.symbol = symbol;
            if (symbol.color.a > 0) {
              timer = requestAnimationFrame(frame);
            }
          }
          frame();
          return {
            remove() {
              cancelAnimationFrame(timer);
            }
          };
        }
      });
    </script>
  </head>
  <body>
    <div id="viewDiv"></div>
    <div id="messageDiv" class="esri-widget esri-heading">
      <h4 class="esri-heading">Click on a country</h4>
    </div>
  </body>
</html>

5.index.html をバケットにアップロード

index.html をバケットにアップロードします。

バケットのプロパティに Web サイトの URL があるのでこれを使って Web サイトにアクセスしてください。

6.動作確認

Web サイトにアクセスできることを確認しました。

さいごに

AWS認定試験でも頻繁に出題される S3 の Webサイトホスティングですが、ちょっとしたWebサイトを公開するのには非常に便利な機能ですね。今後はただ Web サイトを公開するだけではなくCloudFront などを使ってみようと思います。

S3 に格納したファイルを Lambda で操作してみる

はじめに

筆者はAWS認定ソリューションアーキテクト-プロフェッショナル合格に向けて勉強中ですが、座学はあまり性に合わずできるだけ手を動かして勉強したいため、本ブログを通じて AWS の様々なサービスを使ってみようと思っています。本日はLambdaをメインに勉強してみようと思います。

Lambda とは

AWS Lambda は、サーバーレスでイベント駆動型のコンピューティングサービスであり、サーバーのプロビジョニングや管理をすることなく、事実上あらゆるタイプのアプリケーションやバックエンドサービスのコードを実行することができます。(出典:AWS Lambda

今回考えたアーキテクチャ

ざっくり以下のような感じです。

  1. Boto3を使ってS3にファイルをPUT
  2. S3にファイルがPUTされるとLambdaが起動
  3. Lambdaのローカル保存先(/tmp)にファイルを保存、ファイル名を変更してS3にPUT

実行環境

macOS 11.3.1
Python 3.9.6
Boto3 1.20.53

手順

  1. S3にバケットを作成
  2. S3にファイルをPUTするプログラムを作成
  3. Lambdaに付与するIAMロールを作成
  4. Lambda関数を作成
  5. 作成したバケットにイベント通知を作成
  6. 動作確認

1.S3にバケットを作成

2022-sanvarie-bucket というバケットを作成します。設定はデフォルトでOKです。

2.S3にファイルをPUTするプログラムを作成

Boto3を使ってS3にファイルをPUTします。Boto3に関しては以下のエントリーに書いてありますのでご参照ください。

aws-room.hatenablog.com

また、ファイルは以下の画像(hane.jpg)を使用します。

import os
import boto3

s3 = boto3.resource('s3')
bucket = s3.Bucket('2022-sanvarie-bucket')
filepath = '/Users/xxx/Documents/AWS/blog/data/hane.jpg'

# S3にアップロード
bucket.upload_file(filepath, os.path.basename(filepath))

試しに動かしてみます。

対象のファイルがPUTされたことがわかりました。

3.Lambdaに付与するIAMロールを作成

LambdaがS3と連携できるようにIAMロールを作成します。

4.Lambda関数を作成

save-file という Lambda関数を作ってみます。

関数を作成すると関数内で実行するコードの編集が可能になります。

import os
from datetime import datetime
import urllib.parse
import boto3
import subprocess

s3 = boto3.resource('s3')

def lambda_handler(event, context):

    # 対象のバケットとファイル名取得
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    
    # Lambdaのローカルのファイル保存先(/tmp)を設定
    file_path = '/tmp/' + datetime.now().strftime('%Y-%m-%d-%H-%M-%S')

    try:
        bucket = s3.Bucket(bucket) 
        
        # /tmpにダウンロード用のフォルダを作成
        cmd = ['mkdir', '-p', file_path]
        subprocess.run(cmd, stdout=subprocess.PIPE)

        # S3に格納したファイルを作成したフォルダにダウンロード
        bucket.download_file(key, os.path.join(file_path, key))   

        # ファイルの名前を変えてS3にPUT
        bucket.upload_file(os.path.join(file_path, key), 'hane2.jpg')

        return
    except Exception as e:
        print(e)

5.作成したバケットにイベント通知を作成

以下のようにイベント通知を作成します。

6.動作確認

「2.S3にファイルをPUTするプログラムを作成」のプログラムを実行し、S3にhane.jpg と hane2.jpg が格納されることを確認しました。

さいごに

S3 と Lambda の連携ですがかなり簡単にできることがわかりました。ただ、まだ基本的なことところしか触ってないので、今後はLambdaと様々なサービスを組み合わせてサーバーレスなアプリを作ってみようと思います。

S3 にファイル格納時に SNS からメールを送信させてみる

概要

本日はS3とSNSの連携を試してみたいと思います。AWS認定ソリューションアーキテクト-アソシエイトに合格したものの、実際にAWSを触ったことがあまりないため、少しずつですが色々なサービスを使っていこうと思います。

今回の目標

S3とSNSを連携させる

使用するサービス

構成

手順

  1. SNSトピックを作成
  2. サブスクリプションの設定
  3. S3にバケットを作成
  4. SNSトピックアクセスポリシーの編集
  5. S3イベント通知の作成
  6. 動作確認

1.SNSトピックを作成

以下のようにSNSトピックを作成します。

2.サブスクリプションの設定

以下のようにサブスクリプションの設定を行います。

3.S3にバケットを作成

S3に適当にバケットを作成します。今回は前回のエントリーで使用したバケットを使用します。

aws-room.hatenablog.com

4.SNSトピックアクセスポリシーの編集

SNSコンソールページに戻り先ほど作成したトピックを選択し、編集を押下しアクセスポリシーを以下のように編集します。(バケットポリシーを編集)

{
  "Version": "2008-10-17",
  "Id": "__default_policy_ID",
  "Statement": [
    {
      "Sid": "__default_statement_ID",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "SNS:GetTopicAttributes",
        "SNS:SetTopicAttributes",
        "SNS:AddPermission",
        "SNS:RemovePermission",
        "SNS:DeleteTopic",
        "SNS:Subscribe",
        "SNS:ListSubscriptionsByTopic",
        "SNS:Publish"
      ],
      "Resource":"<SNSトピックのARN>"
      "Condition": {
        "ArnLike": {
          "aws:SourceArn": "arn:aws:s3:*:*:<バケット名>"
        }
      }
    }
  ]
}

5.S3イベント通知の作成

作成したバケットに対してイベント通知の作成を行います。

6.動作確認

バケットにファイルをアップロードするとこのようにメールを受信することができるようになりました。

まとめ

いかがでしたでしょうか。AWS認定試験で頻繁に問われるS3とSNSの連携ですが、実際に行ってみるとすごく簡単にできることがわかりました。やはり自分で手を動かしてみると本を読んで勉強するよりイメージが湧きやすくなりますね。次回は Lambda に挑戦してみようと思います。

Boto3 で S3 に格納したファイルにアクセスしてみる

概要

筆者はAWS認定ソリューションアーキテクト-プロフェッショナル合格に向けてAWSの様々なサービスを勉強中。本日は Boto3 と S3 を触ってみる。

Boto3とは

AWS SDK for Python (Boto3) を使用すると、AWS の使用を迅速に開始できます。Boto3 を使用することで、Python のアプリケーション、ライブラリ、スクリプトAWS の各種サービス(Amazon S3Amazon EC2Amazon DynamoDB など)と容易に統合できます。(出典:AWS SDK for Python (Boto3)

今回の目標

Boto3 で S3 のバケットバケット内にある全てのファイル名を取得する

構成

実行環境

macOS 11.3.1
Python 3.9.6
Boto3 1.20.53

手順

  1. Boto3のインストール
  2. AWSアクセスキーの設定
  3. S3にバケットを作成
  4. バケットにファイルを格納
  5. 作成したバケットにアクセス
  6. バケット内のファイルにアクセス

1.Boto3のインストール

pip でインストールします。

pip install boto3

2.AWSアクセスキーの設定

AWS の操作には IAM のアクセスキーが必要なので、あらかじめマネージメントコンソールから作成する必要があります。作成方法に関しては以下を参照してください。

aws.amazon.com

また、以下のどちらかの方法でアクセスキーを設定します。私は2で設定しました。

  1. アクセスキーの情報を ~/.aws/credentials に設定する。
  2. AWS CLI をインストールし aws configure を行って設定する。詳細は以下を参照してください。

docs.aws.amazon.com

3.S3にバケットを作成

「20220211-test-bucket」というバケットを作成しました。

4.バケットにファイルを格納

作成したバケットに適当なファイルを格納します。

5.作成したバケットにアクセス

import boto3
s3 = boto3.resource('s3')
my_bucket = s3.Bucket('20220211-test-bucket')
print(my_bucket.name)

バケットへのアクセスに成功しました。

6.バケット内のファイルにアクセス

import boto3
s3 = boto3.resource('s3')
my_bucket = s3.Bucket('20220211-test-bucket')

for object in my_bucket.objects.all():
    print(object.key)

バケット内のファイルへのアクセスに成功しました。

さいごに

まだ基本的なことだけですが、目標を達成することができました。そのほかのサービスについても徐々に学んでいこうと思います。

AWS認定ソリューションアーキテクト - アソシエイト(SAA-C02)合格への道のり

先日、AWS認定ソリューションアーキテクト - アソシエイト(SAA-C02)の試験を受けて、無事合格することができました。その際の勉強法を紹介しますので、これから受験される方の参考になれば幸いです。

受験にいたったきっかけ

  1. プロジェクトでAWSを使うようになり、今後のキャリアのためにもサービスについてもっと知っておいた方がいいと思ったため
  2. 単純に「AWSすごい!」と思い、エンジニアとしてサービスについて深く勉強してみたいと思ったため

AWS認定ソリューションアーキテクト - アソシエイト(SAA-C02)とは

AWS認定資格は、以下のような分類になっており、SAA-C02は「AWS で分散システムを設計、実装する能力を認定」する資格です。(出典:AWS認定

詳細は以下をご参照ください。

aws.amazon.com

学習期間

2021年10月から勉強をスタートし2022年2月に受験しました。ただ、最初は週末の1~2時間のみでラスト一ヶ月に集中的に勉強することになりました。

学習時間

特に学習時間を計上はしていなかったのですが、体感的には80~100時間といったところでしょうか。最初の二ヶ月は継続的に学習ができず、覚えては忘れの繰り返しだったので効率は非常に悪かったと思います。

学習方法

1.教科書

こちらの書籍(AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版)を読み込みました(計三回)。これだけでは不十分ですが、基礎固めには有効かと思います。

2.Udemy

Udemyで以下二つの講座を受講しました。前者でハンズオンをしながら各サービスについて学び、後者で試験対策を行いました。特に後者の講座はとても有効で模擬試験で90%くらいとれるなら本番は問題なく合格できるのではないかと思います。

【2022年版】これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座

【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問)

3.問題集

Udemyだけでは不安だったので、こちらの問題集でとにかくたくさんの問題に触れ試験対策を行いました。本番でも似たような問題が出てきたので非常におすすめできる書籍です。

www.amazon.co.jp

4.AWSの公式模擬試験

AWSが提供している模擬試験です。こちらは20問で構成されていて自分の実力を測るいい機会になるかと思います。ただ、試験のスコアがわかるだけで正解や解説は提供されないため、受験しなくても大丈夫かと思います。

さいごに

AWS認定クラウドラクティショナー(CLF-C01)に合格した際はあまり苦労はしなかったのですが、AWS認定ソリューションアーキテクト - アソシエイト(SAA-C02)はとにかく大変でした。試験範囲が広いのと単純な暗記では解けないような問題も多く実際にAWSのサービスを使ってみないと攻略するのは難しいのではないかと思います。ただ、決して取得が困難な資格ではないと思うので、紹介したような方法で根気強く勉強することで合格することができるかと思います。