メンテナンスページを CloudFront + S3 で表示させる方法 (SSL付き)


よーく忘れてしまう、サイトのメンテナンス表示の設定方法をまとめておきました。

CloudFront -> S3 で静的HTMLなメンテ表示サイトをホストして、DNS 切り替えでメンテ表示するという方法です。

他にも ELB で EC2 のターゲットグループを変えてメンテ表示させるとか方法ありますが、僕はこれが一番手っ取り早いなーと思います。

また、Route53 をお使いの方は、DNS レベルでのヘルスチェック機能でサーバーが落ちた時の自動表示を使って切り替え表示するのも良いでしょう。

AWS マネージドコンソールな管理画面からの設定手順メモです。

2024.2.29 時点の管理画面の遷移で確認しています。

**作業しながらの殴り書きなので、間違っているステップがあったらご指摘くださいませ！**


## 事前チェック

- DNS: ルートドメイン & DNS の種類確認
    - 対象にルートドメイン (`example.com` などサブドメインが無い) が含まれるか確認
    	- Route53 じゃないと、ルートドメインで、Alias を設定できないからです
	- 対象にルートドメインが含まれ DNS が Route53 意外であれば対応をどうするか考える
		- A レコードが立てられる EC2 を別に立ててメンテページを表示する (この記事の方法は使えない)
		- ALB (Application Load Balancer) を使っていれば、ALB 方式に変える (この記事の方法は使えない)
    		- ALB の固定レスポンス機能を利用してメンテナンス表示を行う
		- ルートドメインを諦めて www サブドメインなどのみをこの記事のメンテ表示をする
		- [裏技] Cloudflare を DNS サービスとして使う

## 事前準備

- DNS: 対象ドメインレコードの TTL を **事前に** 短く設定する。30〜60秒
- SSL証明書をゲット
    - 別で用意している場合はファイルをもらうよう手配する
    - Certificate Manager を通じで無料SSL証明書を利用可の場合は、ACM Certificate Manager で取得しておく
        - CloudFront 用なので、N Virginia リージョン (us-east-1) で登録しておくこと
        - DNS レコード認証をする (メール認証より楽)
- メンテナンスページ表示用の静的 HTML ファイル一式
    - `maintenance.html` の1ページのみをメンテ表示として設定しておく (ファイル名は index.html 以外、自由に決めてOK。）
        - ただし index.html など、通常ページと同じパスにならないよう。間違って 200 ステータスコードを返してしまい、最悪 Google にインデックスされます
    - CSS や画像を読み込むときは、ルートからの絶対パスにする。相対パスだと下層URLでメンテナンスページを表示するときに CSS や画像が 404 になっちゃいます

## S3 バケット作成

- メンテ表示用静的 HTML ページファイルをホストする S3 バケットを作成
    - `バケット名`はわかりやすいもので自由に決める
        - e.g, `PROJ-maintenance` とか
    - `リージョン`の場所は任意。分かりやすいように、いつも使っているリージョンに置いたらいいと思います
    - `パブリックアクセスを全てブロック` の **チェックを全てはずす**
    - `バージョニング`のオン・オフはご自由に
        - メンテページなので、無効で良いと思いますが、過去のメンテページの情報がもしかしたら欲しいかもしれないかもしれないのでオンにする等
    - バケットを作成します
- バケットの `静的ウェブサイトホスティング` をオンにします
    - バケットに入り `プロパティ` タブをクリック
    - `静的ウェブサイトホスティング`を「編集する」をクリック
    -  静的ウェブサイトホスティングを `有効にする`を選択し
    - ホスティングタイプは `静的ウェブサイトをホストする` を選択します。
    - インデックスドキュメント は `index.html`とします。 S3 上に存在しないのでエラーコードが **あえて** 返されて CloudFront に飛ぶようにします
    - エラードキュメントを先ほど作成した HTML ファイルに設定します (e.g., `maintenance.html`)

注釈: CloudFront を使った S3 静的ファイルホスティングにおいて、通常は S3 の `静的ウェブサイトをホストする` 機能をオンにする必要はありませんが、全ページで 503 ステータスを出力させるためにオン & 追加設定をしています。

## CloudFront 作成

メンテナンスページ表示用の Distribution を作成します。

- `Create Distribution` ボタンを押す
- Web の `Get Started` ボタンを押す (RTMP 無くなったので、ここのステップはいつか無くなるような気がする)
- Origin Settings の内容を入れる
	- `Origin Domain Name` で、先程作成した S3 バケットを入力する
	- `Origin Path` は空欄
	- `Origin ID` も空欄
	- `Origin Access Identity` を設定する。もしくはバケットポリシーを追加する必要あり。
- Default Cache Behavior Settings
	- 全てデフォルトのまま
	- キャッシュ時間は 60 秒がおすすめ
    	- デフォルトの CloudFront だとキャッシュ時間が6時間なので、メンテページに何か追加メッセージを表示させたい場合の変更を速く反映させる目的があります。
- Distribution Setting
	- `Alternate Domain Names` にメンテページ対象のドメインを入力。www ありや www なしのドメインなど
	- SSL Certificate は `Custom SSL Certificate (exmaple.com)` を選び、先程生成した証明書を選択
	- その他はデフォルトのまま
- 一旦、Distribution を作成し、10分ほど待つ
- エラーページ設定
	- 先ほど作成した Distrubtion をクリック
	- `Error Pages` タブをクリック
	- `Create Custom Error Response` をクリック 
	- `HTTP Error Code` は「403: Forbidden」を選択
	- `Error Caching Minimum TTL(Second)` は 300 (秒)
	- `Customize Error Response` は「Yes」
	- `Response Page Path` は S3 の静的サイトで作成したファイル名 (e.g., `/maintenance.html`) を入れる
	- `HTTP Response Code` を 「503: Service Unavailable」にする
	    - 503 だと Google さんも、「あ、このサイトはメンテ中なんだな」ということがわかる！
 
  これで一旦設定完了です！
  
## 動作確認

DNS 設定前に動作確認をしてみましょう。
   
    - CloudFront より先ほど設定した Distrubiton の中に入り `General` タブをクリック
    - `Domain Name` にある `XXXXXXXX.cloudfront.net` というドメイン名をつけます。
    - SSH ターミナルなどで、`dig XXXXXXXX.cloudfront.net`とコマンドをうつ
    - そこで出てきた A レコードの IP アドレスを取得
    - 自分の PC の Hosts ファイルを書き換えて、先ほどの A レコードの IP アドレスを、メンテ対象ドメインとして設定し適用
    - ブラウザのプライベートモードなどできちんとメンテページが SSL 付きで表示されているか確認する

# メンテ当日の手順書

## メンテ実施前の手順書

```
1. [自分] メンテページを作成
2. [自分] 作業手順を作成
3. [お客様] メンテページのテスト表示確認
4. [お客様] or [自分]: DNS の対象ドメインの TTL が30秒になっていることを確認 (既存の TTL x2 以上前に、30秒に変更するのが必要)
```

## メンテ当日の手順書

メンテ当日の手順は以下の通り

```
# メンテ作業手順

1. [お客様] or [自分]: DNS を以下の編集し、`exmaple.com` のドメインでメンテページが表示されるよう設定する
    - A レコード (or 元々のレコード) を削除
    - CNAME レコードを追加 (or Alias A Record を追加)
    - 値に `EXAMPLE.cloudfront.net` を入力
    - TTL は30秒
    - 変更実施
2. [お客様] or [自分]から C5J にDNS切り替え完了を連絡
3. 双方: メンテナンス表示確認 (DNS 変更後、ブラウザキャッシュなどを行い2〜3分で確認)
4. [自分]: メンテナンスを実施
5. [自分]: C5J から [お客様] に、メンテ完了を連絡
6.[お客様] or [自分] : DNS 情報を元に戻す
    - CNAME レコードを削除
    - A レコードを追加
    - A レコードの値に元の `XXX.XXX.XXX.XXX`  (or 元々のレコード)  を入力
    - TTL は30秒
    - 変更実施
7. メンテナンス表示解除を確認

### メンテページに切り替える時

Example のレコードを以下のように変更

Domain | Type | Value | TTL
---------------------------------------------|------------|--------------------------------------------|----
example.com | CNAME | XXXXXXXXXX.cloudfront.net | 30


### メンテページから戻すとき

example.com のレコードを以下のように変更

Domain | Type | Value | TTL
---------------------------------------------|----|---------------------|----
example.com | A | XXX.XXX.XXX.XXX | 30
```

以上

# 謝辞

@yousan さんにトップページでも、503 ステータスコードを表示させるために S3 側で全ページが 404 になるように、S3 で作成するファイルは index.html 以外のファイル名で登録したほうが良いとご意見をいただきました。当記事は修正済みです。ありがとうございます。

@hyoketu00 さんのハマったポイントをもとに修正しました。