1. はじめに
オンプレ歴5年以上のサーバ・NW インフラエンジニアです。(クラウド未経験)
SAA(ソリューションアーキテクト – アソシエイト)を苦労の末、取得したものの、「知識を実際のハンズオンで活かしたい」「ポートフォリオとして残したい」と考え、今回サーバーレス構成構築に挑戦してみました。
「費用を一切かけない(完全無料枠運用)」かつ「本番環境を意識したセキュリティ設計」を意識して作成したポートフォリオの概要をご紹介します。
2. 参考としたAWS SAA問題文
構築するにあたってSAAの問題と正解とされる選択肢から実際にその構成を作ってみたいと思いました。
以下のUdemy問題文と回答を参考にSAAで頻出するサーバーレスのベストプラクティス構成を実際に構築しました。
Q38
ある企業が、世界中のユーザーから問い合わせを受け付けるWebアプリケーションを構築しています。このアプリケーションの静的コンテンツ(HTML/CSS/JavaScript)はS3バケットで保持され、動的なバックエンド処理はユーザーが入力したデータをデータベースに永続化する必要があります。
ソリューションアーキテクトは、以下の要件を満たすアーキテクチャを設計することを求められています。
- サーバー管理の手間(運用オーバーヘッド)を最小限に抑えること。
- 予期せぬトラフィックの急増に対応できる自動スケーラビリティを備えていること。
- 静的コンテンツへの直接アクセスを制限し、CDN経由でのみセキュアにアクセスさせること。
- 最もコスト効率が高い構成を採用すること
これらの要件をすべて満たす最適な組み合わせはどれですか?
【選択肢】
A. 静的コンテンツをAmazon S3バケットに配置し、Amazon CloudFrontディストリビューションを作成してOrigin Access Control (OAC) を設定する。バックエンド処理用にAmazon API GatewayとAWS Lambdaを構成し、データをAmazon DynamoDBに保存する。B. Amazon EC2インスタンス上でWebサーバーとアプリケーションサーバーを立ち上げ、Application Load Balancer (ALB) の背後に配置する。データベースにはAmazon RDS for PostgreSQLマルチAZ構成を使用し、静的コンテンツはAmazon EFSに保存する。
C. 静的コンテンツをAmazon S3バケットでウェブサイトホスティングとして公開し、バケットポリシーで全アクセスをパブリック許可する。バックエンド用にAWS Fargate上でコンテナを実行し、Amazon Aurora Serverlessにデータを保存する。
D. Amazon CloudFrontのオリジンとしてAmazon EC2インスタンスを設定し、Auto Scalingグループを構成する。APIのルーティング用にAmazon Route 53を使用し、データはAmazon ElastiCache (Redis) に保存する。
3. ポートフォリオ
今回構築した環境の全コードは、以下の GitHub リポジトリで公開しています。
以下が自作した問い合わせフォームとなります。
- 問い合わせフォーム
また管理者向け画面としてDynamoDBのテーブルが出力されるようにしました。
問い合わせフォームで「送信する」とここに反映されます。
- 管理画面 お問い合わせ一覧
4. ポートフォリオ概要・構成図
S3に静的ファイルを配置しておいて問い合わせフォームとし、ユーザーからのアクセスをS3に誘導した上で、
問い合わせフォームから送信されたデータが、API Gateway・Lambdaを経由してDynamoDBに自動保存される構成です。
使用サービス・技術スタックと役割
-
フロントエンド: HTML5 / JavaScript (Fetch API) / CSS
- 問い合わせフォームまたは管理画面表示と、API通信処理を担当
-
ホスティング・CDN: Amazon S3 / Amazon CloudFront
- S3で静的ファイル(
index.html)を非公開保管(Frontのみ許可)し、CloudFrontでHTTPS化&高速エッジ配信
- S3で静的ファイル(
-
API・バックエンド: Amazon API Gateway (HTTP API) / AWS Lambda (Python 3.12)
- API Gatewayで外部からのリクエストを受信・CORS制御し、LambdaのPythonコードでユーザ用(POST)と管理者用(GET)のルーティング・CORS制御およびデータ処理を実行
-
データベース: Amazon DynamoDB
- 送信された問い合わせデータを自動保存
-
セキュリティ・管理: AWS IAM (OAC / Role) / AWS Budgets
- OACやロールによる最小権限のアクセス制御で強固に保護し、Budgetsのアラートで意図しない課金を完全防止
5. 設計でこだわった点(SAA知識の実践)
SAA問題文の選択肢の通りに構築したといえばそこまでですが、本設計のこだわりポイントをいくつか挙げます。
① CloudFront + S3 による OAC(Origin Access Control)接続
回答にもありましたが、S3バケットへの直接アクセス(パブリックアクセス)はすべてブロックし、CloudFront経由の通信のみを許可する OAC設定 を採用しました。これにより、静的ファイルのセキュアな配信を実現しています。
② CORS対応とサーバーレスAPI構成
API Gateway (HTTP API) と Lambda 間で適切な CORS(Cross-Origin Resource Sharing) ヘッダー(Access-Control-Allow-Origin: * 等)を設定し、ブラウザからのリクエストを安全に処理できるようにしました。
③ サーバーレスAPIと相性のよいDynamoDB
今回はアクセスの波が予測しづらい問い合わせフォームであり、複雑なテーブル結合(JOIN)も不要なため、運用コスト0円かつ Lambda との接続相性が最も良い DynamoDB を選択しました。
複雑なトランザクション処理や集計が必要になった場合は、RDS や Aurora Serverless の導入も選択肢としては出てきますが、SQL不要というのと、最低稼働容量が決まっているため、採用しませんでした。
④ コストゼロ運用の徹底(ゼロ支出予算の設定)
無課金でやりたかったので、すべての構成を 常時無料枠(Always Free) の範囲に収めました。
更に AWS Budgets で「ゼロ支出予算(0.01ドル超過で通知)」 を設定することで大量アクセスによるコスト増大にも備えてます。
6. 構築のながれ(ロードマップ)
細かい手順は省略しますが、以下の流れで段階的に構築・動作確認を進めました。
-
環境準備とセキュリティ設定
- IAMユーザーの作成およびAWS Budgets(ゼロ支出アラート)を有効化しました
-
データベース(DynamoDB)の作成
- 問い合わせデータを格納する
ContactTableのテーブルを作成。
S3に入れる将来的な問い合わせフォームをイメージしながら、パーティションキー(id 元々入ってる)・name(名前)・email(メールアドレス)・message(問い合わせ本文)をデータの属性として入れました。
- 問い合わせデータを格納する
-
バックエンド(Lambda & API Gateway)の作成
- PythonでDynamoDBへデータを保存する処理を実装。
※AIに状況と以下要件を伝えたらWeb問い合わせフォーム用のバックエンドコードを作成してくれました。
- API Gateway 経由でname(名前)・email(メールアドレス)・message(問い合わせ本文)が連携されてくるので処理してほしい
- 保存するデータ項目 (Item)も同様 idは自動生成でOK DynamoDB の
ContactTableテーブルにデータを保存 - エラー処理時は ステータスコード 500 とエラーメッセージを返す(CORSヘッダーも付与)。
- 処理成功時は ステータスコード 200 と成功メッセージ、保存したデータ(JSON)を返す
- PythonでDynamoDBへデータを保存する処理を実装。
🐍 Python(AWS Lambda)コード全文を表示する
```python import json import boto3 import uuid from datetime import datetimeDynamoDBのリソースを初期化
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('ContactTable')
def lambda_handler(event, context):
try:
# API Gateway経由、または直接受け取るリクエストボディを解析
if 'body' in event and event['body']:
body = json.loads(event['body']) if isinstance(event['body'], str) else event['body']
else:
body = event
# 入力データの取得(データがない場合のデフォルト値を設定)
name = body.get('name', '匿名')
email = body.get('email', 'no-email@example.com')
message = body.get('message', '本文なし')
# 保存するデータの作成
item = {
'id': str(uuid.uuid4()), # ランダムなユニークIDを自動生成
'name': name,
'email': email,
'message': message,
'createdAt': datetime.utcnow().isoformat() + 'Z'
}
# DynamoDBに書き込み
table.put_item(Item=item)
# レスポンスの返却(CORSヘッダーを含む)
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'OPTIONS,POST'
},
'body': json.dumps({
'message': '問い合わせが正常に保存されました!',
'data': item
}, ensure_ascii=False)
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'headers': {
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps({'error': str(e)})
}
- API Gateway(HTTP API)を作成し、Lambdaと統合&CORSの有効化。
'4. フロントエンド(S3 & CloudFront)の構築
-
index.html(Fetch APIでPOST送信)の作成とS3へのアップロード。 -
CloudFrontのディストリビューションを作成し、OACによるS3アクセス許可とHTTPS化を設定。
'5. エンドツーエンドの導線テスト
- CloudFrontのURLからフォームを送信し、DynamoDBにリアルタイムでレコードが保存されることを確認。
7.⚠️ 構築時にハマった・注意すべきポイント(初心者が陥りやすい罠)
実際に手を動かして構築・動作検証を進める中で、特につまづいた点や見落としがちなポイントをまとめました。
1. API Gateway と フロントエンドでの CORS エラー
-
ハマりポイント: フロントエンド(CloudFront)から Fetch API で POST リクエストを投げた際、ブラウザのデベロッパーツールで CORS エラー(
Failed to fetch)が発生。 -
原因と対策:
- API Gateway 側で CORS(
Access-Control-Allow-Origin: *やPOST, OPTIONSメソッド)を正しく許可設定する必要がある。 -
Lambda 側のレスポンスヘッダー にも
Access-Control-Allow-Originを明示的に含めて返す必要がある。
- API Gateway 側で CORS(
2. API エンドポイントのパス(/contact)の指定漏れ
-
ハマりポイント:
index.html内のAPI_URLに API Gateway のベースURLのみを設定しており、送信時に 404 エラーや不整合が発生。 -
対策: API Gateway で設定したルーティングパス(例:
https://xxxx.execute-api.ap-northeast-1.amazonaws.com/contact)まで正確に記述されているか、HTML側のJSコードを確認することが重要。
3. CloudFront + S3 の OAC(Origin Access Control)設定とバケットポリシー適用
-
ハマりポイント: S3バケットを非公開(パブリックアクセスブロック)にした後、CloudFront 経由でアクセスしても
403 Forbiddenが発生。 -
対策:
- CloudFront 作成時に OAC 設定 を作成して紐付ける。
- 自動生成された S3 バケットポリシーをコピーし、S3側の「アクセス許可」タブに正しく貼り付ける 手順を忘れない(CloudFront作成後に手動でS3側にポリシーを保存するプロセスが必要)。
4. S3 更新後の CloudFront キャッシュ(Invalidation)
-
ハマりポイント:
index.htmlのバグ(API URLの修正など)を直して S3 に再アップロードしたのに、CloudFront の URL にアクセスすると古い画面が表示され続ける。 -
対策: CloudFront はエッジサーバーにキャッシュを保持するため、S3 変更後は CloudFront コンソールから 「キャッシュの無効化(Invalidation)」を作成(
/*を指定) してキャッシュをクリアする必要がある。
5. デフォルトルートオブジェクト(index.html)の設定漏れ
-
ハマりポイント: CloudFront のドメイン名(
https://xxx.cloudfront.net)にアクセスした際、末尾に/index.htmlを付けないとエラーになる。 -
対策: CloudFront ディストリビューション設定の 「Default root object」に
index.htmlを設定 しておくことで、ルートURLへのアクセス時に自動で TOP ページが開くようになる。
8. まとめ
SAAの勉強で得た机上の知識を、実際のAWSコンソールで手を動かして形にすることで、サービス同士のアクセス許可(IAM/OAC)やCORSの設定など、実践的なエラーハマりポイントと解決法を学ぶことができました。
今後は構成のIaC化(TerraformやAWS CDK)などにも挑戦していきたいと思います!
