CloudFront + S3 + Route53でReactのSPAを独自ドメインでホスティングする
はじめに
フロントエンドエンジニアの守備範囲が広がる昨今、みなさん(主にフロントエンドエンジニアの皆々様方)は普段からReactやNext.js等でアプリケーションの開発を行う際に、脳死でVercelデプロイを選択していませんか?
自分は完全に脳死でVercelデプロイを選択し、${アプリケーション名}.vercel.app
のドメインでアプリケーションのホスティングを行っていました。
今回は、普段Vercelがやってくれているホスティングを理解したいフロントエンドエンジニア向けに、「Vercelに頼らず、CloudFront + S3 + Route53を使って独自ドメインでアプリケーションのHTTPSでホスティング・公開する手順」を紹介できたらと思います。
記事を読み進める前に
今回は説明は省略しますが、独自ドメインをお名前.comを使用して取得しております。
本記事では、独自ドメインを取得済みであることを前提に進めていきますので、ハンズオンっぽくやってみたい方は以下の記事が詳しく記載してくださっているので、こちらを参考にしてドメインの取得を行ってみてください。
今回の成果物とその構成
今回開発したものは至ってシンプルで、Reactでお天気APIを叩いて動的に表示するというシングルページアプリケーション(SPA)を開発しました。
パブリックなアクセスを禁止にしたS3にビルドしたアプリケーションをアップロードし、CloudFrontからのアクセスのみを許可するように構成しました。ドメインはお名前.comで取得し、Route53で管理しています。
デプロイ部分は、GitHub Actionsでmainブランチにマージされたタイミングで自動デプロイが走るように設定しました。
具体的な構成は以下のようになっています。
※miroを使ってチャチャっと作ったものです。わかりにくいかもですが、ご了承いただければと...
ソースコードはこちら
デプロイまでの手順
S3にデプロイして、CloudFront経由で公開する手順は以下の通りです。
- S3バケットの作成
- Route53へドメイン名の設定
- ACMでドメインの証明書をリクエストする
- CloudFrontのディストリビューションを作成する
- Route53でサブドメインの設定を行う
- GitHub Actionsでデプロイフローを自動化
1. S3バケットを作成する
まず最初にS3バケットを新規作成していきます。
今回はあくまでCloudFrontを経由してReactのSPAにアクセスできるようにするため、S3の静的ホスティングは使用しません。そのため、ブロックパブリックアクセスのバケット設定も全てにチェックを入れてオンの状態にします。
ブロックパブリックアクセスをオンにします。
あとは全てデフォルトの状態でバケットの作成を行います。
2. Route53へドメイン名の設定
次にRoute53を使ってDNS周りの設定を行います。
下記のスクショのRoute53のダッシュボードからホストゾーンを作成していきます。
ホストゾーンを作成すると下記のようにNSレコードが作成されネームサーバの情報を取得することができます。(黒塗りしているところがネームサーバの情報です)
ここで取得した4つのネームサーバ情報をお名前.comのダッシュボードから、今回使用するドメインの「その他のネームサーバ情報」に転記します。
詳しくは下記の記事を参考にしてみてください。
3. ACMでドメインの証明書をリクエストする
次にAmazon Certificate Manager(略: ACM)のダッシュボードから証明書を作成します。
今回はパブリック証明書を作成します。
完全修飾ドメイン名のところには取得したドメインを入力します。今回はサブドメインを使用するため*.${ドメイン名}
にします。
そのほかの入力項目はデフォルトの状態でOKです。
証明書が作成されると、ステータスが保留中になっていると思います。
次にACMのダッシュボードで、先ほど作成した証明書の詳細画面からRoute53でレコードの作成を行っていきます。
おそらく、「Route53でレコードを作成」というボタンをクリックするとデフォルトで先ほど作成した証明書が選択されているかと思います。ですので、そのままレコードの作成を進めて問題ありません。
Route53でレコードを作成した後に、おそらく証明書のステータスが発行済みになっているかと思います。
4. CloudFrontのディストリビューションを作成する
続いてCloudFrontのダッシュボードからディストリビューションの作成を行なっていきます。
オリジンドメインに先ほど作成したS3のバケットを選択し、オリジン名(名前)には任意の値を入力してください。自分は分かりやすくするために今回取得したドメインを入力しました。
続いてオリジンへのアクセスをOrigin access control settingsに設定します。
Origin access controlを「コントロール設定を作成」から作成します。
バケットポリシーを設定をコピーして、S3のバケットポリシーにコピペしたら問題なさそうです。
次に最下部の「設定」の部分を色々いじっていきます。
これら以外の今回触れていない項目についてデフォルトの値を使用して問題ないかなと思います。
設定について、代替ドメイン名 (CNAME)はディストリビューション作成後のサブドメインの設定の後に色々設定するので一旦空欄でOKです。
カスタムSSL証明書は先ほど作成した証明書を選択してください。
最後に、デフォルトルートオブジェクトにindex.html
を入力してください。
5. Route53でサブドメインの設定を行う
次にCloudFrontのディストリビューションにルーティングするサブドメインを作成していきます。
今回はsample
を使用します。
レコードをタイプをAレコードにし、ルーティング先をCloudFrontのディストリビューションのエイリアスに設定します。「ディストリビューションの選択」の部分ではCloudFrontのディストリビューションのドメインを選択してください。
サブドメインの設定完了後、再びCloudFront側の設定を行います。
CloudFrontのディストリビューションの「一般」のタブから編集ページに移動します。
設定の代替ドメインにサブドメインを入力してください。
6. GitHub Actionsでデプロイフローを自動化
GitHub Actionsでデプロイフローを作成する部分は非常にシンプルでした!
以下の記事を参考にさせていただきました。よかったらこちらの記事を参考にして実装してみてください。
最終的に完成したデプロイフローのymlファイルはこちらです。
name: Deploy to S3 Buckets
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@main
- name: Install Dependencies
run: npm install
- name: Build
run: npm run build
- name: Deploy
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: aws s3 cp --recursive --region ap-northeast-1 build s3://${S3のバケット名}
ここまでで、mainブランチにpushを行うと以下のようにデプロイが反映されていることが確認できます。以上でVercelに頼らないSPAのホスティングは完了です!🎉👏
まとめ
今回の記事はフロントエンドエンジニア向けに書きましたが、インフラまでフロントエンドの守備範囲となると、あらためてすごいなぁと思います。(語彙力)
フロントエンドエンジニアがデータを受け取ってUIを作る、いわゆるJSONの色付け係だった時代はとうに終わってしまったみたいです。
令和のこの時代はフロントエンドエンジニア ≒ フルスタックエンジニア(異論は認めます)という時代なので、少しずつバックエンド・インフラといった領域の知見も深めていきたいものですね。。。
それでは、良いAWSライフを🥳!!!!!
参考情報
Discussion
この記事の範囲外だと思いますが、何かのご参考になれば幸いです。
S3へのデプロイ
GitHub Acitionでcpでデプロイすると、チャンクファイルなどの前のデプロイのときと違うファイルが残り続ける可能性があります。
syncを使って、最新のみファイルにしたほうがよいかもしれません。
--delete : 差分でコピー元にないファイルを削除
--exact-timestamps デフォルトは同じbyte数のものはsync対象にならないが、コンパイル後サイズがたまたま同じだったとしてもちゃんとsyncされるようにする(タイムスタンプで比較)
CloudFrontのキャッシュ削除
また、デプロイ後にCloud Frontのキャッシュされてデプロイが反映されない可能性があるので、invalidationもGitHub Actionの中にあるとよいかもしれなせん。
ルート以外のパスにアクセスされた場合にindex.htmlをみるようにCloudFrontの設定をする
SPAのルートで
/foo
のページがあった時に/
のあとに/foo
の場合は正しく動きますが、/foo
に直接アクセスするとエラーになります。403エラーの時に
/
をみるようにすると解決します。(またはLambda@Edgeで対応するなど)AWSのクレデンシャル
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEYを使わずにもっと安全にデプロイすることができるようになりました。OIDCプロバイダ+IAMロールでGitHub Actionsに権限を与えてデプロイできます。
まだまだ入門したてなのでとても勉強になります!ありがとうございます