若干ポイント詰め込み過ぎのネタですが。
Laravelベースのシステム経由でアップロードされたファイルをS3に格納し、参照時は効率を考えてシステムを通さずに独自ドメイン配下のファイルとして見られるようにすることを考えます。
単に当該S3バケットを公開しただけでは、独自ドメインが適用できなかったり、HTTPSに対応できなかったりと色々と問題があるようです。
その辺をCloudFrontを経由することで解決します(加えて負荷分散もできますし)。
また、同じドメイン上の静的ページもS3に載せておいて、こちらも独自ドメイン経由でアクセスできるようにしたいですが、この静的ページと先のアップロードファイルは管理方法が異なるので別のバケットにしつつ、同ドメインに対するアクセスの内容によってそれら2つのバケットに振り分けるようにもしたいです。
この辺もCloudFrontのマルチオリジン機能で解決します。
上記のような環境を構築する手順(あくまで概要)を以下に整理していきたいと思います。
なお、本件の前段階として先に記事にした「AWSチャレンジ」で記述したような環境構築が済んでいることを前提とします。
また、独自ドメインも事前に取得しておきます。
Laravel:アップロード環境の調整
Laravel環境が構築済みで、特定のdiskを特定のS3バケットに対応付けていた場合、黙ってファイルの書き込みを行えば当該バケット直下に配置されます。ただ、後々同じドメインに対するアクセスをパスによって振り分けることを考えた場合、同操作の判断材料に使えるフォルダが必要になります。
よって、対象S3バケットの直下に「storage」(名前は何でも良いですが)を作成し、ファイルやフォルダを生成する場合は「storage」配下に作成するようにします。
S3:アップロード環境の公開
「AWSチャレンジ」における想定としては、静的ページを配置するS3バケットは公開、Laravel経由でアップロードされたファイルを格納するS3バケットは非公開という前提でした。
よって、改めてアップロードファイル格納用のS3バケットを公開しておきます。
作業内容は静的ページ用のS3バケットと同様に、「パブリックアクセスをすべてブロック」の解除と静的ウェブサイトホスティングの有効化に関する処理を行います。
CloudFront:ディストリビューション作成(静的ページ用)
CloudFrontでディストリビューションを作成します。このディストリビューションが後ほど設定するRoute53でのAレコードの宛先(一般的なAレコードではドメイン名に対応するIPを設定するところを代わりにディストリビューションを指定)になります。
また、ディストリビューションはあくまで通過点なので、その先にあるアクセスの実体(オリジン)を指定する必要がありますが、最初のオリジンとしては静的ページ用バケットの方を採用しておきます。
ということで、具体的な設定のポイントは以下のようになります(なお、残念ながら現時点でなぜかCloudFrontだけは画面が日本語化されていませんが)。
- Origin domain:静的ページ用バケット(選択できるようになっているはず)
- Path pattern:デフォルト (*)
- Viewew Protocol Policy:「Redirect HTTP to HTTPS」(最初は「HTTP and HTTPS」でも可)
- Default Root Object:index.htmlくらいを設定しておく
特に「Path pattern」がデフォルトであることにより、他の条件(この後設定)が適合しなかったパスが全て当該バケットに割り振られます(その上で当該バケットにも適合するファイルがなければ「Not Found」等に)。
CloudFront:オリジン設定追加(画像用)
先にディストリビューションを作成してオリジン1つ(静的ページ用バケット)を指定しておきましたが、実はディストリビューションには複数のオリジンを設定できます。
ということで、もう一つのアクセス先であるアップロードファイル格納用バケットの設定をします。
具体的な設定のポイントは以下のようになります。
まずは「Create Origin」を実施します。
- Origin domain:アップロードファイル格納用バケット設定(選択できるようになっているはず)
次に「Create Behavior」を実施します。
- Path pattern:storage/*
- Origin:アップロードファイル格納用バケット(選択できるようになっているはず)
- Viewer protocol policy:「Redirect HTTP to HTTPS」(最初は「HTTP and HTTPS」でも可)
- Cache key and origin requests:Cache policy and origin request policy (recommended)
- Cache policy:CachingDisabled
「Path pattern」に「storage/*」と設定することでパスがこのパターンになっているアクセスはアップロードファイル格納用バケットの方に割り振られ、それ以外は静的ページ用バケットに割り振られるようになります。
厳密には「Behaviors」の画面での設定内容の並び順に依存するようですが、少なくとも今回の実行において上記手順で設定を行った結果、前述の順序でルールが適用されるような並びになっています。
なお、「Path pattern」が「storage/*」となっていることで、例えば「domain/storage/a/b.jpg」のようなアクセスがあった場合はアップロード格納用バケット内の「storage/a/b.jpg」が目的のファイルとみなされます。このように「Path pattern」で指定したパス構成と同じ構造がアクセス先のバケットにも必要になるため、Laravel側でのファイル格納時においても「Path pattern」(今回の場合は「storage」)と同名のフォルダ配下にファイルを格納するようにしておく必要がある訳です。
また、「Cache policy」ですがアップロードファイルの更新がある程度の頻度で発生することを想定し、更新後速やかに更新結果が参照できるよう「CachingDisabled」にしておきます。
Route53:ホストゾーン作成
上記までの操作でアクセスを受ける側の形ができました。入り口はCloudFrontのディストリビューションです。
次は、独自ドメインに対するアクセスが前述のディストリビューションへのアクセスとなるようDNS設定をしたい訳ですが、この辺はAWSのDNS機能であるRoute53を使用します。
まずはホストゾーンを作成しますが、これはドメイン名を設定するのみです。
これを行うと同ドメインに対する名前解決をしてくれるネームサーバが4つ(現時点)設定されますので、これをドメイン取得元(例えばお名前ドットコム)のDNS設定で、同ドメインのネームサーバとして登録し直します。
この設定により当該ドメインに対する名前解決は前述のホストゾーン作成で指定されたネームサーバに託された形になりました。
あとはAレコードを設定するだけですが、その前にHTTPSが使用できるようにSSL証明書を取得しておきたいと思います。
Certificate Manager:証明書発行
AWSでは独自に、しかもタダでSSL証明書を取得できる「Certificate Manager」という機能があります。加えて、ここで取得した証明書は有効期間が13ヶ月ですが、期限になると自動更新される模様。毎年定期的にやってくる証明書の更新作業って結構煩わしいので、これはありがたいです。
ということで、SSL証明書を取得してみます。
まず、いろいろなサイトで再三注意喚起されていますが、CloudFrontで使用する証明書はリージョン「米国東部 (バージニア北部)」で取得されたものに限定されます。よって、まずこの点を間違えないように要注意です。
その後は「証明書のプロビジョニング」→「パブリック証明書のリクエスト」→「証明書のリクエスト」のように進めていき、「ドメイン名」には取得済みの独自ドメインを指定します。
「検証方法」としては「DNSの認証」を選択しますが、先にRoute53で当該ドメインに関するホストゾーン設定が済んでいることにより、その情報を元に簡単に認証が済まされます。
その後「確定とリクエスト」→「Route53でのレコードの作成」と進めていき、一覧上の「状況」が「発行済み」なれば証明書発行完了です。
CloudFront設定変更
取得したSSL証明書ですが、普通であればWebサーバ自体に設置するかと思いますが、CloudFrontを通して複数のアクセス先(オリジン)を束ねているような環境ではCloudFrontに設置するのが合理的です。
ということで、当然ながらCloudFrontにそのような機能があります。
先に生成したディストリビューションを選択して編集を実行します。
ポイントは以下。
- Altername Domain Name:ドメイン名
- Custom SSL Certification:選択(さらに、その下のフォームで先に生成した証明書を選択)
これでCloudFrontにSSL証明書が設置できました。
Route53:Aレコード作成
最後にRoute53にAレコードを作成します。
先に生成したホストゾーンを選択し、「レコードの作成」を実行。
- タイプ:Aレコード
- エイリアス:Yes
- エイリアス先:先に生成したディストリビューション
これにより当該ドメインのアクセスに関してはRoute53経由で名前解決され、目的のディストリビューションに到達することになります。その後はCloudFrontに設定した「Behavior」によりパスが「storage/*」かそれ以外かでアップロードファイル格納用バケットと静的ページ用バケットのいずれかにアクセスが割り振られます。
総括
今回はCloudFront配下にS3の複数のバケットをマルチオリジンの形で紐付け、同一ドメイン配下としてアクセスできるようにしましたが、当然ながらここにEC2が加わってきたり、さらに複数のEC2を束ねたELBを配置してしてみたりと、CloudFrontを起点になかなかに複雑な内部システム構成が組めるようになります。
CloudFrontと言えばAWSのCDN機能というイメージが強かったのですが、改めて上記のように見てくると、文字通り「Cloud」としての「Front」機能という表現がしっくりくるようになりました。
SSL証明書が簡単に取得、設置できるのも良いですね。個人的には未確認ながら更新まで自動で行ってくれると言うことなので、至れり尽くせりです。この辺の作業はそれほど頻繁に行うものでもないので、過去の資料と曖昧な記憶を頼りに結構ドキドキしながら行っていますが、その辺の精神的苦痛から解放されるとなれば大変ありがたいです。
なお、今更ですが、内容全般的にネットからの情報をかき集めて独自解釈も加えながら実施したものですので、余計な設定や不適切な(より適切な選択肢がある)設定となっている箇所があるかもしれません。その前提でGoogle先生などにも頼りながら再確認してもらえればと思います。
また、今回はS3バケットへのアクセスをCloudFrontを通すようにしただけで、従来のS3バケットへの直接アクセス自体もまだ活きています。このような構築(学習)過程での残骸的な設定も適宜整理していきたいところですが、その辺はAWSに関する習熟度がもう少し上がってからの課題と言うことで。