HTTPS + Route53フェイルオーバーでsorryページを表示する

# やりたいこと

「`https://www.xxxx.com/app/login`」のようなURLで動作しているWEBアプリケーションに対して、障害時やサーバー停止時にsorryページをサーバーレスで自動で表示させたい。
今回は、Route53のフェイルオーバーを使用し、独自ドメインでSSL暗号化通信での障害発生時に自動でsorryページを表示する方法を紹介します。

# はまったこと

HTTPでのRoute53フェイルオーバーを使用したsorryページの表示は、セカンダリのAliasにS3のエンドポイントを指定することで可能でした。
しかしSSL通信でのHTTPSの場合、S3単体ではSSL証明書を扱うことができないため、フェイルオーバー時にセカンダリとして指定することができません。
この解決方法として、CloudFront経由でHTTPSをS3にリダイレクトする方法を紹介します。
CloudFrontのOriginサーバーにS3のバケットを設定し、CNAMEを設定しSSL証明書を配置します。

# 実現方法

Route53のフェイルオーバーを使用し、サーバー起動時と停止時のリクエストの振り分けを行います。
Route53では1つのドメインに対してヘルスチェックを実行することができ、その結果に対してフェイルオーバーを設定できます。
今回は「`www.xxxx.com`」というサブドメインを使用すると想定します。

実現した構成図です。
<img width="846" alt="1.png" src="https://qiita-image-store.s3.amazonaws.com/0/239179/098b1218-0d42-6eb6-f378-771e0c6be381.png">

通常時の「`www.xxxx.com`」へのリクエストはELBに送信するため、AliasにELBのDNSを設定し、フェイルオーバーの設定はプライマリとし、ヘルスチェックを設定します。
障害時の「`www.xxxx.com`」へのリクエストはCloudFrontに送信するため、AliasにCloudFrontのDNSを設定し、フェイルオーバーの設定はセカンダリとします。
こうすることで、通常時のELBのDNSに対してヘルスチェックを実行し、失敗した場合はフェイルオーバーを実行し、自動でセカンダリにリクエストを振り分けることができます。

通常時の設定を正常系、障害時のフェイルオーバーの設定を異常系としそれぞれの設定を順番に紹介していきます。

## 前提条件

- ドメインを取得し、Route53にホストゾーンを作成している
    ホストゾーンの作成は以下を参考にしてください。今回は「`xxxx.com`」を使用します。

    [パブリックホストゾーンの作成](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/CreatingHostedZone.html)
- VPCを構築し、Public・Privateサブネットをそれぞれ2つ作成できていること。
- Privateサブネット内にWEBサーバーを構築できていること。
    VPCとサブネット、EC2インスタンスの作成は以下のページを参考にしてください。

    [AWSでセキュアなWebサーバーを構築する](https://qiita.com/taru2/items/f348aaedc4532c5b4ce0)

    WEBサーバーはApachをアプリケーションサーバーはTomcatを使用します。

    [ApacheとTomcatを連携させてみた](https://qiita.com/Dace_K/items/9d0419aefcb969335ca5)

<img width="633" alt="2.png" src="https://qiita-image-store.s3.amazonaws.com/0/239179/cc900a51-6537-f277-7a0e-10cb383f4605.png">


## 正常系の作成

ここでの正常系は、WEBサイトをHTTPSで公開できることとします。
以下にその設定手順を記載します。

1. Amazon Certification Manager SSL証明書の作成
2. EC2 ロードバランサーの作成
3. Route53 ロードバランサーのDNS登録
   
<img width="637" alt="3.png" src="https://qiita-image-store.s3.amazonaws.com/0/239179/4d40be90-5280-c649-45f8-acf44a95303d.png">


既に正常系ができている人は、異常系の作成に進んでください。

### Amazon Certification Manager SSL証明書の作成

ACMでパブリック証明書を作成します。ELBの配下にWEBサーバーを配置し、ACMでパブリック証明書を作成します。
Certificate Managerの「証明書のリクエスト」から、作成します。この時使用するリージョンは東京(ap-northeast-1)です。

- 証明書のリクエスト：パブリック証明書のリクエスト
- ドメイン名：`www.xxxx.com`
- 検証方法の選択：DNSの検証

証明書のリクエストを送信すると、ドメインのDNS設定にCNAMEレコードを追加する必要があります。
Route53でドメインの設定を行っている場合は、リクエストを送信した後にレコードを作成することができます。
「Route53でレコードの作成」ボタンを押すと自動的にRoute53のレコードに登録されます。

![4.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/553f1bc8-f6db-1bb2-ec66-f5cda1cad0fb.png)

検証が完了するまでに30分以上かかる場合があります。完了すると状態が「発行済み」になります。

[パブリック証明書のリクエスト](https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/gs-acm-request-public.html)

### EC2 ロードバランサーの作成

EC2のコンソール画面から「ロードバランサー」を選択し、「ロードバランサーの作成」を選択します。
Application Load Balancer かClassic Load Valancerを選択します。
リスナーのロードバランサーのプロトコル(in)にはHTTPS:443を、インスタンスのプロトコル(out)にはHTTP:80を指定します。
SSL通信を行うために、ロードバランサーに先ほど作成したSSL証明書を配置してください。

- ロードバランサーの種類：Application Load Balancer か Classic Load Balancer
- 名前：loadbalancer
- スキーム：インターネット向け
- IPアドレスタイプ：ipv4
- リスナー：HTTPS 443
- アベイラビリティゾーン：WEBサーバーが構成されているAZのパブリックサブネットを2か所選択（＊サーバーがプライベートに配置されていてもパブリックのサブネットを選択すること）
- 証明書タイプ：ACMから証明書を選択する
- 証明書の名前：`www.xxxx.com`
- セキュリティポリシーの選択：ELBSecurityPolicy-2016-08
- セキュリティグループの割り当て：新しい（すでに作成していれば既存）
    セキュリティグループ名：sg-elb , タイプ：カスタムTCP , ポート範囲：443 , ソース：0.0.0.0/0,::/0
- ターゲットグループ：新しいターゲットグループ
- 名前：alb-target-group
- プロトコル：HTTP
- ポート：80
- ターゲットの種類：instance
- ヘルスチェック：HTTP
- パス：/
- ターゲットの登録：構築したWEBサーバー2台を選択します。

[HTTPSリスナーを使用したClassic Load Balancerの作成](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/elb-create-https-ssl-load-balancer.html)

### Route53 ロードバランサーのDNS登録

ELBを作成するとAレコードが設定されるので、そのAレコードとサブドメインを紐づけます。
Route53のホストゾーンから、使用するドメインを選択します。
「Create Record Set」から、サブドメインを作成します。
ここでフェイルオーバーの設定を行うので、ここではフェイルオーバーの設定をプライマリーに設定しておきます。
またプライマリのドメインに対して、ヘルスチェックの設定をします。

- Name：www
- Type：A-IPv4 address
- Alias：Yes
- Alias Target：ELBのAレコード
- Routing Policy：Failover
- Failover Record Type：Primary
- Set ID：www-Primary
- Evaluate Target Health：Yes
- Associate with Health Check：No

![5.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/92ff1853-9b07-53f6-4d30-943f5fc3600a.png)

ここまでで、WEBサイトをHTTPSで公開することができます。

## 異常系の作成

ここでの異常系は、WEBサーバーに障害が発生しRoute53のヘルスチェックに失敗しフェイルオーバーが発生した場合とします。
フェイルオーバー先としてS3の静的ウェブホスティングで公開するsorryページを表示する構成を構築していきます。
CloudFrontにSSL証明書を配置し、HTTPSのリクエストに対するS3バケットへのリダイレクトを行います。

### S3バケットの作成

S3にsorryページを配置します。
S3サイトのための静的ウェブサイトホスティング設定を行います。
S3単体では独自ドメインでSSL証明書を使用したHTTPS通信を行うことができないので、CloudFrontで全てのリクエストをリダイレクトする必要があります。

- バケット名：`www.xxxx.com`(使用するサブドメインと同じ名前にしてください)
- リージョン：東京
- その他の設定はデフォルトのままにします

バケット作成後に、そのバケットの一番上の階層にsorryページのhtmlファイル「maintenance.html」をアップロードします。
バケットポリシーを以下のJSONファイルに変更します。

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

最後にバケットのプロパティから「Static website hosting」を選択し、S3の静的WEBサイトホスティングの設定を行います。

- このバケットを使用してウェブサイトをホストする
- インデックスドキュメント：maintenance.html
- エラードキュメント：maintenance.html

![6.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/45d703cd-7694-ce7a-a4ec-7a3a34294aa7.png)

設定画面の一番上にエンドポイントが表示されています。
CloudFrontの設定で使用するエンドポイントです。クリックしてsorryページが表示されると設定が完了です。

### Amazon Certification Manager SSL証明書の作成

正常系の場合と同じ手順で作成しますが、リージョンはバージニア北部で作成します。
東京リージョンで作成した証明書は、CloudFrontのSSL証明書として選択することができませんので注意してください。

- 証明書のリクエスト：パブリック証明書のリクエスト
- ドメイン名：`www.xxxx.com`
- 検証方法の選択：DNSの検証

### CloudFront OriginをS3に設定しSSL証明書を設定する

HTTPSに対応したS3の静的ウェブサイトホスティングの設定を行います。
CloudFrontの「Create Distribution」を選択し、配信方法に「WEB」を選択し、設定画面に移動します。
Origin SettingでオリジンサーバーにS3の静的ウェブサイトホスティングを設定します。
この時、Origin Domain NameにS3のバケットを指定します。

- Origin Domain Name：S3のバケット名
- Origin Path：
- Origin ID：自動的に埋まります
- Restrict Bucket Access：Yes
- Origin Access Identity：Create a New Identity
- Comment：自動的に埋まります
- Grant Read Perimssions on Bucket：Yes,Update Bucket Policy
- Origin Custom Headers：任意

![7.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/089a40a7-9be9-6e96-68b7-a6f2be377b6b.png)

Distribution SettingsでサブドメインとCloudFrontの紐づけとSSL証明書の選択をします。
またサブドメインのルートとなるオブジェクトの設定を行います。

- Alternate Domain Names(CNAMEs)：`www.xxxx.com`
- SSL Certificate：Custom SSL Certificate (異常系で作成したSSL証明書を選択します。)
- Default Root Object：maintenance.html

![8.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/17f0232d-0ade-dc9d-77aa-5dbb3944c824.png)

以上を設定後に「Create Distribution」で設定を終了します。
設定後にCloudFront Distributionsの画面から、先ほど設定したDistributionを選択し「Distribution Settings」から設定の変更を行います。
「Error Pages」タグから「Create Custom Error Response」を選択します。

![9.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/1e28f21d-e67f-4b4f-f8ab-169dc28b6df0.png)

ここでは、アプリケーションのURL「`www.xxxx.com/app/login`」などにリクエストが送信されてき時もsorryページを表示できるように設定します。
ここまでの設定では、「`www.xxxx.com`」へのリクエストに対してはsorryページを表示できますが、それ以下のURLにアクセスするとそのパスにsorryページのhtmlファイルが存在しないため、S3が403のエラーコードを返却します。CloudFront側でS3から403のエラーコードが返却されるとエラーページを表示する設定をすれば、「`www.xxxx.com/・・・`」以下のどのURLのアクセスに対してもsorryページを表示することができます。

- HTTP Error Code：403:Forbidden
- Error Caching Minimum TTL (seconds)：300
- Customize Error Response：Yes
- Response Page Path：/maintenance.html
- HTTP Response Code：503:Service Unavailable

![10.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/e4804c0e-1c3d-7d8b-f2ca-6e38d3f3c313.png)

設定が終わても公開されるまでに時間がかかります。CloudFrontのStatusがDeployedに変わるまで気長に待ちましょう。

### Route53 CloudFrontの登録

Route53でCloudFrontのDNSの設定を行います。
「`www.xxxx.com`」に対して、AliasにCloudFrontのドメイン名を指定します。
フェイルオーバーの設定はセカンダリにします。

- Name：www
- Type：A-IPv4 address
- Alias：Yes
- Alias Target：CloudFrontのドメイン名
- Routing Policy：Failover
- Failover Record Type：Secondary
- Set ID：www-Secondary
- Evaluate Target Health：No
- Associate with Health Check：No

![11.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/f4551a5c-2b05-749b-7ad3-c05bc8061044.png)

以上で異常系の設定が終了です。
CloudFrontの設定は少し時間がかかるので、15分ぐらい放置したほうが良いと思います。

## 確認

簡単なログインページを作成しサーバーに公開したとします。
まずはWEBサーバーを起動し、正常系の動作確認を行います。
今回は`https://www.xxxx.com/app/login`にアクセスするとログイン画面が表示するとします。

![12.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/df909435-c94f-8d12-72e8-aaeea3a2644c.png)

ログイン画面が表示されたことを確認した後に、サーバーに障害が発生し停止したと想定してApacheを停止させます。

```
$ sudo systemctl stop httpd
```

Apacheを停止した後に再度`https://www.xxxx.com/app/login`にアクセスすると、sorryページが表示されていれば設定がうまくできています。

![13.PNG](https://qiita-image-store.s3.amazonaws.com/0/239179/dfcd5931-4b8c-26b5-be5b-55ba32b38f3b.png)

# まとめ

S3とCloudFrontを用いて、Route53のフェイルオーバーを構築しました。
障害時のフェイルオーバー先がサーバーレスで構成されているため、sorryサーバーをEC2で構築・管理するよりも低コストで運用することができます。
また、S3とCloudFrontのSLAは99.9%以上なので高可用性を実現できます。
障害対策時だけでなく、サーバーメンテナンス時など意図的にサーバーを停止する際にも使用することができます。