Next.js 製の静的サイトを S3 + CloudFront + Github Actions で自動デプロイする
これまで Next.js や Gatsby を使った Jamstack なサイトを作る際には Netlify や Vercel といったホスティングサービスを使うことがほとんどでした。
そこで Jamstack に関する理解を深めるため、AWS の S3 + CloudFront (デプロイは Github Actions で自動化する) という、もう少しマニュアル寄りな構成で Jamstack なサイトを構築してみました。
最終的にできたサイトは以下になります。
https://d1ocf77jr70lm2.cloudfront.net
今回は、構築のための方法を自分用にざっくりメモがてら書いていきます。詳細な手順を記すものではないことをご了承ください。
Next.js でサイトを構築
{ "list": [ { "id": 1, "name": "hoge1", "age": 29, "branch": "fuga1" }, { "id": 2, "name": "hoge2", "age": 30, "branch": "fuga2" }, { "id": 3, "name": "hoge3", "age": 31, "branch": "fuga3" } ] }
- npm script で、静的サイト生成用のコマンド
"export": "next build && next export -o dist"
を定義する。また通常、next export
を実施すると/out
ディレクトリに静的ファイルは吐き出されるが、なんとなくの好みで/dist
に変更した。
Github Actions 用の設定ファイルを作成
- リポジトリ内に Github Actions 用の yml ファイルを作成する。mainブランチにpushした際にワークフロー(S3 への同期、CloudFront のキャッシュ削除)が走るようにする。
※デプロイに直接関係ない job については省略しています。 name: Deploy to AWS S3 on: workflow_dispatch: push: # push時に作動 branches: [main] # main ブランチが対象 jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout # ソースコードのチェックアウト。リポジトリ内のファイルにアクセスする uses: actions/checkout@v2 - name: Setup Node # Node 環境のセットアップ uses: actions/setup-node@v3 with: node-version: 16 - name: Install Dependencies # 依存モジュールをインストール run: yarn install - name: Export # ビルド & 静的ファイルエクスポート run: yarn export - name: Deploy # デプロイ実行 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} run: | echo "AWS s3 sync" aws s3 sync --region ap-northeast-1 ./dist s3://${{ secrets.AWS_BUCKET_NAME}} --delete echo "AWS CF reset" aws cloudfront create-invalidation --region ap-northeast-1 --distribution-id ${{ secrets.AWS_CF_ID }} --paths "/*"
S3 の設定
バケットポリシーを以下のように設定する(CloudFront のディストリビューションドメイン名は、CloudFrontの設定時に分かるので、最初は仮で後ほど値を入力する)。各ステートメントの意味としては、"PublicList" は Github Actions で S3 にファイルをアップロードするための指定で、"PublicReadGetObject" は CloudFront を経由せず直接 S3 にアクセスするのを拒否するための指定。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicList", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::[AWSアカウントID]:user/[userネーム]" }, "Action": "s3:ListBucket", "Resource": [ "arn:aws:s3:::[バケットネーム]", "arn:aws:s3:::[バケットネーム]/*" ] }, { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::[バケットネーム]/*", "Condition": { "StringLike": { "aws:Referer": "[CloudFront のディストリビューションドメイン名]/*" } } } ] }
CloudFront の設定
S3 に引き続き、こちらを参考にディストリビューションを作成する。
S3 への直接アクセス拒否のためにカスタムヘッダー設定を行う。
CloudFront > ディストリビューション > [ディストリビューションID] > オリジンを編集
からカスタムヘッダーを追加する。これを忘れると、CloudFront のディストリビューションドメインにアクセスしても、ずっと 403 (Access Denied)エラーになる。404 の場合はNext.js で用意されているエラーページを表示したいので、
CloudFront > ディストリビューション > [ディストリビューションID] > エラーページのレスポンスを編集
で、設定する。レスポンスページのパスには、/404/index.html
を指定する。この時点では、サブディレクトリにあるHTMLファイルにアクセスできないので、 CloudFront Functions を利用して参照できるようにする。スクリプトはこちらを参考にした。
デプロイ実行
main
ブランチに push すると Github Actions のワークフローが走る。- デプロイに成功すると、S3 にエクスポートされたファイルがアップロードされ、CloudFront のキャッシュが削除される。また、Github Actions 内に Artifact が生成される。
S3 および CloudFront の設定が正しく行われていれば、 S3 のバケットウェブサイトエンドポイントにアクセスした際は403が、CloudFront のディストリビューションドメインにアクセスした際は200番台が返ってくるはず。