ALB + S3 + Lambda で画像やデザイン含むちょっとリッチなメンテナンス画面をお手軽実装で表示させる

TECH記事

こんにちは、とりです

CloudFront や WAF が既に立っている構成では、そちらでメンテ画面を制御する方法がメジャーかと思いますが、そうでない場合 ALB で制御するケースもあるかと思います。

ALB で制御する方法もいろいろアイデアはあるかと思いますが、今回は lambda + S3 のお手軽実装でメンテページを表示させる方法について紹介したいと思います。

構成図

ALB のリスナーに メンテナンス用のLambdaを向いたルールを追加します。

普段は通常時用のリスナールールより優先順位を下げておき、メンテイン時は優先順位を上げることでメンテナンス画面を表示させます

ALB の操作だけでぱっとメンテインできるので、運用が楽ちんで嬉しいですね

Lambda の実装

Lambda コード

メインとなる lambda ロジックはこんな感じで実装をしました

import boto3
import os
import base64

# 環境変数からS3の情報を取得
BUCKET_NAME = os.environ['BUCKET_NAME']

s3 = boto3.client('s3')

def lambda_handler(event, context):
    try:
        # リクエストされたパスからファイル名を取得
        file_name = event.get('path', '').lstrip('/')
        if not file_name:
            file_name = 'maintenance.html'  # デフォルトのファイル名

        # S3バケットからファイルを取得
        s3response = s3.get_object(Bucket=BUCKET_NAME, Key=file_name)
        body = s3response['Body'].read()
        # S3オブジェクトのメタデータからContent-Typeを取得
        content_type = s3response['ContentType']

        # content_type が text 以外の場合は base64 エンコードを挟んでから utf-8 でデコード。
        if 'text' in content_type:
            response_body = body.decode('utf-8')
            is_base64_encoded = False
        else:
            response_body = base64.b64encode(body).decode('utf-8')
            is_base64_encoded = True

        return {
            # メンテ画面のため 正常系だが 503 を返す
            'statusCode': 503,
            'headers': {
                'Content-Type': content_type
            },
            'body': response_body,
            'isBase64Encoded': is_base64_encoded
        }
    except Exception as e:
        return {
            'statusCode': 500,
            'body': f"Error getting the file from S3: {str(e)}"
        }

実装のポイントとしては以下となります

  • event.get('path', '').lstrip('/')で HTTP リクエストからパス部分のみ取り出して、取り出すS3のファイルパスとして使用しています
  • content_type は S3 ファイルのメタタグから Content-type を取得しています
    • 小ネタですが、Content-type を何も指定せずにS3にファイルアップロードした場合は python 製のライブラリによって型を推測するようです(aws cli)
  • text は utf-8 でそのままデコードが可能ですが、画像ファイルなどは 一度 base64 でエンコードしてから utf-8 に直すとうまく表示されました

その他リスナールールやS3の作成

Lamda を作った後は上記構成図の S3 や ALB のリスナールールを作成します

そのあたりの作業は特に特徴はないので、参考になる記事の紹介に留めたいと思います

ALBリスナールールとLambdaだけでメンテナンスページへの切替が簡単にできる仕組みを作る | DevelopersIO
ALBで1025文字以上のメンテナンス画面を表示する方法 - Qiita
背景AWSのALB(Application Load Balancer)のリスナールールを使ってメンテナンス画面を表示することは一般的ですが、ALBのリスナールールには1024文字の制限があります。そのため、1024文字を超えるメンテナンス...

上記の記事の手順に加えて、 Lambda -> S3 の間に、Gateway 型の VPC エンドポイントを噛ませておけると、S3 のアウトバウンドの転送料金がかからなくなるため、ある程度のトラフィック量が見込まれる場合は入れておくといいかもしれません。

EC2 outbound data transfer cost to S3 Gateway Endpoint
If I create an S3 Gateway Endpoint in a VPC and the EC2 instances within the VPC use this S3 Gateway Endpoint to connect...

おまけ: メンテナンス画面の表示方法いろいろ

冒頭で軽く触れた通り、ALB でメンテナンス画面を表示させるにはいろいろな実装方法があります。

個人的な比較を備忘録として残しておきます。

今回はサービスの性質上あまり大量アクセスを見込んでいなかったため、実装のお手軽さを重視して Lambda を採用してみました。トラフィックがある程度見込まれるサービスの場合は、Cloudfront 経由がなんだかんだ綺麗なのかもなという印象でした。

1.ALBの固定レスポンスで返す

  • 参考記事: https://www.netassist.ne.jp/techblog/29666/
  • ALB 単体で完結するため、実装としては一番楽だしコストも安い
  • 別ファイルのCSSや画像の読み込みはできない
  • レスポンスの内容は1024文字までに制限されるのでリッチなページは返せない

2.Cloudfrontを経由してメンテ画面を表示

  • 参考記事:https://techblog.nhn-techorus.com/archives/21488
  • AWS上で完結させる要件だとおそらく一番メジャーな実装方法
  • CDNキャッシュが効く分、早くレスポンスが返る嬉しさがある
  • CDNキャッシュされて、S3へのリクエスト量が節約できるのでコストメリットがある
  • この中だと実装が比較的重め (IaC してる場合だと設定値多いリソースなので特に)
  • 別ドメインにリダイレクトさせる実装になるので、F5 リロードでメンテ明け待機してる勢から不満があったりなかったりという話も

3.Privatelinkを経由してメンテ画面を表示

4.Lambda を経由してメンテ画面を表示 (今回の方法)

コメント

タイトルとURLをコピーしました