※本記事は [ストックリスト:AWS CLIでゼロから構築するWeb監視システム&自動コスト防衛] の第2弾です。
1. はじめに
前回の記事では、AWSの各種サービスを組み合わせて「運用コスト0円(無料枠内)」で稼働するWeb配信・死活モニタリングシステムの構成概要について紹介しました。
👉 [前回の記事] https://qiita.com/4eena/items/a83c455d9c7ad3ded52f
今回は、実際の構築手順、ハマったポイント(IAM権限やCloudFrontのキャッシュ制御など)、およびセキュリティや運用性を意識した設計のポイントをハンズオン形式で詳しく解説します。
2. 全体構成と事前準備
システム構成
- 実行・配信: EventBridge ➔ Lambda ➔ S3 ➔ CloudFront
- 監視・通知: CloudWatch ➔ SNS ➔ Amazon Q Developer ➔ Slack
前提条件
- AWSアカウントが開設済みであること
- Slackワークスペースおよび通知用チャンネル(例:
#alert)が作成済みであること
3. 構築手順(ステップ・バイ・ステップ)
STEP 1:S3バケットとCloudFrontの作成(OACによるセキュリティ強化)
-
S3バケットの作成
- パブリックアクセスはすべてブロックに設定します(S3への直接アクセスを遮断し、CloudFront経由のみ許可するため)。
-
CloudFrontディストリビューションの作成
- オリジンに作成したS3バケットを指定します。
- **OAC(Origin Access Control)**を作成・設定し、S3側のバケットポリシーにCloudFrontからのアクセス許可を自動付与します(※従来型のOAIではなく、よりセキュアなOACを採用)。
STEP 2:Lambda関数の作成とS3連携
-
Lambda関数の作成
- ランタイム: Python 3.12(または使用したバージョン)を選択。
- 処理概要: モニタリング結果を反映させた
index.htmlを動的に生成し、S3バケットへアップロードします。
💻 Lambdaサンプルコード(lambda_function.py)
import json
import boto3
import os
from datetime import datetime, timezone, timedelta
s3 = boto3.client('s3')
# 環境変数からバケット名を取得(または直接指定)
BUCKET_NAME = os.environ.get('BUCKET_NAME', 'your-s3-bucket-name')
def lambda_handler(event, context):
# 日本時間の現在時刻を取得
JST = timezone(timedelta(hours=+9))
now = datetime.now(JST).strftime('%Y-%m-%d %H:%M:%S')
# 配信・モニタリング用のHTMLコンテンツを作成
html_content = f"""<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>WEBサイト死活・検索モニタリング</title>
</head>
<body>
<h1>WEBサイト死活・検索モニタリングシステム</h1>
<p>ステータス: 正常稼働中</p>
<p>最終更新日時 (JST): {now}</p>
</body>
</html>
"""
try:
# S3バケットへindex.htmlをアップロード
s3.put_object(
Bucket=BUCKET_NAME,
Key='index.html',
Body=html_content.encode('utf-8'),
ContentType='text/html; charset=utf-8'
)
return {
'statusCode': 200,
'body': json.dumps('index.html updated successfully!')
}
except Exception as e:
print(f"Error updating S3: {str(e)}")
raise e
-
IAMロールの設定
- Lambdaの実行ロールに、対象S3バケットへの
s3:PutObject権限を付与します。
- Lambdaの実行ロールに、対象S3バケットへの
STEP 3:EventBridgeによる5分間隔の定時実行
-
Amazon EventBridgeスケジューラの作成
- 配信の定期化のため、スケジュールタイプで「定期的なスケジュール(rate式)」を選択。
-
rate(5 minutes)に設定し、ターゲットとして作成したLambda関数を指定します。
STEP 4:CloudWatch × SNS × Slackでの監視・通知設定
-
Amazon SNSトピックの作成
- エラー通知用のSNSトピックを作成します。
-
CloudWatch Alarmの設定
- 評価期間・間隔を「5分」に設定(AWS無料枠に確実に収めるため)。
- エラー検出時にSNSトピックへ通知を飛ばすアクションを設定します。
-
Amazon Q Developerを用いたSlack連携(UX向上)
- 障害時は「赤色(🔴)」、正常復帰や正常ログは各種「緑色(🟢)」で表示されるよう通知メッセージを調整し、ひと目でステータスが判断できる運用視点の設計に工夫しました。
4. 構築中にハマったポイントと対策(トラブルシューティング)
実際に構築を進める中で直面した課題と、その解決プロセスです。
① IAM権限およびEventBridgeトリガーの設定不整合
- 現象: 動作確認時、Lambdaは動いているのにSlackへ通知が届かない。
- 原因・対策: CloudWatch Logsを確認したところ、SNSへのパブリッシュ権限およびEventBridgeからの呼び出し権限の記述に不足がありました。ログを追って不足しているIAMアクションを特定し、**必要最小限の権限(Least Privilege)**を付与し直すことで解決しました。
② 検証・テスト段階での通知・ログの過剰発生
- 現象: テスト運用中、5分間隔で大量のエラーログや通知が走り、視認性が低下した。
- 対策: CloudWatchのアラーム閾値や評価期間を見直し、開発中のテスト通知と本番アラートのロジックを整理することで過剰通知を防ぎました。
③ CloudFrontのキャッシュ(TTL)による表示遅延
-
現象: LambdaでS3上の
index.htmlを更新したにもかかわらず、ブラウザ側で古い画面が表示され続ける。 - 対策: CloudFrontのキャッシュ設定(TTL: Time To Live)およびInvalidation(キャッシュ無効化)の動作を理解し、最新状態が適切に反映されるようエッジでの保持時間を最適化しました。
5. まとめと今後の展望(Next Step)
今回の構築を通じて、単にサービスを繋ぎ合わせるだけでなく以下のノウハウを得ることができました。
- 0円運用の設計力: メトリクス間隔(5分)やリクエスト数を計算し、確実にAWS無料枠内に収める調整手法。
- インフラのトラブルシューティング力: エラー時にCloudWatch Logsを起点として権限や通信経路の原因を特定するプロセス。
- セキュリティと配信効率: OACによるS3保護と、TTL調整によるキャッシュ制御のバランス思考。
🚀 今後の展望(Next Step)
今後はさらにシステムを発展させるため、以下の拡張に挑戦したいと考えています。
- 単一ページの死活監視だけでなく、ステータスコード(404, 500等)の詳細分析と複数ドメイン監視への拡張。
- **AWS Cost Anomaly Detection(コスト異常検出)**を導入し、無料枠超過や予期せぬリクエスト急増に対するガードレール構築。