AWS CloudfrontおよびS3を使って静的サイトジェネレータ「Astro 3.x」をデプロイしてみる

 

2024年12月12日掲載

Weekly Google Cloud アップデート情報

AWS S3およびコンテンツ配信サービスのCloudfrontを使って「Astro 3.x」をデプロイする手順を紹介します。

はじめに

Astroは、JavaScriptとTypeScriptに対応したオールインワンのWebフレームワークです。コンテンツ中心のウェブサイトを素早く構築するためによく使われます。

Astroの設計思想として、可能な限りクライアントレンダリングではなくサーバーレンダリングを使用することです。そのため、Astroに基づいて開発されたWebサイトは、Astroツールの build 出力を使ってサーバーレスでデプロイすることができます。この戦略は、一般的なサーバーサイド開発フレームワークと一致しています。

<参考>  Astro

このAstroが最近Astro3.0をリリースしました。内容もかなり簡易化されてるので、この機会に、サーバレスWebサイトとしてAWS上でAstro3.xをデプロイする方法を紹介します。

目次

1. 全体構成図

Astroは、Webサイトを作る際に役立つプロジェクトのテンプレートやテーマがたくさんあり、開発者はAstroが提供する対話型インストールウィザードを使って、その中から好きなテーマを選んで開発することができます。今回は、Astroベースで構築済みブログサイトを使用しながら説明します。

構築の流れとして、Astroでbuildし生成した静的WebサイトをS3 Bucketへデプロイします。あとはAmazon CloudFrontサービスを使用して、パフォーマンスとセキュリティを確保しながら静的コンテンツの配信を高速化します。なおAstro 3.xにはNodejsのバージョンが必要なので、v18.14.1以上を使用する必要があります。

<参考>  Amazon CloudFrontAmazon Simple Storage Service (S3)

2. 静的ウェブサイトのローカル生成

AstroによるBlogのWebサイトをデプロイするには、まずローカルにプロジェクト環境を構築する必要があります。ローカル開発環境にNodejs v18.14.1以上がインストールされていることを確認します。

Astroは統合インストールウィザードを提供します。Astroプロジェクトは、プロジェクトのルート・ディレクトリから`npm create astro@latest`コマンドを実行することで、自動設定のうえ素早くインストールできます。もちろん、手動設定でインストールすることもできますが、その分複雑になります。

<参考>   Astroの手動インストール

Astroのインストール設定で、インストール先のパス、プロジェクトテンプレート、開発言語などを選択します。

Astroはインストールに成功すると、プロジェクトパスの配下に関連ファイルを生成します。

<参考>   プロジェクトの構成


生成されたプロジェクトのルートディレクトリに入ったら、`npm run dev`コマンドを使ってアプリケーションを開発モードで起動します。ローカルからブラウザでプロジェクトページを見るには、http://localhost:4321/ にアクセスします。

次の画像は、ブログプロジェクトテンプレートで作られたアプリケーションのインデックスページです。

開発モードとして起動が成功したら、今度は `npm run build` コマンドを使って、アプリケーションをデプロイします。

コマンドが成功すると、プロジェクトのルートディレクトリに `dist` フォルダーが生成されます。フォルダ内のファイルは、以降デプロイメント手順にて使用されます。これでローカル環境にてAstroが無事デプロイできたと思います。次のステップとして、ローカル環境にデプロイしたAstroや関連フォルダをAWS側へ反映する作業になります。

3. 静的ウェブサイトのAWSへのデプロイ

3-1. Amazon S3 bucket の作成と設定

Amazon S3コンソールで新しいS3 bucket を作成し、 build したプロジェクトファイルを bucket へアップロードします。S3 bucket のプロパティとして設定事項は基本的にデフォルトのままです。ただし、S3 bucket のプロパティ設定でデフォルト設定のままだと、bucket はプライベート専用となるため、一般公開されなくなります。今回、S3付帯の静的ウェブホスティングサービスを使用してAstroプロジェクトをデプロイするため、ユーザーは生成されたURLを通じてS3 bucket 内のコンテンツに直接アクセスする必要があることから、このbucket に対し、パブリックアクセスを有効にするようにします。

コンテンツ配信にAmazon CloudFrontを使用する場合、ユーザはCloudFrontを通して bucket のコンテンツにアクセスします。このため、以降のステップでは、パブリックアクセスを無効にし、 bucket コンテンツへのCloudFrontアクセスのみを設定します。

 bucket が正常に作成されたら、CLIコマンドまたはコンソールからアプリケーションプロジェクトの build 出力を bucket へアップロードします。

アップロードに成功すると、生成されたすべてのファイルがローカルの `dist` ディレクトリから S3 ストレージ bucket のルートにアップロードされます。

3-2. Amazon CloudFrontインスタンスの作成と設定

Amazon CloudFrontコンソールで新しいDistributionインスタンスを作成します。`Origin Domain`の設定項目で、作成したS3 bucket をドロップダウンボックスから選択します。今回はクイックな構築なので WAFサービス は有効にしないようにします。もし商用サービスとして構築するのであれば、セキュリティを向上させるためにWAFサービスを有効化する必要があります。

インスタンスが正常に作成されると、インスタンスのアクセスアドレスとARNデータがコンソールページから取得されます。以降のステップでは、アクセスアドレスを使用してプロジェクトページにアクセスし、ARNデータを使用してアクセス権を設定します。

3-3. S3 bucket へのCloudFrontインスタンスアクセスの設定

CloudFrontインスタンスが正常に作成された後、コンソールに表示されたアクセスアドレスにブラウザから直接アクセスすると、「Access Denied(権限がない)」というエラーメッセージが表示されます。これはコンソールに表示されているアクセスアドレスが、S3 bucket のルートディレクトリに合わせていないためです。このエラーを排除すべく、特定のページに正しくアクセスできるようにするには、この作業の後にファイルパスを追加する必要があります。例えば、プロジェクトのホームページ、つまりルートディレクトリにある `index.html` ファイルにアクセスする場合、アクセスアドレスの後に対応するファイルパス情報(`/index.html`)を追加することで、エラーは解消します。

CloudFrontとS3 bucket の関連付け権限を設定する方法はいくつかあります。以降のステップでは、AWSが推奨するCloudFront OAC(Origin Access Control)の設定方法を使用します。

<参考>   CloudFront OACの設定

S3 bucket の`Permissions`管理タブにアクセスします。

`Bucket policy`セクションまでスクロールダウンし、編集します。

以下の情報を許可ポリシーエディットボックスに追加して、CloudFrontインスタンス用のS3 bucket の読み取り専用パーミッションを設定します。 bucket 名CloudFront ARN情報を置き換える必要があります。

{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "AllowCloudFrontServicePrincipalReadOnly",
        "Effect": "Allow",
        "Principal": {
            "Service": "cloudfront.amazonaws.com"
        },
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::/*",
        "Condition": {
            "StringEquals": {
                "AWS:SourceArn": ""
            }
        }
    }
}

保存に成功すると、S3 bucket 許可ポリシーセクションに新しく設定された許可ポリシーが表示されます。

CloudFrontコンソールに戻り、対応するインスタンスの`Origins`管理タブにアクセスします。関連するS3データソースを選択し、`Edit`ボタンをクリックします。

ソースアクセス方法を `Legacy access identities` に設定します。`Origin access identity`に対応するドロップダウンリストに該当するOAIオプションがない場合は、ポップアップウィンドウの右側にあるボタンを使って新しいOAIを作成します。

設定情報を保存する前に、S3 bucket アクセスポリシーを同期するを選択します。システムは、更新が有効になった後、関連するS3 bucket の許可ポリシーを同期的に更新します。

OAC設定が完了したら、対応するS3 bucket の許可ポリシー管理ページで、新しく更新された許可ポリシー情報を見ることができます。

この時点で、AstroプロジェクトはCloudFrontのアクセスアドレスから普通にアクセスできます。 ページ内容はローカル起動した時と同じです。

3-4. リクエストのリダイレクトのためのCloudFront関数の設定

デフォルトでは、CloudFrontはアクセスルーティングをサポートしていません。なので、プロジェクトの指定されたページにアクセスするには、URLで明示的に宣言する必要があります。プロジェクトのルートディレクトリに直接アクセスすると、エラーメッセージが返されます。

この問題を解決するには、リクエストのリダイレクトのためのCloudFront関数を設定する必要があります。CloudFront 関数管理ページで新しい関数を作成します。

関数の作成に成功すると、コンソールページに対応する設定情報とデフォルトコードが表示されます。

CloudFrontの関数コードを更新して、ページリクエストのリダイレクトを完了させます。

<参考> 関数を使用してエッジでカスタマイズ

function handler(event) {
    var request = event.request;
    var uri = request.uri;

    // Check whether the URI is missing a file name.
    if (uri.endsWith('/')) {
      request.uri += 'index.html';
    }
    // Check whether the URI is missing a file extension.
    else if (!uri.includes('.')) {
      request.uri += '/index.html';
    }
    return request;
  }

CloudFront関数の更新が完了したら、CloudFront関数をPublishします。

CloudFrontインスタンスの`Behavior`タブで、新しく公開した関数を`Viewer Request`に関連付けます。ドロップダウンリストを使用して、対応する設定オプションを選択することができます。

更新に成功すると、CloudFrontが提供するアクセスアドレスへ直接アクセスすると、インデックスページへジャンプされます。

4. 補足事項

4-1. npm runシリーズコマンドを使用する際に表示される「astro not recognized」というエラーメッセージを修正する方法

Astroインストールウィザードを使用してAstroプロジェクトをインストールおよび設定する際、インストール依存パッケージのステップでタイムアウトエラーが発生した場合、その後のステップでnpm runコマンドを使用すると、高い確率で「astro not recognized」エラーメッセージが表示されます。

Astroが認識されない理由は、Astroが正しくインストールされていないためです。プロジェクトのルートディレクトリにある `npm install` コマンドを使って依存関係を再インストールするだけで、問題は解決します。

4-2. npm run buildコマンド使用時の[vite]関連エラーメッセージの修正方法

npmコマンドを使用して静的サイトを構築する際に、`[vite]: Rollup failed to resolve import "xxx" from xxx` のようなエラーメッセージが表示されることがあります。

ブログプロジェクトテンプレートに基づいて作成されたAstroプロジェクトを build すると、`[vite]: Rollup failed to resolve import "sharp" from ...`というエラーメッセージが表示されます。

Rollupは、Viteが本番環境用にコードをパッケージ化し最適化するために使用するモジュールパッケージャーです。デフォルトでは、Rollupという複数のファイルに書かれたJavaScriptはすべての依存関係がローカルに配置されるため、バンドルに含まれるべきであると仮定しています。しかし、バンドルに大きな依存関係を含めると、アプリケーションのサイズが大きくなり、総じてユーザーのロード時間が遅くなる可能性があります。

これを避けるために、Rollupは特定のモジュールを外部依存としてインクルードする機能を提供しています。これは、Rollupがこれらのモジュールをバンドルに含めず、実行時に利用可能になることを期待することを意味します。これにより、バンドルサイズを小さくし、アプリケーションのパフォーマンスを向上させることができます。

エラーメッセージによると、Rollupがファイル中の「sharp」モジュールのimport文を解析できないことがわかる。そこで、コンフィギュレーションファイルで外部依存として「sharp」を指定する必要があります。

<参考>   build のカスタマイズ

プロジェクトのルートディレクトリに設定ファイル `vite.config.js` を作成します。

// vite.config.js
export default({
    build: {
      rollupOptions: {
        // https://rollupjs.org/configuration-options/
        external: ['sharp'],
      },
    },
  })

コンフィギュレーション・ファイルで、「sharp」モジュールを外部依存として宣言します。実際の問題を解決する際には、エラーメッセージで促された特定のモジュール名で対応するモジュールを交換します。 build コマンドを再実行すれば、対応する問題は解決します。

4-3. CloudFront Viewer Request関数設定時にエラーメッセージが表示される問題の修正方法

コンソールでCloudFront Viewer Request関数を設定する際、該当する関数が見つからないというエラーメッセージが表示される場合は、関数管理ページで該当する関数が正しく公開されているかを確認する必要があります。

5. さいごに

本記事では、S3に静的ファイルをホストし、CloudFrontをCDNとして使用してコンテンツを配信する手法をご紹介しました。CDNとして高度なキャッシュ戦略やエッジロケーションを通じて、グローバルなユーザーに対して高速なコンテンツ配信が可能ですが、今回この手法ではCI/CDをサポートしておらず、もしCI/CDとして持続可能なサービス開発・展開を行いたいのであればAWS CodePipelineのような他のAWSサービスと統合するか、別途CI/CDを手動で行う必要があります。

関連サービス

Google サービスを支える、信頼性に富んだクラウドサービスです。お客さまのニーズにあわせて利用可能なコンピューティングサービスに始まり、データから価値を導き出す情報分析や、最先端の機械学習技術が搭載されています。

MSP(Managed Service Provider)サービスは、お客さまのパブリッククラウドの導入から運用までをトータルでご提供するマネージドサービスです。

おすすめの記事

条件に該当するページがございません