フロントエンド初心者がAngular5でSPA(XEMハーベスト確認)を作成(その3)

# 今回の内容
[前回（その2）](https://qiita.com/second113/items/7b4faebc07e62dab2884)で、XEMハーベストの状態確認を行って結果を表示するコンポーネント作成し、SAPとしてローカル環境で動作確認するところまで完了しました。今回は、作成したSPAをAWSの上で動作させてみます。


#本番環境用のSPA準備
## production modeでbuild
開発中にng serveして確認している間は、dev modeでビルドされており、公開用に最適化された状態ではないようです。そこで、production環境用に改めてビルドします。

```
> ng build --prod
Date: 2018-05-02T15:47:59.749Z
Hash: 5afeeed047ea9f670834
Time: 23990ms
chunk {0} polyfills.b6b2cd0d4c472ac3ac12.bundle.js (polyfills) 59.7 kB [initial] [rendered]
chunk {1} main.baa7dea2001c3e406d06.bundle.js (main) 234 kB [initial] [rendered]
chunk {2} styles.ac89bfdd6de82636b768.bundle.css (styles) 0 bytes [initial] [rendered]
chunk {3} inline.318b50c57b4eba3d437b.bundle.js (inline) 796 bytes [entry] [rendered]
```

すると、~\app01\distにビルド結果が出力されているはずです。こちらをAWS上に配置していきます。

#AWSの設定
SPA提供のアーキテクチャとしては非機能要件に応じて、色々なパターンが考えられます。最終的にはRoute53（DNS）→ CloudFront(CDN) → S3（オリジン） としたいと思いますが、せっかくですので、まずはS3単体でのホスティングを試した後に、CloudFrontを利用する形へ移行したいと思います。

## 構成：S3単体

###S3バケットの作成

ここで対象とするS3バケットの名前は、Route 53（DNS)と連携する場合に備えて、利用するドメイン名と一致させておきます。たとえば、ルートドメイン名がexample.comであれば、バケット名もexample.comとします。また、www.example.comなどのサブドメインもサポートする場合は、こちらのバケットも作成しておきましょう。

- 作成するバケット
    - example.com
    - www.example.com

###S3バケットへのSPA配置

AWS CLIなどでS3バケットにdist配下のファイルを配置しましょう。

```
cd ./dist
aws s3 sync . s3://example.com
```

Route 53と連携する場合は、example.comにのみSPAを配置すればOKです。www.example.comからexample.comへリクエストのリダイレクト設定を行うためです。


###S3単体での静的Webサイトホスティング設定
CloudFrontを利用する場合は別の設定（よりセキュアな設定）を行いますが、一旦S3単体での動作を確認してみます。いろいろと制約も多いですが、メリットはとりあえず”簡単”ということです。


- バケットの静的Webサイトホスティング有効化（example.com）

バケットの設定メニューから、プロパティ → Static website hostingを選択、「このバケットを使用してウェブサイトをホスティングする」を選択します。インデックスドキュメントはindex.htmlで良いでしょう。

![WS000118.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/d2c95883-8b39-031d-3b54-4b7dc077a7e4.jpeg)

後述のバケットポリシーを設定後、上記画面のエンドポイントにアクセスすると、アプリケーションにアクセスできるようになります。

- バケットポリシー設定（example.com）

対象バケット以下の全てのオブジェクトに対してRead権限を付与します。（誰でもアクセス可能なパブリックアクセスと呼ばれる状態になるため、本来ユーザに見せないはずのオブジェクトも見せてしまうことになります。）

```例

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::[バケット名]/*"
            ]
        }
    ]
}
```

- リダイレクト設定（www.example.com）

www.example.comバケットの設定メニューから、プロパティ → Static website hostingを選択し、「リクエストをリダイレクトをする」にドメインそのもの（Zone Apex、今回はexample.com)へのリダイレクトを設定します。

![WS000121.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/00f722b6-59a4-daa4-8a6f-da79a8e9c963.jpeg)

こちらを行っておかないと、後述のRoute 53を設定した場合もwww.example.comバケットで静的Webサイトホスティングが有効になっていない旨のエラーとなります。




以上でS3単体での静的WebサイトホスティングによるSPAの提供ができる状態です。（動作確認した結果は[前回（その2）](https://qiita.com/second113/items/7b4faebc07e62dab2884)と同様ですので、割愛します。）ただし、エンドポイント名は、「http://[ドメイン名].s3-website-ap-northeast-1.amazonaws.com」のような形式となりますので、一般公開するには長いですね。また、S3単体の仕様上、以下のような状態です。

- SSLの利用が不可
- ユーザ認証が不可（パブリックアクセスなので、全てのオブジェクトを参照可能）
- エラーページやメンテナンスページのステータスコード管理（200番でしか返せない）

## 構成：Route 53 + S3
次に、Route 53を利用して独自ドメイン名を利用する構成を取る場合の設定方法です。
名前解決の設定を追加するだけですので、S3の設定は単体の場合と同様でOKです。以下は、example.comを利用する場合を例に記載します。

###ホストゾーンの作成
Route 53にてルートドメインでパブリック・ホストゾーンを作成します。
![WS000119.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/1fecb9a0-e824-a753-263d-da3f186328cd.jpeg)

###エイリアスレコードの追加
example.comとwww.example.comから、S3バケットへのエイリアスレコードを作成します。Route 53の独自機能であり、AWSサービス間での名前解決についてCNAMEを介さずに読み替えを行うことができます。設定内容にも記載している通り、他のDNSからみると、ただのAレコードのように見えます。

「example.com」用のエイリアス設定は、レコードの追加メニューから以下のように設定します。

- Name
    - エイリアス元（example.com）
- Type
    - Aレコード
- Alias
    - Yes
- Alias Target
    - 対象のS3バケット

![WS000120.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/be3cf3d6-36f1-b54a-d3ef-a540a17be2dc.jpeg)

次に「www.example.com」用のエイリアス設定も同様に行いますが、Alias Targetは、直前で設定したRoute 53のホストゾーン「example.com」へ設定します。S3バケットではありません。


以上の設定を行うことで、「example.com」or 「www.example.com」にアクセスすれば、SPAを利用することができるようになりました。ただ、名前解決を追加しただけですので、S3単体とできることは変わっていません。


##構成：Route 53 + CloudFront + S3
最後に、CloudFrontを利用するアーキテクチャへ変更します。CloudFrontとS3に配置するオリジンデータのフェッチは無料であり、S3の場合と大差ないため、基本的には使ったほうがいいと思います。

＃真面目に計算すると、ちょっとだけS3単体のほうが安くなるはずですが、企業的には気にならないレベル。個人のお小遣いからすると、多少の差異が気になるかも。

### ディストリビューション作成

Origin Domain NameにS3バケット指定します。Origin IDは自動生成されるため、そのままでOKです。

また、S3の静的Webサイトホスティング用のエンドポイントに直接アクセスさせないためは、**Restrict Bucket AccessをYesに設定**しましょう。このとき、Grant Read Permissions on BucketをYesにすれば、必要なバケットポリシーがアップデートされます。

![WS000124.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/ccef8233-8e1d-3674-8266-b837043f71f3.jpeg)


次に、CDNとしての詳細な挙動の設定を行います。利用するプロトコルや暗号化の有無、TTL等、必要に応じて設定可能です。今回は、以下の設定で進めますが、動作確認が完了するまでは、TTL等を短く（0などに設定し、キャッシュ破棄を早く）して変更の反映を行えるようにしたほうが良いでしょう。確認できたらTTLを長く設定するのを忘れずに。

![WS000125.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/e746325b-534d-bb14-5c93-16d082800434.jpeg)


最後に、配信の詳細を設定します。今回は以下を設定。

- 配信するエッジロケーションを少し絞って料金クラス200の地域に設定。
    - 南米やオーストラリアが抜けているクラスです。
- （この後で）独自ドメイン名を利用するため、Alternate Domain Namesに利用予定のドメイン名を設定。
    - Route 53側からの設定だけでは、403エラー（The request could not be satisfied）がCloudFrontから返されます。
- Default Root Objectにindex.htmlを設定
    - 今回はAngularのデフォルトで利用しているindex.htmlに設定。


![WS000129.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/f0bf5039-3095-9fab-878a-084f84cd807b.jpeg)


その他は、要件に応じて設定を検討すれば良いと思いますが、まずはデフォルトで動作確認でも良いと思います。


コンソールに戻ると、Statusがin Progressになっているエントリが作成されているはずです。

![WS000127.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/61d326f2-73ef-796a-9f55-08e29c2e6dde.jpeg)

処理が完了するとStatusがDeployedになり、上記のDomain Nameにアクセスすることで、S3のオリジンからCDNに配置されたリソースへアクセスすることができるようになっています。このDomain Nameは、CloudFrontとして自動で設定されたものであり、ランダムな文字列とともに構成されています。

###独自ドメインの利用
S3の場合と同様に、Route 53のエイリアスレコードを利用して名前解決の設定を行います。既にS3単体に設定したエイリアスレコードの「Alias Target」をCloudFrontのDomain Nameに変更するだけでOKです。

![WS000128.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/eff96a23-ec8a-1363-25cd-f2ba7d1c04ea.jpeg)

###S3バケットの設定変更

- 静的Webサイトホスティングの無効化

直接S3へアクセスが不要になっているため、無効化します。

![WS000130.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/4f5592e3-4900-163d-dd7b-13011756c7d7.jpeg)


- S3 バケットポリシーの変更

everyoneからの読み取りを許可していましたが、CloudFrontからの読み込みのみを許可します。S3単体でのWebサイトホスティングの際に設定したものから下記のように変更（CloudFrontの設定時に自動で設定したもの以外を削除）しましょう。Principalの部分で許可するCloudFrontのディストリビューションを指定しますので、ARNを指定します。

```json:バケットポリシーのイメージ
{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXX"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::example.com/*"
        }
    ]
}
```

上記の設定によって、S3上のindex.htmlなどのオブジェクトに直接アクセスすると、以下のようにエラーが返る状態となります。

![WS000131.JPG](https://qiita-image-store.s3.amazonaws.com/0/179480/1b65e2f3-13f6-2fe5-5eca-f388d1e8e11e.jpeg)

これで設定したドメイン名でアクセスすると、
Route 53（名前解決）→ CloudFront → (キャッシュになければCloudFrontからのみ）S3オリジン
という経路のみで利用できる状態でSPAを公開することができました。
＃上手くアクセスできる場合の絵面は、例によって[前回（その2）](https://qiita.com/second113/items/7b4faebc07e62dab2884)と変わらないため省略。

------------------

今回は以上になります。見た目はともかく、まずは公開までの一通りの流れを追うことができました。まだ残課題や見た目の問題、Angularの関連コンポーネントの活用ができていませんが、追い追い。

[フロントエンド初心者がAngular5でSPA(XEMハーベスト確認)を作成(その1)](https://qiita.com/second113/items/0550aae709b20d81e875)
[フロントエンド初心者がAngular5でSPA(XEMハーベスト確認)を作成(その2)](https://qiita.com/second113/items/7b4faebc07e62dab2884)
[フロントエンド初心者がAngular5でSPA(XEMハーベスト確認)を作成(その3)](https://qiita.com/second113/items/00555bc4349f5777d289)