ALBリスナールールとLambdaだけでメンテナンスページへの切替が簡単にできる仕組みを作る

2021.01.18

どーもsutoです。

毎日決まった時間帯にWebサイトをメンテナンスページに自動切替を行う仕組みを構築する機会がありましたので、備忘録も含めて本記事に残そうと思います。

作成するリソース

今回作成するリソースは以下となります。(既にALB作成、Webサイト(EC2)へ転送するALBリスナールールを作成済であることを前提とします)

  • メンテナンス画面を表示させるLambda(1)を作成
  • Lambda(1)をターゲットグループに登録し、それをALBリスナールールに設定
  • ALBリスナールールのPriority値を変更するLambda(2)を作成
  • Lambda(2)を定期実行させるEventBridgeを作成

やってみた

メンテナンスページ用Lambdaとターゲットグループの作成

EC2メニューから「ターゲットグループの作成」をクリックし、ターゲットタイプでLambda関数を選択し、「ターゲットグループ名」を任意で入力し「次へ」をクリックします。

「関数を作成します」をクリックし、Lambda作成の画面に飛びます。関数作成の画面で「Serverless Application Repositoryの参照」で「ALB-Lambda-Target-HelloWorld」を検索して選択します。

AWS Lambda」の「関数」画面で、「アプリケーションの設定」の「アプリケーション名」を「ALB-Lambda-Target-HelloWorld」を任意の名前に書き換え(今回はALB-Lambda-Target-maintenanceとしました)デプロイをクリックします。

下記画面が表示され、メンテナンスページを表示させるためのLambdaが作成されました。

ちなみにこのLambdaは裏でCloudFormationスタックが作成しているリソースです。

ターゲットグループの作成の「ターゲットの登録」画面に戻り、先ほど作成したLambdaを選択して「ターゲットグループを作成」をクリックします。これでLambda用ターゲットグループ作成は完了です。

Lambda関数内のhtmlコードを編集する

Lambdaメニューから作成したLambda関数の関数名をクリックし、htmlコードの部分(下画像の11〜25行目)を編集してメンテナンスページを作成します。

今回は以下の例のようにコードを編集してみました。

# coding: UTF-8
def lambda_handler(event, context):
	response = {
		"statusCode": 200,
		"statusDescription": "200 OK",
		"isBase64Encoded": False,
		"headers": {
			"Content-Type": "text/html; charset=utf-8"
	}
	}

	response['body'] = """<html lang="ja">
<head>
	  <meta charset="utf-8">
	  <title>ただいまメンテナンス中です</title>
	  <meta name="description" content="システムメンテナンスのお知らせ">
	  <!--[if lt IE 9]>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
	  <![endif]-->
	</head>
  <body>
    <div style='max-width: 600px; margin: 30px auto;'>
      <div style='border: 1px solid #ccc; padding: 30px; margin-top: 30px;'>
        <h1 style="color: #F00">システムメンテナンスのお知らせ</h1>
        <p>
          いつもご利用いただき、誠にありがとうございます。<br />
          このたびサービス向上のため、システムメンテナンスを実施させていただきます。<br />
          下記日時はサービスをご利用いただけません。</p>
		  <ul>
			<li>2021/1/11(月)AM00:00~2021/1/11(月)AM3:00</li>
		  </ul>
        <p>お客さまには大変ご迷惑をおかけいたしますが、何卒、よろしくお願い申し上げます。
</p>
      </div>
    </div>
  </body>
</html>"""
	return response

ALBリスナールールを編集する

EC2メニューで対象のALBを選択し、リスナールールの編集画面を開きます。

「リスナーの追加」で、既存のEC2(Webサイト)が登録してあるターゲットグループ(test1-tg)を転送先とするルールと、先ほど作ったメンテナンスページ用Lambdaを登録したターゲットグループ(alb-tg-lambda)を転送先とするルールを作成します。

※IFの条件は例としてSourceIPで設定しています

リスナールールのPriority値を変更するLambdaを作成

LambdaメニューでALBリスナールールのPriority値を変更する処理を実行するためのLambdaを新規作成します。(例として関数名は「test-alb-rule」という名前にしています)

上記Lambdaの実行ロールには、ALBへのアクセス権限が必要なため以下のポリシーで作成しています。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:ap-northeast-1:133998493178:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:133998493178:log-group:/aws/lambda/test-alb-rule:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:Describe*",
                "elasticloadbalancing:SetRulePriorities"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

変更処理のコードは以下となります。(今回はPythonで作成しました)

import json
import boto3

def lambda_handler(event, context):
    client = boto3.client('elbv2')

    #リスナールールの優先順位切替。(メンテナンスページを優先して開くようにする)
    responce = client.set_rule_priorities(
        RulePriorities=[
            {
                'RuleArn': 'EC2用ターゲットグループ転送するリスナールールのARN',
                'Priority': 2
            },
            {
                'RuleArn': 'Lambda用ターゲットグループ転送するリスナールールのARN',
                'Priority': 1
            },
        ]
    )
    
    # 変更後の設定を表示
    result = client.describe_rules(
        ListenerArn='ALBリスナーのARN',
    )
        
    return result

実際に上記のLambda関数を手動で実行してみると、

リスナールールの優先順位が入れ替わったことが確認できました。

この状態でWebサイトのURL(ALBのDNS)へアクセスしてみると、

Lambda内に作成したhtmlページが表示され、通常のトップページにはアクセスされないようになりました。

あとは、このLambdaをスケジュール実行させるためにEventBridge等でトリガーを追加すれば完成です。

また、メンテナンスページ→通常のページに切り戻す処理を作りたい場合は、上記のLambda+EventBridgeの設定と同じ仕組みで、コード内の「Priority値」を逆にして作成すればOKです。

まとめ

メンテナンスページの作成に関しては、S3+Cloudfrontで作成する他、今回のようにALBとLambdaだけで比較的簡単に作れる方法もあります。

本記事がどこかでお役に立てれば幸いです。以上、メンテナンスページ自動切替の実装方法について一例のご紹介でした。