はじめに
EC2 がダウンしていても、ユーザーにメンテナンスの案内を出せるようにしました。
ALB(Application Load Balancer)のリスナールールで「固定レスポンス」を返す設定にして実現しています。サーバーを一切使わずに、ALB 自身がメンテナンス画面の HTML を返す仕組みです。
まず処理の流れのおさらい
ユーザーがアクセスしてから、アプリのサーバーに届くまでの流れはこうなっています。
ユーザー
↓
CloudFront(AWS の配信拠点。VPC の外)
↓
インターネットゲートウェイ(VPC の玄関)
↓
ALB(VPC のパブリックサブネット)
↓
EC2(アプリのサーバー)
今回手を加えたのは ALB です。
ALB は AWS が管理しているサービスで、複数の AZ(データセンター)にまたがって動いています。自分たちで運用している EC2 に比べて、落ちる可能性はかなり低いです。そのため「EC2 が落ちても ALB は生きている」状況を前提に、ALB でメンテナンス画面を出すことにしました。
ALB とは
ALB は、届いたリクエストを後ろのサーバー(EC2 やコンテナ)にうまく振り分けてくれるロードバランサーです。
- 複数のサーバーにアクセスを分散できる
- 複数の AZ にまたがって置けるので、1 つの AZ が落ちても止まらない
- ルールを設定して、どのリクエストをどこに送るかを制御できる(パスやドメインなどで振り分け)
ターゲットグループとは
ターゲットグループは、ALB がリクエストを転送する**「宛先のリスト」**です。
ユーザー → ALB(受付)→ ルール「このパスならこのグループへ」→ ターゲットグループ(宛先リスト)→ EC2
レストランで例えると、こんな関係です。
| AWS | 例え |
|---|---|
| ALB | 受付 |
| リスナールール | 受付の案内ルール(「予約の方は 2 階へ」) |
| ターゲットグループ | 2 階の席の一覧 |
| EC2 | 実際の席 |
ターゲットグループは、宛先の一覧に加えてヘルスチェックも持っています。定期的にサーバーへアクセスして、応答しないサーバーには転送しないようにしてくれます。
リスナールールで、EC2 が壊れてもメンテ画面を出す
リスナールールの優先度
リスナールールには優先度(1〜50,000 の数字)があり、数字が小さい順に評価されて、最初に一致したルールで処理が決まります。
普段はこうしています。
| 優先度 | 条件 | 動作 |
|---|---|---|
| 10 | パス = *
|
ターゲットグループ(EC2)へ転送 |
| 100 | パス = *
|
固定レスポンスでメンテ画面を返す(503) |
優先度 10 の転送ルールが先にすべてのリクエストに一致するので、優先度 100 のメンテのルールは普段は使われません(待機状態)。
メンテナンスにするとき
メンテのルールの優先度を 100 → 5 に変えるだけです。
| 優先度 | 条件 | 動作 |
|---|---|---|
| 5 | パス = *
|
メンテ画面を返す ← 先に一致する |
| 10 | パス = *
|
転送(ここまで届かない) |
戻すときは、5 → 100 にします。
固定レスポンスとは
ALB のルールのアクションには「転送」「リダイレクト」のほかに、**「固定レスポンスを返す」**があります。これは、後ろのサーバーに問い合わせず、ALB が設定された内容をそのまま返す機能です。
- レスポンスコード:
503(一時的に利用できない) - コンテンツタイプ:
text/html - 本文: メンテナンス画面の HTML
EC2 にリクエストが届かないので、EC2 が落ちていてもメンテ画面を表示できます。
以前のやり方の問題点
以前は、メンテナンス時に /maintenance.html へリダイレクトする方法を使っていました。
ユーザー → ALB「/maintenance.html へ移動して」(302) → /maintenance.html を EC2 に取りに行く
この方法は、メンテ画面のファイル自体が EC2 上にあるので、EC2 が落ちているとメンテ画面も表示できません。障害のときこそ出したいのに、出せない状態でした。
ほかにも、次の問題がありました。
- URL が
/maintenance.htmlに変わってしまうので、メンテが終わって再読み込みしても、メンテ画面のまま - ステータスが 302(リダイレクト)なので、検索エンジンに「一時的な停止」と伝わらない
固定レスポンスにしたことで、URL はそのまま、ステータスは 503 になり、どちらも解消しました。
注意点
-
優先度の順番を間違えると、全員がメンテ画面になる: 本番で作業するときは、条件を
*にしたメンテのルールを、転送ルールより小さい番号にしないよう注意が必要です。事前に見た目を確認したいときは、条件を/maintenance-previewのような専用パスにして、そのパスだけでメンテ画面を出して確かめました - 本文は 1024 文字まで: メンテ画面の HTML はこの範囲に収める必要があります。画像は外部の URL から読み込む形にしました
- CloudFront がエラー応答を短時間キャッシュする: 503 などのエラーは CloudFront が約 10 秒キャッシュするので、切り替えが 10 秒ほど遅れて反映されることがあります
感想
502 や 504 のようなエラー表示は見た目もよくないですし、一般のユーザーからすると「サイトが壊れた?」と不安になって、信頼を失いかねません。こちらが意図的にメンテナンスしていることを伝えるのが大切だと思います。
今回の方法は手動での切り替えなので、本当は障害時に自動でメンテ画面を表示できるとよいと思っています。CloudFront のカスタムエラーページを使えば、オリジン(ALB や EC2)が 502 / 503 / 504 を返したときに、S3 に置いたメンテ画面を自動で返せるので、次はそれを試してみたいです。


