AWS S3を使ってSSL&独自ドメインの静的ブログをホストする
技術スタック インフラAWS

AWS S3を使ってSSL&独自ドメインの静的ブログをホストする

2021.10.30

こんにちはjunです。会社で静的書き出ししたコンテンツをs3でホストし、cloudfrontを用いてwebサーバーの様にブログ内容をリクエストできる様な構成を作成しました。せっかくなので自分のブログも同じ様にやってみようと思い、復習兼ねて記事にしようと思います。今回は以下のことを説明します。

  • S3の設定
  • cloudfrontの設定
  • 独自ドメインの設定(DNSはお名前ドットコム)
  • IAMでのデプロイユーザーの作成
  • nuxt generate で転送する方法

私のこのブログはnuxt content を用いてnuxt generate をして静的書き出したものをサーバに転送しています。今まではレンタルサーバーの一部ディレクトリを使用していましたが、AWSの勉強ややってみたい機能をつけやすくするためにS3へ引っ越しました。それでは早速やっていきましょう。

この記事が説明している内容は2021年10月時点の情報です。

参考資料

CloudFront を使用して、Amazon S3 でホストされた静的ウェブサイトを公開するにはどうすればよいですか?

ドメイン名を使用したトラフィックの Amazon CloudFront ディストリビューションへのルーティング

S3でデプロイ用のバケットを作成する。

では最初にまずホスティング元であるバケットを作成します。

バケットの名前は ホストするドメインの名前と同じ になるようにします。私の場合はバケット名が「jun-app.com」です。

設定した後にバケット一覧から選択し、プロパティを選びます。プロパティの一番下までスクロールすると「静的ウェブサイトホスティング」とあるのでここを編集します。

無効から有効に変更し、インデックスドキュメントにindex.htmlを入力します。こうするとhttps://jun-app.com/dir/みたいにパスだけであってもhttps://jun-app.com/dir/index.htmlに接続してくれます。また、エラードキュメントを指定しておくことで404リクエストの際のフォールバックとして利用できます。私の場合はルート直下の 404.htmlを指定しています。

編集後もういちど静的ウェブサイトホスティングの設定までいくと、一応URLが提供されますがアクセスしても「403 Forbidden」となります。これはバケットに対してパブリックアクセスがないからです。バケット作成時、デフォルトでは全てのパブリックアクセスが制限されていたので、その制限を外してあげます。バケットの画面から「アクセス許可」を選択し、「ブロックパブリックアクセス (バケット設定)」の編集をします。(ここを見ると「パブリックアクセスをすべて ブロック」が有効になっている)

編集の画面で「パブリックアクセスをすべて ブロック」のチェックを外して、全てのブロックを外します。

まだ終わりではありません。先ほどのはバケット単位の設定であり、アップロードされるオブジェクト(静的ファイル)には公開される設定がされません。毎回オブジェクトに対してパブリックアクセスを与えるのは面倒なので、「アクセス許可」の画面にあるバケットポリシーを以下のように編集します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::your-buget-name/*"
            ]
        }
    ]
}

上記のJSONはPrincipalはこのポリシーを付与するユーザーです。*としてゲストを含む全てのユーザーにs3:GetObjectのアクション、つまりファイルの読み取りを"arn:aws:s3:::your-buget-name/*"のバケットオブジェクトに許可するという意味です。

your-buget-nameにはあなたが設定したバケット名を入力します。そしてそのバケット配下の全オブジェクトに適用するためyour-buget-name/*とディレクトリのように指定します。"arn:aws:s3:::your-buget-nameという記述(Amazon リソースネーム)はバケットのプロパティで取得できます。

こうすればこのバケットないのファイルは公開されます。試しにindex.htmlをアップロードして、提供されたオブジェクト URLにアクセスすると確かに見れます。

これでS3側の設定が完了しました。ただしドメインはS3の初期状態のままです。独自ドメインを割り当てとSSL化を行います。

SSLと独自ドメインを割り当てる

SSL化した独自ドメインをS3に接続させるためにはcloudfrontの力が必要です。また今回使用するドメイン「jun-app.com」はお名前ドットコムで取得しているので、CNAMEを加えるなどの設定が必要です。クライアントがコンテンツを取得する流れは以下のようになります。

  1. クライアントがURLにアクセス
  2. お名前ドットコムへDNS問い合わせ
  3. お名前ドットコムはRoute53にNSを移譲しているので、問い合わせはRoute53へ
  4. Route53 はcloudfrontのIPを返す
  5. cloudfrontからS3に接続

といった流れです。

Route53と外部DNSの設定

まずはRoute53でホストゾーンというものを作成して、お名前.comからNSサーバーをRoute53へ委任できるようにします。Route53に移動して「ホストゾーンの作成」をします。

委任したいドメインを入力します。「パブリックホストゾーン」で大丈夫です。タグは管理上のものなので空欄で大丈夫です。問題なければ「ホストゾーンの作成」をクリックします。

作成後にはこのようにNSレコードとSOAレコードが作成されます。ドメイン自体が外部DNS(お名前.com)にある場合この「NSレコード」を指定することでRoute53に委任します。(ぼやけていますが4つ作成されます。)

このNSレコードをお名前.comの場合は「ネームサーバーの設定」を選択します。

そして「2.ネームサーバーの選択」>「その他」>「その他のネームサーバーを使う」にてRoute53で表示された4つのNSレコードをいれて「確認」クリックします。

「確認」の後にはDNSのNSが切り替わり、「jun-app.com」はまずお名前.comへ問い合わせられますが、結局Route53へたらい回しされます。これで「jun-app.com」のAレコードなどをRoute53で制御できるようになりました。

次はS3とcloudfrontと連携させてSSLを利用できるようにします。

独自ドメインの証明書を取得する

まずcloudfrontの設定の前に独自ドメイン(jun-app.com)のSSL証明書をAWSで取得しておきます。AWSではAWS Certificate Manager で取得できます。ただしここで注意点があります。

以下の画像のようにAWS Certificate Managerでのカスタム証明書(独自ドメインの証明書)をcloudfrontに割り当てる場合、AWS Certificate Managerのリージョンが「米国東部 (バージニア北部) リージョン (us-east-1) 」でないといけません。私は間違って東京リージョンで作成してしまい、証明書がサジェストで出てこずはまりました。

リージョンを「us-east-1」に変更したのち「証明書をリクエスト」をクリックして作成します。

証明書のタイプは「パブリック証明書をリクエスト」を選択し、ドメイン名などを入力します。そして検証では「DNS検証」を行います。Route53で委任してあれば「DNS検証」ですぐに発行できます。DNS検証はドメインを管理しているDNSにAWS Certificate Managerで発行されたCNAMEを設定することで、ドメインの所有権を確認します。全て入力して「リクエスト」となります。

リクエスト後には一覧に入力したドメインが現れ、対応するCNAMEが現れます。Route53で管理している場合は「Route53でレコードを作成」のボタンを押すだけで作ってくれます。手動で設定する場合は「CNAMEレコード」を作成して、その値を設定するだけです。

このようにDNS検証に必要なCNAMEが追加されています。

数分経つと証明書の検証が完了となりますので、これで「jun-app.com」の証明書の準備ができました。次はcloudfrontとS3を独自ドメインを用いて連携します。

cloudfrontとS3を連携

それではcloudfrontとS3を独自ドメインを連携します。cloudfrontのコンソールに移動して「ディスりビーションの作成」をクリックします。

そしてcloudfrontと連携するAWSリソースやオリジンなどを設定します。「オリジンドメイン」でS3リソースを選択するのですがS3をwebサーバーとして利用する場合 サジェストに出てくる 「~~~~.s3.ap-northeast-1.amazonaws.com」 を選択してはいけません。 これは単純に「S3オブジェクトURL」でありwebサーバーのような動きをしません。

今回の構成ではS3の「静的ウェブサイトホスティング」を有効にした際に出現した(下図のボケているとこ)のバケットウェブサイトエンドポイントをいれます。このバケットウェブサイトエンドポイントは「S3をwebサーバーとして使用するときのドメイン」です。ここを忘れて「S3オブジェクトURL」に設定しても一応ルートはs3に接続はできるのですが、「https://jun-app.com/test/」のように接続するとエラーが発生したり、思うように動きません。結構ハマりポイントです。

オリジンの設定をしたら他の項目は基本的にデフォルトのままでOKです。下に進んで独自ドメインと証明書を設定する箇所があります。「代替ドメイン名 (CNAME)」に連携したいドメイン名を入力し、そのドメインに対応する証明書(先ほど取得した証明書)を選択します。

最後にデフォルトルートオブジェクトに「index.html」を設定します。こうすると「https://jun-app.com/test/」とリクエストした際「https://jun-app.com/test/index.html」が自動的に返されます。

これで一通りcloudfrontとS3の連携が完了しました。「ディストリビーションを作成」を行いますと一覧にて ~~~~~~.cloudfront.net というS3オリジンに対するcloudfrontのドメインが作成されます。このドメインにアクセスしてみますと、SSL付きでS3上にアップロードした index.html が表示されます。

つぎは cloudfrontのドメインが jun-app.com に向くようにRoute53を設定します。

cloudfrontのドメインを独自ドメインに向ける

ではRoute53に戻ってcloudfrontのドメインが独自ドメインに向くようにします。ドメインのホストゾーンにて「レコードの作成」を選択します。そして以下のような画面が開きます。「レコード名」はサブドメインを作るわけでないので、空欄で大丈夫です。そしてレコードタイプは「A - ipvアドレスと..」を選択します。「トラフィックのルーティング先」のエイリアスをONにします。(トグルになっています)

Aレコードは普通、IPアドレスを入力しますがcloudfrontなどのAWSリソースでしているする場合「エイリアス」を使用します。エイリアスでは「CloudFront ディストリビューションへのエイリアス」を選択します。そしてその下にcloudfrontのディストリビューションの入力欄が現れますので、そこに先ほどのcloudfrontのドメインを入力します。

本来であれば登録したcloudfrontのディストリビューションがサジェストに表示されますが、登録したてだと出てこないことがあります。ただディストリビューションがあればとりあえず登録できます。

そして最後に「レコードの作成」をクリックします。すると jun-app.com に対するAレコードがcloudfrontのディストリビューションに向くようになります。ここまでドメインの流れを追うと以下の通りになります。

  1. jun-app.com にリクエストしたとき、まずお名前.comに問い合わせるが「Route53」に委任されているのでRoute53へ問い合わせ
  2. Route53では jun-app.com のAレコードが xxxx.cloudfront.net に向いているので、xxxx.cloudfront.netへ接続。
  3. xxxx.cloudfront.net は S3のwebホスティングURLに向いているので、そこに接続

となります。(結構ざっくりとしてます) 以上でバックエンド側の準備ができました。すでにドメインを使用している場合はDNSキャッシュが効いていることがあります。別の端末からアクセスしたり、DNSキャッシュをクリアするコマンドを打ってみましょう。

DNSの設定がうまく、対象のサーバーに向いているかを調べるときはnslookupdigコマンドを使って対象ドメインに紐づけられているサーバーを確かめてみましょう。

現在のところS3にindex.htmlが置いてあれば、独自ドメイン+SSLでそのindex.htmlが表示されていればOKです。

AWS CLIを用いたS3へのファイルのデプロイ

では最後にブログファイル群をS3にアップロードします。私の場合はNuxt.jsであらかじめ書き出しを行い、distファイル配下を送信します。手動は毎回大変ですのでAWS CLIを用いて自動化と差分アップロード できるようにします。その手順を解説します。最初にAWS CLIをもちいて対象バケットにアクセスできるIAMユーザーを作成します。

IAMでデプロイユーザーを作成する

AWSの運用ベストプラクティスでは

AWS アカウントのルートユーザー のアクセスキーを使用しないでください。 IAM により、複数のユーザーに AWS リソースへの安全なアクセスを簡単に提供できます。IAM により以下が可能となります。

とある様に基本的にIAMというものを用いてリソースにアクセスする様にします。例えば今回の様にAWS CLIを用いて自分のAWSリソースにアクセスする時にシークレットキーとアクセスキーを使用します。AWSを始めた時に作ったRootアカウントでも可能ですが、rootはCLIを通じで本当になんでもできてしまうので使うべきではありません。であればアクションとアクセスを制限されたユーザー(IAM)を使用すれば、もしキーが漏れたとしても被害は小さめです。なので今回のブログデプロイ用のIAMを作成してしまいます。

AWSにログインしてIAMに移動し、「ユーザーを追加」をクリックします。

ユーザー名を設定し、「プログラムによるアクセス」にチェックをし、次のアクセス権限の設定を行います。

ここでこのユーザーがアクセスできるアクションを設定できます。一応JSONを用いて細かい設定もできますが、今回はチュートリアル的な内容なので「AmazonS3FullAccess」を与えておきます。

次にタグを設定しますが、組織でなければ特に設定しません。最後に全体を確認して「ユーザーを作成」をします。これでS3だけにアクセスできるIAMができます。

このユーザー用のアクセスキーとシークレットキーがダウンロードできますので、控えておきます。まずデプロイ用のIAMユーザーができました。AWS CLIのインストールはここでは割愛しまが、ここで取得したIAMのキーをプロファイルに設定します。

aws configure --profile junapp-s3

プロファイル名は分かりやすい名前にしておくといいです。これでIAMと転送のセットアップは完了です。

ファイルを生成してS3へアップロード

私の環境では npm run generatedist ファイルが生成され、必要なHTMLファイルとアセットが出力されます。その distファイル配下を全てs3://jun-app.com/へ転送します。そのときはAWS CLIで以下のコマンドを使用します。

aws s3 sync ./dist/ s3://jun-app.com/ --delete --profile junapp-s3

s3 syncは2回目以降、差分をみてあれば同期してくれます。二回目以降は転送量を節約できます。--delete は同期先(S3)にて同期元(ローカル)にないファイルを消してくれます。例えばnuxt.jsではキャッシュ対策のため、_nuxtディレクトリ配下に34234324.jsみたいなハッシュ化したjsファイルが書き出しごとに生成されます。単純に転送していくと__nuxt配下にjsファイルが溜まっていくので差分を見て古いファイルを削除します。他にも「メニューなどにはないけれど、削除した記事がファイルとしては残ったままになっている」みたいな事故も防げます。

--profile にて先ほど設定したIAMのプロファイルを指定します。

このコマンドを実行する前に --dryrunのオプションをいれてシミュレートしてから本番を実行しましょう。

準備ができたらコマンドを打ちます。終わったらバケットを確認して終了です。

スクリプトに入れておくと便利

Nuxt.jsでnpm run generateした際にsyncされるようにpackage.jsonを以下のように編集しておきました。

pakcage.json
{
"scripts": {
    ...
    "generate-production": "nuxt generate && aws s3 sync ./dist/ s3://jun-app.com/ --delete --profile junapp-s3"
}
}

cloudfrontのキャッシュに注意

cloudfrontはCDNであり、キャッシュが強いです。更新したのに本番が変わらないときはキャッシュのせいかもしれません。cloudfrontでは明示的にキャッシュをクリアすることもできますし、キャッシュの期間を変更することもできます。閲覧数などに合わせて設定しましょう。

以上!

以上で S3 x SSL x 独自ドメインな静的ブログを構築できました。利用量によっては無料枠でも収まりそうです。しばらく料金の方も見てみてレンサバよりお得かを確認してみます。

Copyright © 2021 jun. All rights reserved.