CloudFront+S3で静的コンテンツ配信、API Gatewayで動的情報を取得するWebサイト構築

スポンサーリンク
ハンズオン
スポンサーリンク

はじめに

このハンズオンでは、Amazon S3に静的なコンテンツをアップロードし、CloudFrontを利用して静的コンテンツを配信し、API Gatewayを使って、動的なコンテンツも取得できるサーバレスなWebサイトを実装します。
API GatewayではLambda統合などで他のサービスと組み合わせて柔軟なAPIを作成できますが、今回は簡易的にMockを使ってAPIを実装します。

環境構成

以下の構成でWebサイトを構築します。

構築の流れ

API GatewayのMockの設定

API Gatewayの「Mock」統合を使用して、APIレスポンスをシミュレーションします。
準備したMockレスポンスを設定し、エンドポイントにリクエストがあると指定したJSONが返されるようにします。

API Gatewayの設定値は以下を参考にしてください。

  • API:REST API
  • API の詳細
    • 新しいAPI
    • API名:任意の名前を入力してください。
    • 説明:任意で入力してください。
    • API エンドポイントタイプ:リージョン
  • リソース
    • プロキシのリソース:OFF
    • リソースパス:/
    • リソース名:books
    • CORS (クロスオリジンリソース共有):ON → チェックをつけないとブラウザで実行するAPIがエラーになります。
  • メソッドの詳細
    • メソッドタイプ:GET
    • 統合タイプ:Mock
  • GETメソッドの設定
    • 統合レスポンス
      • マッピングテンプレート
        • コンテンツタイプ:application/json
        • テンプレート本文:下記レスポンスを入力
    • メソッドレスポンス
      • ヘッダー名:Access-Control-Allow-Origin

レスポンスは以下を入力

[
    {
        "title": "本のタイトル01",
        "author": "著者01",
        "year": "2021年",
        "rating": 5
    },
    {
        "title": "本のタイトル02",
        "author": "著者02",
        "year": "2022年",
        "rating": 4
    },
    {
        "title": "本のタイトル03",
        "author": "著者03",
        "year": "2023年",
        "rating": 2
    },
    {
        "title": "本のタイトル04",
        "author": "著者04",
        "year": "2024年",
        "rating": 1
    }
]

設定後、APIをデプロイしてください。ステージは任意で構いません。
デプロイ後、APIのエンドポイントが表示されるのでメモしておいてください。次のS3に配備するhtmlに記載します。

S3バケットの作成と静的コンテンツのアップロード

S3バケットの作成と静的コンテンツの配置

今回のハンズオン用に準備したhtmlファイルをS3にアップロードします。
おすすめの本のリストを出力するWebサイトです。任意バケット名でS3バケットを作成してください。


以下のhtmlを「index.html」というファイル名で保存して、S3バケットにアップロードしてください。
※【API Gatewayのエンドポイント+/books】はAPI GatewayのMockの設定で設定したエンドポイントとリソースのパスに置き換えてください。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>おすすめ本リスト</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
            padding: 20px;
        }

        header {
            text-align: center;
            margin-bottom: 20px;
        }

        h1 {
            font-size: 2rem;
            color: #4a90e2;
        }

        main {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        section {
            margin-bottom: 20px;
            width: 100%;
            max-width: 600px;
            background-color: #fff;
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        h2 {
            margin-bottom: 10px;
            color: #333;
            border-bottom: 2px solid #4a90e2;
        }

        ul {
            list-style-type: none;
        }

        button {
            padding: 10px 20px;
            background-color: #4a90e2;
            color: #fff;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }

        button:hover {
            background-color: #357ABD;
        }

        #details {
            margin-top: 10px;
        }

        footer {
            text-align: center;
        }
    </style>
</head>
<body>
    <header>
        <h1>おすすめ本リスト</h1>
    </header>

    <main>
        <section id="book-list">
            <h2>検索</h2>
            <ul>
                <li><button id="loadBooks">探す</button></li>
            </ul>
        </section>

        <section id="book-details">
            <h2>検索結果</h2>
            <div id="details"></div>
        </section>
    </main>

    <footer>
        <p>&copy; 2024 おすすめ本リスト</p>
    </footer>

    <script>
        document.getElementById('loadBooks').addEventListener('click', () => {
            // API Gateway経由でLambdaを呼び出すためのAPIエンドポイント
            const apiUrl = '【API Gatewayのエンドポイント+/books】';

            fetch(apiUrl)
                .then(response => response.json())
                .then(data => {
                    const detailsSection = document.getElementById('details');
                    detailsSection.innerHTML = '';

                    if (data.length > 0) {
                        data.forEach(book => {
                            const bookDiv = document.createElement('div');
                            bookDiv.innerHTML = `
                                <h3>${book.title}</h3>
                                <p>著者: ${book.author}</p>
                                <p>出版年: ${book.year}</p>
                                <p>評価: ${'★'.repeat(book.rating)}</p>
                            `;
                            detailsSection.appendChild(bookDiv);
                        });
                    } else {
                        detailsSection.innerHTML = '<p>詳細情報がありません。</p>';
                    }
                })
                .catch(error => {
                    console.error('エラーが発生しました:', error);
                });
        });
    </script>
</body>
</html>

CloudFrontの設定

CloudFrontを使用して、S3からのコンテンツをキャッシュし、全世界に配信します。
新しいCloudFrontディストリビューションを作成し、オリジンにS3バケットを指定します。

設定値はこちらを参考にしてください。

  • オリジン
    • Origin domain:作成したS3バケット
    • Origin path – optional:そのまま
    • 名前:Origin domainを選択すると自動で入力されます
    • オリジンアクセス:Origin access control settings (recommended)
    • Origin access control:Create neew OACを選択してOACを作成
    • カスタムヘッダーを追加 – オプション:そのまま
    • Enable Origin Shield:いいえ(デモ用なので)
  • デフォルトのキャッシュビヘイビア
    • パスパターン:そのまま
    • オブジェクトを自動的に圧縮:Yes
    • ビューワープロトコルポリシー:HTTPS only
    • 許可された HTTP メソッド:GET, HEAD
    • ビューワーのアクセスを制限する:No
    • キャッシュキーとオリジンリクエスト:Cache policy and origin request policy (recommended)(そのまま)
    • レスポンスヘッダーポリシー – オプション:そのまま
    • 関数の関連付け – オプション:そのまま
    • ウェブアプリケーションファイアウォール (WAF) :セキュリティ保護を有効にしないでください
    • 料金クラス:北米、欧州、アジア、中東、アフリカを使用
    • 代替ドメイン名 (CNAME) – オプション:ドメイン名を指定する場合は設定
    • Custom SSL certificate – optional:ドメイン名を指定する場合は設定
    • サポートされている HTTP バージョン:そのまま
    • デフォルトルートオブジェクト – オプション:アップロードしたhtmlファイル名
    • 標準ログ記録:そのまま
    • IPv6:オン
    • 説明 – オプション:任意で入力してください。

ディストリビューション作成後、ディストリビューションのIDとディストリビューションドメイン名をメモしてください。

S3のアクセスポリシーの修正

S3バケットのアクセスポリシーは以下を指定します。
【S3バケット名】と【ディストリビューションID】は置き換えてください。

{
    "Version": "2012-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::【S3バケット名】/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::781014508458:distribution/【ディストリビューションID】"
                }
            }
        }
    ]
}

アクセスしてみる

CloudFrontの設定でメモしたディストリビューションドメイン名にアクセスします。

以下が表示されました。

「探す」ボタンをクリックしてみます。

検索結果が表示されました。

まとめ

このハンズオンでは、S3に静的なWebサイトコンテンツを配置し、CloudFrontを使用して配信し、API GatewayのMock機能を使って、動的な情報を取得する手法も実装しました。
今回はAPI GatewayのMockを使っているため、APIのレスポンスが固定になっていますが、API GatewayとLambdaなどの他のサービスと結合することで柔軟なAPIを作成することができます。
今回のハンズオンを応用して、サーバレスなWebサイトを構築してみてください。

ドメイン登録をしたい場合

Route53を使ってドメインを取得ができますが、投稿日2024/10/08時点で******.comは14.0USD/年間でした。
安くドメインを取得したい方は、以下のドメイン取得サイトでドメインを取得することをお勧めします。


タイトルとURLをコピーしました