こんにちは、とりです
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 のリスナールールを作成します
そのあたりの作業は特に特徴はないので、参考になる記事の紹介に留めたいと思います
上記の記事の手順に加えて、 Lambda -> S3 の間に、Gateway 型の VPC エンドポイントを噛ませておけると、S3 のアウトバウンドの転送料金がかからなくなるため、ある程度のトラフィック量が見込まれる場合は入れておくといいかもしれません。
おまけ: メンテナンス画面の表示方法いろいろ
冒頭で軽く触れた通り、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を経由してメンテ画面を表示
- 参考記事:https://aws.amazon.com/jp/blogs/news/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/
- VPC エンドポイント追加して向けるだけなので実装はかなり楽
- インターフェース型 VPC エンドポイントを使うので、コストは結構かかっちゃう
4.Lambda を経由してメンテ画面を表示 (今回の方法)
- 参考記事: https://dev.classmethod.jp/articles/alb-listener-rule-lambda-maintenance-page/
- 実装はまあまあお手軽
- lambda ロジックの管理コストがかかる
- とはいえ layer も不要で、大分軽微な実装なので認知負荷はそんな高くないはず
コメント