たくさんの自由帳

CloudFront と S3 で静的サイトホスティングをする

投稿日 : | 0 日前

文字数(だいたい) : 17961

どうもこんにちは。
コイバナ恋愛 攻略しました。めっちゃおもろかったです
特に共通ルート面白い!!男友達とバカやってるのがいい。

Imgur

髪下ろしてるのかわいい

Imgur

Imgur

個別みじかい!!!ただでさえ短い個別がサブカプに取られてて更に短くなってる気がする;;
(でもサブカプの話も面白かった)

Imgur

Imgur

(ところでこの手の途中下車パターン(?)ってセーブして先に共通全部見るのがいいのかな、どうなんだろう)

Imgur

おすすめです!

本題

このブログは Netlify にあるわけですが、読み込みが遅い・・・
原因は日本にはCDNが無いらしく?(今もなはず?2020年から速度変わってない気がするんだよな)、日本からアクセスだとちょっと遅い。

ほかを探す

個人の感想 + 昔使ったことがあるとかで今は知らない ので参考にしてはいけない。
静的ファイルホスティングなので、SSRとかしたい場合知らないです

  • Cloudflare Pages
    • 無料枠がすごい
    • APEXドメイン使いたい場合はドメインのCDNCloudflare DNSにしないといけないぐらいしか欠点がなさそう感
    • 前調査したけど使いやすさは同じ感じだと思う。日本からも早いよ
  • Vercel
    • Next.js 作ってる会社
      • あんまり無いと思うけどNext.jsの一部の機能(next/imageとか)はVercelレンタルサーバーで Node.js を動かすのどっちかが必要だったはずで、NetlifyのノリでNext.js全部の機能を使いたければこれが良さそう
        • 別に使ってないですが・・・
    • 一回だけ使ったことがある、UI が綺麗だった記憶
  • Netlify
    • このサイトをホスティングしている(記述時時点)
    • 日本からのアクセスが遅い(多分今も)
    • (今もか知らない + ネット情報なのでよく分かりませんが)無料枠を超えてしまった場合は利用停止ではなく課金が勝手に始まるらしい
    • パソコンにあるindex.htmlが入ってるフォルダをドラッグアンドドロップすればデプロイ出来る機能とかが便利だった
      • Cloudflare Pagesにもある(小声)
  • GitHub Pages
    • 仕様がちょっとあったはず
      • 公開するフォルダの名前を/docsにするか、ルートに置くか
      • パブリックリポジトリじゃないといけない
    • なんかすぐ反映されないことがあった記憶
      • URL を直接変えると最新のが降ってくるとか
    • 一回使ったことあるしいいかな・・・
  • Firebase Hosting
    • このためだけにFirebaseのプロジェクト作るのアレじゃない・・・
      • すでにFCMとか使ってるならいいんじゃない?
    • 数年前にCLI経由でデプロイした気がするけど、いまもCLI経由しか無いんですかね?

こんなかんじ(めっちゃ個人的というか偏ってるな)
うーんCloudflare Pagesかなあ

AWS

そういえばお一人様 MisskeyのためにAWSLightsail や S3を使っているわけですが
すでにAWS使ってるならこのサイトもAWSに乗せていいのでは・・!

料金もお一人様を動かす費用から見たらたいしたことないはず。。です

(後なんかAWSで動かしてるのなんかかっこいいじゃん)

AWS で静的サイトホスティングするには

二択あるらしい

  • Amplify を使う
  • CloudFront + S3 を使う

前者のAmplifyだと、CI/CD機能が標準であったりして簡単に倒していそう、
ただ、転送量の無料枠、CloudFrontよりも低く設定し過ぎじゃない・・・?

後者のCloudFront + S3を使う方法だと、2つのサービス(正確にはもうちょっとある)を使う必要がある、CI/CDを自前で用意する必要がある
が、GitHub Actionsすでにあるし、何と言っても無料枠CloudFrontのほうがあるのでこっちで!

CloudFront + S3 も二通りある

参考にしました:https://dev.classmethod.jp/articles/s3-cloudfront-static-site-design-patterns-2022/

どっちも一長一短で、難しい

  • S3の静的サイトホスティング機能を有効にして、CloudFrontを経由してアクセスするようにする
    • S3単体だとhttpsに出来ないので、CloudFrontを噛ますようにします、他にもキャッシュしてくれるので
  • S3の静的サイトホスティングは利用しないで、CloudFrontからリクエストが来たらS3から取り出す
    • CloudFront Functionsを利用する必要があります

Imgur

S3 の静的ホスティング機能を使う

Imgur

前者のS3 静的サイトホスティングの方法が簡単ですが、アクセスをCloudFront限定にすることが出来ません。
S3の静的サイトホスティング用のURLがバレた(あるのかな・・)場合、S3にアクセスできてしまいます。
CloudFrontを噛ますことでキャッシュを返してくれて、S3へのアクセス料金が安くなるのですが、S3に直接アクセスされると料金が安くならない!)

S3 の静的ホスティング機能を使わない

Imgur

後者はS3からとってくるのはCloudFrontしか出来ないので、アクセスをCloudFrontだけに出来ます。
一方、S3の静的サイトホスティング機能の一つに、インデックスドキュメント機能というのが存在し、これは//about等にアクセスが来た際に自動的にindex.htmlをレスポンスとして返してくれるのですが、
この機能はCloudFrontにはなく、/aboutにアクセスされてしまった場合403? 404?になってしまいます。
何もしない場合は/about.html/about/index.htmlみたく、index.htmlまでURLを書かないと正しく表示できません。

この解決のためにCloudFront Functionsという、リクエストが来た際にindex.htmlをURLに足すようなJavaScriptコードを書くことで実現出来ます。
JavaScriptを書くと言っても、既にサンプルがあるのでこれをコピーするだけだと思います。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html

CloudFront Functionsは一ヶ月200万リクエストまでは無料で出来るので、多分この規模なら超えることはない・・・ハズ?

でどっちを使うかなんですが、後者を試してみようと思います。
直接アクセスはやっぱされたくないかなー・・・

つくる!!

大体これとおなじ
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/HostingWebsiteOnS3Setup.html

ながれ

  • S3のバケットを作る
  • S3 にファイルを置く
    • (静的サイトホスティングするファイルを置く)
  • CloudFrontのディストリビューションを作る
    • OACS3をオリジンとして利用できるように
  • CloudFrontでドメインとHTTPS化を行う
  • CloudFront Functionsを書く
  • GitHub Actionsgit push時にS3にアップロードとCloudFrontのキャッシュクリア
  • エラー時の代わりのページの設定
  • ドメインの向き先をCloudFrontにする(CNAMEの値をCloudFrontにして、リクエストがCloudFrontに向くようにする)

S3 のバケットを作る

S3のバケットを作ります、名前はおまかせします。
東京リージョンでいいはずですが、CloudFrontを経由させるので本当に金払いたくなければ安いところでいいんじゃね?知らんけど

Imgur

その他の設定は変えずに、作ります!

S3 にファイルを置く

が、この時点でNext.jsSSGをして公開するファイルを生成するのは面倒なので、適当に確認用のindex.htmlを書いてアップロードします
ところでVSCode!を書いた後にエンターを押すとhtmlのひな形みたいなのが出てくるんですけど、これVSCodeの標準機能なんですかね?

Imgur

Imgur

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>てすと</title>
</head>

<body>
    あいうえお
</body>

</html>

S3の画面へドラッグアンドドロップすればアップロード出来ます

Imgur

Imgur

CloudFront のディストリビューションを作る

さんこう:https://repost.aws/ja/knowledge-center/cloudfront-serve-static-website

CloudFrontに移動して、ディストリビューションを作成を押します

Imgur

オリジンはさっき作ったS3のバケットを選びます。
ドロップダウンメニューで選べるので入力しなくて済むはず

Imgur

次はオリジンアクセス、これはOrigin access control settings (recommended)を選び、

Imgur

コントロール設定を作成を押して作成画面を開きます
多分デフォルトで作成すればいいはず、説明とかはおまかせします。

Imgur

この警告はディストリビューション作成後に対応するので一旦飛ばします。

Imgur

次はWAFですが、よくわからないので無効にしておきました。

Imgur

これで作成を行います!

そしたら上の方に案内が出ているので、ポリシーをコピーしてS3 バケット~のリンクを開きます。

Imgur

そしたら、バケットポリシーの項目までスクロールして、編集を押します

Imgur

エディターが表示されるので、さっきコピーした内容を貼り付けて変更の保存を押します。

そしたらCloudFrontに戻ってきてください。アクセスできるか確認します
ディストリビューションドメイン名をコピーして、後ろに/index.htmlをつけてリクエストしてみます。

Imgur

どうでしょう?index.htmlが表示されましたか?

Imgur

ちょっとそれますがCloudFrontから帰ってきた場合、CloudFront側のキャッシュが帰ってきたのか、キャッシュもなくS3からとってきたのかはレスポンスヘッダーから見ることが出来ます。
一回目はMiss from cloudfront、二回目以降はHit from cloudfrontが帰ってくるはず。

C:\Users\takusan23>curl -I https://{ディストリビューションドメイン名}/index.html
HTTP/1.1 200 OK
Content-Type: text/html
Server: AmazonS3
X-Cache: Miss from cloudfront

C:\Users\takusan23>curl -I https://{ディストリビューションドメイン名}/index.html
HTTP/1.1 200 OK
Content-Type: text/html
Server: AmazonS3
X-Cache: Hit from cloudfront

カスタムドメインと HTTPS 化

既にhttpsですが、カスタムドメインを設定する際にhttps周りの作業が必要なので・・

さんこう:https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html

CloudFrontのディストリビューションを開いて、編集を押します。

Imgur

代替ドメイン名という項目があるので、項目を追加を押します

Imgur

はい

Imgur

これで変更を保存しようとすると、、、出来ません。

Imgur

To add an alternate domain name (CNAME) to a CloudFront distribution, you must attach a trusted certificate that validates your authorization to use the domain name. For more details, see: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html#alternate-domain-names-requirements

カスタム SSL 証明書 - オプションの項目もやらないといけないようです。
というわけでスクロールして戻って、証明書をリクエストを押します

Imgur

AWS Certificate Manager (ACM)が開くはず、
CloudFrontで使う証明書はリージョンがバージニア北部である必要があるのでちゃんと確認しましょう。

Imgur

パブリック証明書をリクエスト次へ

Imgur

完全修飾ドメイン名https://こ↑こ↓/index.htmlの部分です、FQDNとかかっこいい名前がついてる
サブドメインじゃない場合はどうなんだろう・・・

Imgur

検証方法はDNSでいいはず

Imgur

その他の項目はそのままにして、リクエストを押します

すると上の方に、追加のアクションが必要ですみたいなのが出てくるので、証明書を表示を押します。

Imgur

ちょっとスクロールすると、ドメインの項目でCNAME 名CNAME 値が表示されているはず。
この値を使ってドメインを持っていることを証明します。

DNS のレコードを追加する

私はドメインのDNSCloudflare DNSを使っています。
が、別に特別なことはしてないので操作は大体同じだと思います。

Cloudflareにログインした後、ドメイン名を押してDNSを開いて、レコードを追加する画面を出します。
タイプはCNAMEにして、名前にはCNAME 名、値にはCNAME 値をそれぞれ入れます。
入力の際に一番最後の.が消えちゃいますが特に問題なかったです。

Cloudflareの場合は追加でProxyするか選べますが、別にユーザー公開するやつじゃないのでProxyするまでもないはずなのでチェックを外してDNS Onlyにします。

Imgur

Imgur

あとはAWS側で証明が終わるまで待ちます。

Imgur

終わるとこんな感じにチェックマークが付きます

Imgur

あとはCloudFrontのディストリビューション設定に戻って、さっき作った証明書を設定します。

Imgur

これでようやく設定が変更できました。

Imgur

あ、あとリージョンを元の(多分?東京)に戻しておいてね、多分戻ってると思うけど

CloudFront Functions を書く

こうすることで、https://takusan.negitoro.dev/index.htmlみたいなURLを
https://takusan.negitoro.dev/みたいにindex.html省略してリクエストできるようにします。

↓ 以下現状

Imgur

CloudFrontから関数を選び、関数を作成を押します。

Imgur

Imgur

いい感じの名前をつけます、説明はお任せ

Imgur

JavaScriptのコードは以下のものを使うことにします。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/example-function-add-index.html

書いたら変更の保存して、

Imgur

発行を押します

Imgur

そしたら関連付けを追加を押して

Imgur

埋めます。

  • ディストリビューション はさっき作ったCloudFrontを選ぶ
  • イベントタイプ はViewer requestを選ぶ
  • キャッシュビヘイビア は多分default

Imgur

関連付けを追加を押します。

なんか時間かかってるけど待ちます

Imgur

成功していれば、index.html無しで開けるようになっているはずです!

Imgur

GitHub Actions で S3 へアップロードし、CloudFront のキャッシュを消す

さんこう:https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws
さんこう:https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services

さて、二日目に突入しました、がんばります
GitHub Actionsにビルドしてもらって成果物をS3にアップロードして、CloudFrontが持っているキャッシュを消す作業をしてもらいます。

調べてみるとOpenID Connectのほうがいいらしいので、今回はアクセスキーじゃなくてこっちを使ってみる。
アクセスキーだとGitHubへ機密情報(アクセスキー)を登録しないといけないのに対し、OpenID Connect (OIDC)だとGitHubへ機密情報を渡すこと無く同様のことが出来るらしい

AWS 側

まずはIAMの管理画面へ進み、ID プロバイダを押して、プロバイダを追加を押す

Imgur

そしたら以下の項目を埋めます

  • プロバイダのタイプ
    • 「OpenID Connect」を選ぶ
  • プロバイダの URL
    • https://token.actions.githubusercontent.com
  • 対象者
    • sts.amazonaws.com

Imgur

埋めたらサムプリントを取得を押します、押したらなんか出ます、、がそのままスクロールしてプロバイダを追加を押して閉じます

Imgur

完了したら、上の方にIAM ロールを追加しろと出るので、追加します。
ロールの割り当てを押しましょう

Imgur

新しいロールの作成、でいいはず

Imgur

ラジオボタンはウェブアイデンティティを選びます。
以下を埋めます(各自違うはず)

  • Audience
    • ドロップダウンメニューを開くと一個だけあるのでそれ
  • GitHub 組織
    • GitHub の名前
  • GitHub リポジトリ
    • GitHub のリポジトリ。多分必要、省略するとユーザー名が合っていれば使えるとかなんですかね?

ブランチも制限出来るらしいですがとりあえずこれで。

Imgur

次は許可する権限を設定する画面です。

Imgur

多分独自のポリシーを作らないといけないはず・・(読み取り権限を指定したS3 バケットに付与、みたいな)
がとりあえず今回はAmazonS3FullAccessCloudFrontFullAccessを付けました、多分指定したリソース(S3バケットCloudFront ディストリビューション)だけにアクセスできるようにするのがお作法な気がする

Imgur

Imgur

そしたら次へ進み、名前と説明をいい感じに入れてロールを作成を押します

Imgur

これで完了。次はGitHub Actionsを書いていきます。

GitHub 側

さんこう:https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services

まずは必要な値をGitHubのシークレットに登録します!
アクセスキーは登録する必要がなくなりましたが、S3 のバケット名とかCloudFrontのディストリビューション名とかは隠さないといけないので・・・

GitHubのリポジトリの設定画面から、Secrets and variablesを押して、Actionsを押す
New repository secretを押して、追加画面を出します

Imgur

Imgur

Imgur

シークレットに登録する値は以下の4つです。
(後述するGitHub Actionsymlをまるまるコピーする場合)

なまえあたい
AWS_ROLEIAM ロールの ARN の値です。コピーしてね Imgur
AWS_REGIONリージョンです、多分東京 ap-northeast-1 でいいはず?
AWS_S3_BACKETビルド成果物を保存するS3 バケットの名前です
AWS_CLOUDFRONT_DISTRIBUTIONCloudFrontのディストリビューションIDです、ID の列に出てるやつ

出来ました!

Imgur

次はGitHub Actionsを書いていきます、
Actionsを開きます。既に一個あるので、ここから追加します

Imgur

一個もない場合は多分こっちの画面が最初から出ると思う。
set up a workflow yourselfを選びます

Imgur

そして以下のようなymlを書きます。
ビルド成果物のフォルダが./outじゃない場合は直してください。

s3 sync --deleteを使うとローカルとS3バケットの中身が一緒になるように同期してくれるそうです!すごい

CloudFrontのキャッシュも消します。/*で一括削除です。キャッシュクリアは回数制限がありますが無料で出来ます。
(ワイルドカードを使うと無料でできる回数制限の枠を一つだけ消費した上で、複数のファイルに対してキャッシュクリア出来るのでお得!)
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#PayingForInvalidation

# ビルドして成果物を Amazon S3 にアップロードして、CloudFront のキャッシュを消す

# 名前
name: AWS Deploy

# 起動条件。pushと手動起動
on:
  push:
  workflow_dispatch:

# OpenID Connect
permissions:
  id-token: write
  contents: read

# やらせること
jobs:
  build:
    # OS
    runs-on: ubuntu-latest
    steps:

      # リポジトリをクローン
      - name: Clone repository
        uses: actions/checkout@v2

      # AWS の認証を行う
      - name: Setup aws credentials
        uses: aws-actions/configure-aws-credentials@v3
        with:
          role-to-assume: ${{ secrets.AWS_ROLE }}
          aws-region: ${{ secrets.AWS_REGION }}
      
      # Node.js インストール
      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 20.9.0
      
      # 依存関係
      - name: Package install
        run: npm i
      
      # 書き出し
      - name: Build page
        run: npm run deploy
        
      # Amazon S3 のバケットにアップロード
      - name: Upload S3 backet
        run: aws s3 sync --delete ./out s3://${{ secrets.AWS_S3_BACKET }}

      # CloudFront のキャッシュを消す
      - name: Clear CloudFront cache
        run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION }} --paths "/*"

さて!待ちます。

デプロイ結果

無事一発で通りました、逆に心配で草

Imgur

S3 のバケットもみてみましたが、ちゃんと中身あります!

Imgur

開いてみましたが、めっちゃ早い気がする!いや気がするじゃなくて実際早い!!!なにこれ!!!
CloudFrontまじ早くない?Next.jsももちろん早いんだけど

エラー時の代わりのページの設定

さんこう:https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/GeneratingCustomErrorResponses.html

このままだと存在しないURLにアクセスされた際に、/404/index.htmlではなくAWS側のエラー画面が出てしまいます。
Imgur

というわけで、S3にもなかった場合は404ページをブラウザに返してあげるようにCloudFrontを設定します。

ところで

この構成(Amazon S3 + CloudFront)で存在しないパスにアクセスすると、404ではなく403になります。
何でかはよく知りませんが、この辺がそう?

https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html

話を戻して、エラー画面を返す方法ですが、
CloudFrontのディストリビューションを開いて、エラーページを開き、カスタムエラーレスポンスを押します。

Imgur

先述の通り、存在しない場合にS3側は403を返すので、HTTP エラーコード403を選びます。
Next.jsの場合、trailingSlash: trueの場合は/404/index.htmlfalseの場合は多分/404.html404 画面に飛ばせるはず。
ステータスコードは404で返しておきます。

Imgur

しばらく待ちます

Imgur

これで、エラーページが返ってくるようになりました。
私も勘違いしてましたが、これはリダイレクトではなく、エラー内容をCloudFront側で変更しているだけなので、ブラウザのアドレス欄はそのままになります。

Imgur

DNS の向き先をホスティングサービスから CloudFront に直す

まずCloudFrontのディストリビューションを開いてディストリビューションドメイン名をコピーします

Imgur

そのあと、使ってるドメインのDNSの管理画面で、向き先を変えます(新規サイトの場合は追加します)
CNAMEで、名前は使いたいサブドメインを入れて(APEXドメインは知らない)、値にはさっきのディストリビューションドメイン名を入れます

Imgur

後は暫く待つと、サブドメインでアクセスできるようになってるはずです。
httpsなのでちゃんと鍵マークも付いてるはず!

おわり!!!

ながい!!!お疲れ様です・・・
参考にした記事ありがとうございます

ソースコード

GitHub Actionsのワークフローだけあったところでではありますが、、一応置いておきます
https://github.com/takusan23/ziyuutyou-next/blob/main/.github/workflows/aws-deploy.yml

速度

早くなってる!

CloudFront
Imgur

Netlify
Imgur

お金がかかるポイント?

  • S3 - CloudFront の間のリクエスト回数
    • 転送量は 0 だけどリクエストは課金されるはず、
    • が、CloudFrontが間に入ってキャッシュするのでそこまでで
      • GETリクエストは他と比べれば高くない
  • GitHub Actions から S3 にアップロードする
    • PUT リクエストの回数
      • AWSに入ってくる通信は無料だけど、PUTリクエストの課金はかかってしまう
        • そこまで高くはないとは思うけど
  • CloudFront Functions
    • CloudFront の転送量は1TBまで無料なので超過はまず無いはず
      • 動画とかあってもそんなにじゃない...
    • 一方 CloudFront Functions はリクエスト回数のたびに呼ばれるので、ちょっと心配かも
      • Next.jsはページ表示に複数のjsファイルをリクエストするので、1ページに複数回リクエストになってしまう

くらい?

おわりに

お金問題なければこれで行きたいかも。
しれーっと切り替わってるかもしれない。一応戻せるようにしばらくはNetlifyGitHub Actionsも動かしとこうかな。

おわりに2

もう誰も読んでないと思うけど

Imgur

冒頭のゲームの話なんですけど(本題関係ない)、デフォルトのフォントがモトヤマルベリで、なんかすごい懐かしい気分になった。丸い感じすき
というのもAndroid 4 ~ 5の標準日本語フォントがモトヤマルベリだったんですよね、なんでもAOSPのためにApache Licenseの元使えるようにしたとかなんとか

だた、Galaxy / Xperia / arrows / AQUOS Phone あたりは独自フォントのはずなので(めっちゃ頑張ればフォントだけでおおよそのメーカーが見分けられそう)、
Nexus 系列(まだ Nexus の頃)を買わないと見る機会はなかったのかもしれない?

このゲームのreadme.txt開いたらAOSPのライセンス(Apache License 2.0)あったけどもしかしてフォントのことだったのかな。

おわりに3 2023/12/26 追記

2023年 12月 から試しにCloudFrontからこのブログを配信しています。特に問題は無さそう感。

Imgur

C:\Users\takusan23>curl -I https://takusan.negitoro.dev/
HTTP/1.1 200 OK
Content-Type: text/html
Server: AmazonS3
X-Cache: Hit from cloudfront