ハンズオン: サーバーレスアーキテクチャ ~AWS認定デベロッパーアソシエイト(DCA-C02)~
このブログは2026年6月29日翔泳社さんより発売される「AWS教科書 AWS認定デベロッパーアソシエイト テキスト&問題集」で扱う内容を体験していただくためのハンズオンガイドです。
このハンズオンガイドでは、サーバーレスアーキテクチャを構成する主要サービス(S3、DynamoDB、Lambda、API Gateway、SNS、SQS)を使って、アンケートフォームのバックエンドを構築します。各タスクでサービスを1つずつ作成し、最終的にそれらを組み合わせて動作させます。
目次
全体構成
東京リージョンで構築してください。
完成するアーキテクチャは以下のとおりです。
(1) S3バケットに静的なHTMLアンケートフォームを配置して公開する
(2) フォームからAPI Gateway(REST API)にPOSTリクエストを送信する
(3) API Gatewayが受付用Lambda関数を実行する
(4) 受付用Lambda関数がSNSトピックにメッセージをパブリッシュする
(5) SNSトピックからSQSキューにメッセージがファンアウトされる
(6) SNSトピックからメール通知が送信される(フィルターポリシーで評価1のみ)
(7) SQSキューをトリガーに書き込み用Lambda関数が実行される
(8) 書き込み用Lambda関数がDynamoDBテーブルにアンケート内容を保存する
アンケートフォームでは「感想(自由記述)」と「友達に勧めたいかどうかの5段階評価」を入力します。
評価が1(最低評価)の回答があった場合のみ、管理者にメール通知が届くようにSNSのサブスクリプションフィルターを設定します。
準備:IAMポリシーの作成とアタッチ
ハンズオンで使用するIAMユーザーまたはIAMロールに、以下のポリシーをアタッチしてください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DynamoDBAccess", "Effect": "Allow", "Action": [ "dynamodb:CreateTable", "dynamodb:DeleteTable", "dynamodb:DescribeTable", "dynamodb:UpdateTable", "dynamodb:PutItem", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:ListTables" ], "Resource": "*" }, { "Sid": "LambdaAccess", "Effect": "Allow", "Action": [ "lambda:CreateFunction", "lambda:DeleteFunction", "lambda:Get*", "lambda:InvokeFunction", "lambda:UpdateFunctionCode", "lambda:AddPermission", "lambda:RemovePermission", "lambda:CreateEventSourceMapping", "lambda:DeleteEventSourceMapping", "lambda:List*", "lambda:PublishVersion", "lambda:CreateAlias", "lambda:UpdateAlias" ], "Resource": "*" }, { "Sid": "IAMRoleAccess", "Effect": "Allow", "Action": [ "iam:CreateRole", "iam:DeleteRole", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:PassRole", "iam:Get*", "iam:List*" ], "Resource": "*" }, { "Sid": "APIGatewayAccess", "Effect": "Allow", "Action": [ "apigateway:*" ], "Resource": "*" }, { "Sid": "S3Access", "Effect": "Allow", "Action": [ "s3:CreateBucket", "s3:DeleteBucket", "s3:PutObject", "s3:Get*", "s3:DeleteObject", "s3:List*", "s3:PutBucketPolicy", "s3:DeleteBucketPolicy", "s3:PutBucketWebsite", "s3:DeleteBucketWebsite", "s3:PutBucketPublicAccessBlock" ], "Resource": "*" }, { "Sid": "SNSAccess", "Effect": "Allow", "Action": [ "sns:CreateTopic", "sns:DeleteTopic", "sns:Subscribe", "sns:Unsubscribe", "sns:Publish", "sns:GetTopicAttributes", "sns:SetTopicAttributes", "sns:SetSubscriptionAttributes", "sns:GetSubscriptionAttributes", "sns:ListTopics", "sns:ListPlatformApplications", "sns:ListSubscriptions", "sns:ListSubscriptionsByTopic", "sns:ListTagsForResource" ], "Resource": "*" }, { "Sid": "SQSAccess", "Effect": "Allow", "Action": [ "sqs:CreateQueue", "sqs:DeleteQueue", "sqs:SendMessage", "sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes", "sqs:SetQueueAttributes", "sqs:GetQueueUrl", "sqs:ListQueues" ], "Resource": "*" }, { "Sid": "CloudWatchLogsAccess", "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogGroups", "logs:DescribeLogStreams", "logs:GetLogEvents" ], "Resource": "*" }, { "Sid": "CloudShellAccess", "Effect": "Allow", "Action": [ "cloudshell:*" ], "Resource": "*" } ] } |
タスク 1:DynamoDBテーブルを作成する
目的
アンケート回答を保存するDynamoDBテーブルを作成しましょう。グローバルセカンダリインデックス(GSI)も作成して、評価段階での検索を可能にします。
手順
(1) マネジメントコンソール上部の検索フィールドにDynamoDBと入力し、DynamoDBコンソールにアクセスします。
(2) リージョンがap-northeast-1(東京)であることを確認します。
(3) 左メニューから[テーブル]-[テーブルの作成]をクリック。
(4) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| テーブル名 | SurveyResponses |
| パーティションキー | ResponseId (文字列) |
| ソートキー | ResponseDate (文字列) |
| テーブル設定 | 設定をカスタマイズ |
| 読み込み/書き込みキャパシティモード | オンデマンド |
(5) [セカンダリインデックス]セクションで[グローバルインデックスの作成]をクリック。
(6) 以下の設定でGSIを作成します:
| 項目 | 設定値 |
|---|---|
| パーティションキー | Rating (数値) |
| ソートキー | ResponseDate (文字列) |
| インデックス名 | Rating-ResponseDate-index |
(7) [インデックスを作成]をクリック。
(8) [テーブルの作成]をクリック。
(9) テーブルのステータスがアクティブになることを確認します。
ポイント
- パーティションキー(ResponseId)とソートキー(ResponseDate)の組み合わせでプライマリキーを構成しています。
- GSIを作成することで、評価段階(Rating)をキーにした検索が可能になります。例えば「評価1の回答だけを抽出する」といったクエリが効率的に実行できます。
- オンデマンドモードはリクエスト数に応じた課金なので、開発やテスト用途に適しています。
タスク 2:SNSトピックを作成する
目的
アンケート回答を受け付けたときに通知を配信するためのSNSトピックを作成しましょう。
手順
(1) マネジメントコンソール上部の検索フィールドにSNSと入力し、Amazon SNSコンソールにアクセスします。
(2) 左メニューから[トピック]-[トピックの作成]をクリック。
(3) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| タイプ | スタンダード |
| 名前 | SurveyNotification |
(4) その他の設定はデフォルトのまま[トピックの作成]をクリック。
(5) トピックが作成されたら、トピックARNをメモしておきます。
ポイント
- SNSはPub/Sub(パブリッシュ/サブスクライブ)モデルのメッセージングサービスです。
- このあとのタスクで、メール通知とSQSキューの2つのサブスクリプションを追加します。
タスク 3:SQSキューを作成してSNSにサブスクライブする
目的
アンケート回答をDynamoDBに書き込むための中間キューとして、SQSキューを作成しましょう。SNSからのサブスクリプションとして設定します。
手順
(1) マネジメントコンソール上部の検索フィールドにSQSと入力し、Amazon SQSコンソールにアクセスします。
(2) まずデッドレターキューを作成します。[キューを作成]をクリック。
(3) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| タイプ | 標準 |
| 名前 | SurveyQueue-DLQ |
(4) その他の設定はデフォルトのまま[キューを作成]をクリック。
(5) 次にメインのキューを作成します。[キューを作成]をクリック。
(6) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| タイプ | 標準 |
| 名前 | SurveyQueue |
| 可視性タイムアウト | 60秒 |
| メッセージ保持期間 | 4日(デフォルト) |
(7) [デッドレターキュー]セクションで以下を設定します:
| 項目 | 設定値 |
|---|---|
| デッドレターキューの有効化 | オン |
| キューの選択 | SurveyQueue-DLQ |
| 最大受信数 | 3 |
(8) [キューを作成]をクリック。
(9) キューが作成されたら、SurveyQueueのARNをメモしておきます。
(10) 次に、SNSトピックからこのキューにメッセージを送信できるようにします。SNSコンソールに移動し、SurveyNotificationトピックを開きます。
(11) [サブスクリプションの作成]をクリック。
(12) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| プロトコル | Amazon SQS |
| エンドポイント | SurveyQueueのARN |
| rawメッセージ配信の有効化 | チェックを入れる |
(13) [サブスクリプションの作成]をクリック。
これでSNSトピックにパブリッシュされたメッセージが、SQSキューに配信されるようになります。
SNSコンソールからサブスクライブすると、キューポリシー(SNSからのSendMessage許可)も自動的に設定されます。
rawメッセージ配信を有効にすることで、SNSのメタデータなしでメッセージ本文だけがキューに送信されます。
ポイント
- SQSはメッセージキューサービスで、サービス間の疎結合化を実現します。
- SNSとSQSを組み合わせたファンアウトパターンにより、1つのメッセージを複数の処理に並列で配信できます。
- rawメッセージ配信を有効にすることで、後続のLambda関数のコードがシンプルになります。
- デッドレターキュー(DLQ)を設定することで、処理に3回失敗したメッセージがDLQに移動します。DLQのメッセージを確認して問題の調査ができます。
- 可視性タイムアウトを60秒に設定して、並列実行されるLambda関数がメッセージを重複受信しないようにしています。
- キューを挟むことで、DynamoDBへの書き込みが一時的に失敗しても、メッセージがキューに残りリトライできます。
タスク 4:SNSにメール通知サブスクリプションを追加する(フィルターポリシー付き)
目的
評価が1(最低評価)の回答があったときだけ管理者にメール通知が届くように、サブスクリプションフィルターを設定しましょう。
手順
(1) SNSコンソールでSurveyNotificationトピックを開きます。
(2) [サブスクリプションの作成]をクリック。
(3) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| プロトコル | Eメール |
| エンドポイント | 自分のメールアドレス |
(4) [サブスクリプションフィルターポリシー]セクションを展開します。
(5) [サブスクリプションフィルターポリシー]を有効にして、フィルターポリシーの適用先に[メッセージ属性]を選択します。
(6) 以下のJSONを入力します:
|
1 2 3 4 |
{ "rating": [1] } |
(7) [サブスクリプションの作成]をクリック。
(8) 入力したメールアドレスに確認メールが届くので、メール内の[Confirm subscription]リンクをクリックして確認します。
ポイント
- サブスクリプションフィルターポリシーを使うと、メッセージ属性の値に基づいて特定のサブスクリプションにだけメッセージを配信できます。
- この設定により、評価が1の回答があったときだけ管理者にメールが届きます。評価2〜5の回答ではメールは送信されません。
- フィルターポリシーはSNS側で評価されるため、不要なメッセージがサブスクリプションに届くことを簡単に防げます。
- パブリッシュ時にメッセージ属性としてratingを設定する必要があります(受付用Lambda関数で対応します)。
タスク 5:受付用Lambda関数を作成する
目的
API Gatewayからのリクエストを受け取り、SNSトピックにアンケート内容をパブリッシュする受付用Lambda関数を作成しましょう。
手順
(1) Lambdaコンソールにアクセスし、[関数の作成]をクリック。
(2) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| 作成方法 | 一から作成 |
| 関数名 | SurveyReceiver |
| ランタイム | Python 3.14 |
| アーキテクチャ | x86_64 |
(3) [関数の作成]をクリック。
(4) 関数が作成されたら、[設定]タブ-[アクセス権限]をクリックし、ロール名のリンクをクリックしてIAMコンソールを開きます。
(5) [許可を追加]-[インラインポリシーを作成]をクリックし、[JSON]タブで以下のポリシーを貼り付けます。
|
1 2 3 4 5 6 7 8 9 10 11 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sns:Publish", "Resource": "arn:aws:sns:ap-northeast-1:*:SurveyNotification" } ] } |
(6) ポリシー名をSurveyReceiverSNSPolicyとして保存します。
(7) Lambdaコンソールに戻り、[コード]タブでコードエディタに以下のコードを貼り付けます:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
import json import boto3 import uuid from datetime import datetime, timezone, timedelta sns = boto3.client('sns', region_name='ap-northeast-1') def lambda_handler(event, context): # リクエストボディを取得 body = json.loads(event['body']) # 入力値を取得 comment = body.get('comment', '') rating = int(body.get('rating', 3)) # 一意のIDと日時を生成 response_id = str(uuid.uuid4()) jst = timezone(timedelta(hours=9)) response_date = datetime.now(jst).isoformat() # SNSにパブリッシュするメッセージを構築 message = json.dumps({ 'responseId': response_id, 'responseDate': response_date, 'comment': comment, 'rating': rating }, ensure_ascii=False) # アカウントIDを取得してトピックARNを構築 sts = boto3.client('sts') account_id = sts.get_caller_identity()['Account'] topic_arn = f"arn:aws:sns:ap-northeast-1:{account_id}:SurveyNotification" # SNSにパブリッシュ(メッセージ属性にratingを設定) sns.publish( TopicArn=topic_arn, Message=message, MessageAttributes={ 'rating': { 'DataType': 'Number', 'StringValue': str(rating) } } ) return { 'statusCode': 200, 'headers': { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }, 'body': json.dumps({ 'message': 'アンケートを受け付けました', 'responseId': response_id }) } |
(8) [Deploy]をクリックしてコードをデプロイします。
(9) バージョンを発行します。[バージョン]タブ-[新しいバージョンを発行]をクリックし、説明に「初回リリース」と入力して[発行]をクリック。
(10) エイリアスを作成します。[エイリアス]タブ-[エイリアスを作成]をクリックし、以下を設定します:
| 項目 | 設定値 |
|---|---|
| 名前 | prod |
| バージョン | 1 |
(11) [保存]をクリック。
(12) [テスト]タブでテストイベントを作成して動作確認します。以下のJSONをテストイベントに設定します:
|
1 2 3 4 |
{ "body": "{\"comment\": \"とても良いサービスでした\", \"rating\": 5}" } |
(13) [テスト]をクリックして実行し、ステータスコード200が返ることを確認します。
ポイント
- この関数はSNSにパブリッシュするだけのシンプルな受付処理です。実際のDB書き込みは後続のLambda関数が担当します。
- MessageAttributesにratingを数値型で設定することで、SNSのサブスクリプションフィルターが評価値を判定できるようになります。
- メッセージ本文(Message)はJSON文字列で、後続のSQS→Lambda→DynamoDB書き込みに必要な情報が含まれています。
- バージョンを発行してエイリアスprodを作成することで、コードの更新時にエイリアスの向き先を変えるだけでリリースやロールバックが可能になります。
タスク 6:書き込み用Lambda関数を作成する
目的
SQSキューからメッセージを受信して、DynamoDBテーブルにアンケート回答を書き込むLambda関数を作成しましょう。
手順
(1) Lambdaコンソールで[関数の作成]をクリック。
(2) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| 作成方法 | 一から作成 |
| 関数名 | SurveyWriter |
| ランタイム | Python 3.14 |
| アーキテクチャ | x86_64 |
(3) [関数の作成]をクリック。
(4) [設定]タブ-[アクセス権限]をクリックし、ロール名のリンクをクリックしてIAMコンソールを開きます。
(5) [許可を追加]-[インラインポリシーを作成]をクリックし、[JSON]タブで以下のポリシーを貼り付けます:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "dynamodb:PutItem", "Resource": "arn:aws:dynamodb:ap-northeast-1:*:table/SurveyResponses" }, { "Effect": "Allow", "Action": [ "sqs:ReceiveMessage", "sqs:DeleteMessage", "sqs:GetQueueAttributes" ], "Resource": "arn:aws:sqs:ap-northeast-1:*:SurveyQueue" } ] } |
(6) ポリシー名をSurveyWriterPolicyとして保存します。
(7) Lambdaコンソールに戻り、[コード]タブで以下のコードを貼り付けます:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import json import boto3 dynamodb = boto3.client('dynamodb', region_name='ap-northeast-1') def lambda_handler(event, context): for record in event['Records']: # rawメッセージ配信が有効なので、メッセージ本文がそのまま届く body = json.loads(record['body']) # DynamoDBに書き込み dynamodb.put_item( TableName='SurveyResponses', Item={ 'ResponseId': {'S': body['responseId']}, 'ResponseDate': {'S': body['responseDate']}, 'Comment': {'S': body['comment']}, 'Rating': {'N': str(body['rating'])} } ) print(f"書き込み完了: {body['responseId']}") return {'statusCode': 200} |
(8) [Deploy]をクリックしてコードをデプロイします。
(9) バージョンを発行します。[バージョン]タブ-[新しいバージョンを発行]をクリックし、説明に「初回リリース」と入力して[発行]をクリック。
(10) エイリアスを作成します。[エイリアス]タブ-[エイリアスを作成]をクリックし、以下を設定します:
| 項目 | 設定値 |
|---|---|
| 名前 | prod |
| バージョン | 1 |
(11) [保存]をクリック。
(12) prodエイリアスにSQSトリガーを設定します。[エイリアス]タブでprodをクリックし、[設定]タブ-[トリガー]-[トリガーを追加]をクリック。
(13) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| ソース | SQS |
| SQSキュー | SurveyQueue |
| バッチサイズ | 10 |
(14) [追加]をクリック。
ポイント
- SQSをトリガーにしたLambda関数はプルイベントモデルです。LambdaサービスがSQSキューをポーリングしてメッセージを取得します。
- Lambda関数のIAMロールにSQSからのReceiveMessage、DeleteMessage権限が必要です。
- rawメッセージ配信を有効にしているため、パブリッシュしたメッセージ本文がそのまま届きます。コードがシンプルになります。
- 処理が正常完了すると、Lambdaサービスが自動的にSQSからメッセージを削除します。
- SQSトリガーをprodエイリアスに設定することで、$LATEST(開発中のコード)ではなく、本番用のバージョンでメッセージが処理されます。
タスク 7:API Gatewayを作成する
目的
アンケートフォームからのリクエストを受け付けるREST APIを作成し、受付用Lambda関数と統合しましょう。
手順
(1) マネジメントコンソール上部の検索フィールドにAPI Gatewayと入力し、API Gatewayコンソールにアクセスします。
(2) [APIの作成]をクリック。
(3) REST APIの[構築]をクリック。
(4) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| APIの詳細 | 新しいAPI |
| API名 | SurveyAPI |
| 説明 | アンケートフォームAPI |
| APIエンドポイントタイプ | リージョン |
(5) [APIを作成]をクリック。
(6) [リソースを作成]をクリックし、リソース名にsurveyと入力します。[CORS(クロスオリジンリソース共有)]にチェックを入れて[リソースを作成]をクリック。
(7) /surveyリソースを選択した状態で[メソッドを作成]をクリック。
(8) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| メソッドタイプ | POST |
| 統合タイプ | Lambda関数 |
| Lambdaプロキシ統合 | オン |
| Lambda関数 | SurveyReceiver:prod |
※ SurveyReceiverのARNを選択して後ろに :prod を書き加えます。
(9) [メソッドを作成]をクリック。
(10) [メソッドリクエスト]タブの右のほうの[テスト]タブにアクセスして、リクエスト本文に次のメッセージを入力して[テスト]ボタンをクリックします。
|
1 2 |
{"comment": "楽しかったです", "rating": 5} |
(11) 結果のステータス 200を確認して、DynamoDBコンソールでSurveyResponsesテーブルの[項目を探索]を開き、データが保存されていることを確認します。テストが成功したらAPIをデプロイします。
(12) [APIをデプロイ]をクリック。
(13) 以下の設定でデプロイします:
| 項目 | 設定値 |
|---|---|
| ステージ | 新しいステージ |
| ステージ名 | prod |
(14) [デプロイ]をクリック。
(15) 表示されるURLの呼び出しをメモします。
例:https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod
(16) CloudShellを開いて、curlコマンドでAPIをテストします:
|
1 2 3 4 5 |
curl -X POST \ https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/survey \ -H "Content-Type: application/json" \ -d '{"comment": "とても良かったです", "rating": 5}' |
URLのxxxxxxxxxx部分は自分のAPIのIDに置き換えてください。
成功すると以下のようなレスポンスが返ります。
|
1 2 |
{"message": "アンケートを受け付けました", "responseId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"} |
(17) DynamoDBコンソールでSurveyResponsesテーブルの[項目を探索]を開き、データが保存されていることを確認します。
(18) 次に、評価1でテストしてメール通知が届くことを確認します:
|
1 2 3 4 5 |
curl -X POST \ https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/survey \ -H "Content-Type: application/json" \ -d '{"comment": "改善してほしい点があります", "rating": 1}' |
評価1の場合のみ、管理者メールアドレスに通知が届くことを確認してください。
評価2〜5ではメールは届きません。
ポイント
- API GatewayのREST APIは、HTTPリクエストを受け付けてバックエンドサービスに転送するインターフェースの役割を果たします。
- Lambdaプロキシ統合を使用すると、リクエスト情報がそのままLambda関数のeventに渡されるので開発が簡単です。
- CORSを有効にすることで、S3の静的サイトなど別ドメインからのリクエストを許可できます。
タスク 8:S3で静的ウェブサイトをホスティングする
目的
アンケートフォームのHTMLをS3バケットに配置して、静的ウェブサイトとして公開しましょう。
手順
(1) マネジメントコンソール上部の検索フィールドにS3と入力し、S3コンソールにアクセスします。
(2) [バケットを作成]をクリック。
(3) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| バケット名 | survey-form-handson-{自分の名前など一意の文字列} |
| リージョン | ap-northeast-1 |
| パブリックアクセスをすべてブロック | チェックを外す |
(4) 「現在の設定により、このバケットとバケット内のオブジェクトがパブリックになる可能性があることを承認します。」にチェックを入れます。
(5) [バケットを作成]をクリック。
(6) 作成したバケットを開き、[プロパティ]タブの[静的ウェブサイトホスティング]セクションで[編集]をクリック。
(7) 以下の設定を行います:
| 項目 | 設定値 |
|---|---|
| 静的ウェブサイトホスティング | 有効にする |
| インデックスドキュメント | index.html |
(8) [変更の保存]をクリック。
(9) [アクセス許可]タブの[バケットポリシー]セクションで[編集]をクリックし、以下のポリシーを貼り付けます(バケット名は自分のものに置き換えてください):
|
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::survey-form-handson-{自分の名前}/*" } ] } |
(10) [変更の保存]をクリック。
(11) CloudShellを開いて、以下のコマンドでHTMLファイルを作成してアップロードします。API_URLの部分は次の手順で置き換えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
cat > /tmp/index.html << 'EOF' <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>アンケートフォーム</title> <style> body { font-family: sans-serif; max-width: 600px; margin: 40px auto; padding: 0 20px; } h1 { color: #333; } label { display: block; margin-top: 16px; font-weight: bold; } textarea { width: 100%; height: 120px; margin-top: 8px; padding: 8px; } .rating { margin-top: 8px; } .rating label { display: inline; font-weight: normal; margin-right: 12px; } button { margin-top: 24px; padding: 12px 32px; background: #0073bb; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; } button:hover { background: #005a9e; } #result { margin-top: 16px; padding: 12px; border-radius: 4px; display: none; } .success { background: #d4edda; color: #155724; } .error { background: #f8d7da; color: #721c24; } </style> </head> <body> <h1>アンケートフォーム</h1> <p>ご利用いただきありがとうございます。以下のアンケートにご協力ください。</p> <form id="surveyForm"> <label for="comment">感想をお聞かせください:</label> <textarea id="comment" name="comment" placeholder="自由にご記入ください"></textarea> <label>友達に勧めたいですか?(5段階評価)</label> <div class="rating"> <input type="radio" name="rating" value="1" id="r1"><label for="r1">1</label> <input type="radio" name="rating" value="2" id="r2"><label for="r2">2</label> <input type="radio" name="rating" value="3" id="r3" checked><label for="r3">3</label> <input type="radio" name="rating" value="4" id="r4"><label for="r4">4</label> <input type="radio" name="rating" value="5" id="r5"><label for="r5">5</label> </div> <button type="submit">送信</button> </form> <div id="result"></div> <script> const API_URL = 'https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/survey'; document.getElementById('surveyForm').addEventListener('submit', async (e) => { e.preventDefault(); const comment = document.getElementById('comment').value; const rating = document.querySelector('input[name="rating"]:checked').value; const resultDiv = document.getElementById('result'); try { const res = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ comment, rating: parseInt(rating) }) }); const data = await res.json(); resultDiv.textContent = data.message; resultDiv.className = 'success'; resultDiv.style.display = 'block'; document.getElementById('surveyForm').reset(); } catch (err) { resultDiv.textContent = '送信に失敗しました。もう一度お試しください。'; resultDiv.className = 'error'; resultDiv.style.display = 'block'; } }); </script> </body> </html> EOF |
HTMLファイル内のAPI_URLを自分のAPIエンドポイントに書き換えてからアップロードします。
|
1 2 |
sed -i "s|https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/survey|https://自分のAPIエンドポイント/prod/survey|" /tmp/index.html |
(12) S3バケットにアップロードします:
|
1 2 3 4 |
aws s3 cp /tmp/index.html s3://survey-form-handson-{自分の名前}/index.html \ --content-type "text/html" \ --region ap-northeast-1 |
(13) S3コンソールでバケットの[プロパティ]タブを開き、[静的ウェブサイトホスティング]セクションに表示されているバケットウェブサイトエンドポイントのURLにブラウザでアクセスします。
例:http://survey-form-handson-{自分の名前}.s3-website-ap-northeast-1.amazonaws.com
(14) アンケートフォームが表示されたら、感想を入力して評価を選択し、[送信]をクリックします。
(15) 「アンケートを受け付けました」と表示されることを確認します。
(16) DynamoDBコンソールでデータが保存されていることを確認します。
(17) 評価1で送信して、メール通知が届くことを確認します。
ポイント
- S3を使うと、Webサーバーを用意せずにHTMLを公開できます。
- バケットポリシーでパブリックアクセスを許可することで、インターネットからアクセス可能になります。
- JavaScriptのfetch APIでAPI Gatewayにリクエストを送信しています。CORSが正しく設定されていないとブラウザからのリクエストがブロックされます。
- 本番環境ではCloudFrontを前段に配置してHTTPS化するのがベストプラクティスです。
タスク 9:GSIを使ってデータを検索する
目的
グローバルセカンダリインデックスを使って、特定の評価段階のアンケート回答を検索してみましょう。
手順
(1) CloudShellで以下のPythonスクリプトを実行して、GSIを使った検索を体験します:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
python3 << 'EOF' import boto3 from boto3.dynamodb.conditions import Key dynamodb = boto3.resource('dynamodb', region_name='ap-northeast-1') table = dynamodb.Table('SurveyResponses') # GSIを使って評価1の回答を検索 response = table.query( IndexName='Rating-ResponseDate-index', KeyConditionExpression=Key('Rating').eq(1) ) print("=== 評価1の回答一覧 ===") for item in response['Items']: print(f" ID: {item['ResponseId']}") print(f" 日時: {item['ResponseDate']}") print(f" 感想: {item['Comment']}") print(f" 評価: {item['Rating']}") print(" ---") print(f"\n合計: {response['Count']}件") EOF |
(2) 評価5の回答も検索してみましょう:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
python3 << 'EOF' import boto3 from boto3.dynamodb.conditions import Key dynamodb = boto3.resource('dynamodb', region_name='ap-northeast-1') table = dynamodb.Table('SurveyResponses') # GSIを使って評価5の回答を検索 response = table.query( IndexName='Rating-ResponseDate-index', KeyConditionExpression=Key('Rating').eq(5) ) print("=== 評価5の回答一覧 ===") for item in response['Items']: print(f" ID: {item['ResponseId']}") print(f" 日時: {item['ResponseDate']}") print(f" 感想: {item['Comment']}") print(f" 評価: {item['Rating']}") print(" ---") print(f"\n合計: {response['Count']}件") EOF |
(3) GSIがない場合との違いを確認するため、テーブル全体をスキャンしてフィルタリングする方法も試してみましょう:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
python3 << 'EOF' import boto3 from boto3.dynamodb.conditions import Attr dynamodb = boto3.resource('dynamodb', region_name='ap-northeast-1') table = dynamodb.Table('SurveyResponses') # Scanでフィルタリング(非効率な方法) response = table.scan( FilterExpression=Attr('Rating').eq(1) ) print("=== Scan + FilterExpression での検索 ===") print(f"合計: {response['Count']}件") print(f"スキャンした項目数: {response['ScannedCount']}件") print("(ScannedCountがCountより大きい場合、不要なデータも読み込んでいます)") EOF |
ポイント
- GSIを使ったQueryは、必要なパーティションだけを読み込むので効率的です。
- Scan + FilterExpressionはテーブル全体を読み込んでからフィルタリングするため、データ量が増えるとコストとレイテンシーが増加して非効率です。
- GSIのパーティションキーにRating(数値)を設定しているので、特定の評価段階での検索が高速に行えます。
- ソートキーにResponseDateを設定しているので、同じ評価内で日時順に並べたり、特定期間内での検索もできます。
クリーンアップ
ハンズオンで作成したリソースをすべて削除します。以下の順番で削除してください。
(1) Lambda関数のトリガーとイベントソースマッピングの削除
Lambdaコンソールで SurveyWriter関数を開き、[設定]タブ-[トリガー]からSQSトリガーを削除します。
(2) Lambda関数の削除
Lambdaコンソールで以下の関数を削除します:
– SurveyReceiver
– SurveyWriter
(3) API Gatewayの削除
API GatewayコンソールでSurveyAPIを削除します。
(4) SNSトピックの削除
SNSコンソールでSurveyNotificationトピックを削除します。サブスクリプションも自動的に削除されます。
(5) SQSキューの削除
SQSコンソールでSurveyQueueとSurveyQueue-DLQを削除します。
(6) DynamoDBテーブルの削除
DynamoDBコンソールでSurveyResponsesテーブルを削除します。
(7) S3バケットの削除
S3コンソールでバケット内のオブジェクトをすべて削除してから、バケットを削除します。
(8) IAMロールの削除
IAMコンソールで以下のロールを削除します(Lambda関数作成時に自動作成されたもの):
– SurveyReceiver-role-xxxxxxxx
– SurveyWriter-role-xxxxxxxx
(9) ハンズオン用ポリシーの削除
ハンズオン用に作成したIAMポリシーがあれば削除します。
まとめ
| タスク | サービス | 役割 |
|---|---|---|
| タスク 1 | DynamoDB | アンケート回答の保存先(GSI付き) |
| タスク 2 | SNS | メッセージのファンアウト(通知の分配) |
| タスク 3 | SQS | 非同期処理のためのメッセージキュー |
| タスク 4 | SNS(フィルター) | 評価1のみ管理者にメール通知 |
| タスク 5 | Lambda(受付) | リクエスト受付とSNSへのパブリッシュ |
| タスク 6 | Lambda(書き込み) | SQSからメッセージを受信してDynamoDBに書き込み |
| タスク 7 | API Gateway | REST APIエンドポイントの提供 |
| タスク 8 | S3 | 静的アンケートフォームのホスティング |
| タスク 9 | DynamoDB(GSI) | 評価段階での効率的なデータ検索 |
このハンズオンで体験したサーバーレスアーキテクチャの特徴をまとめます。
- サーバーの管理が不要で、コードとサービスの設定に集中できます。
- SNSとSQSを組み合わせたファンアウトパターンで、1つのイベントから複数の処理を並列実行できます。
- SQSキューを挟むことで、処理の失敗時にリトライが可能な疎結合設計になっています。
- SNSのサブスクリプションフィルターで、条件に合致するメッセージだけを特定のエンドポイントに配信できます。
- DynamoDBのGSIを使うことで、プライマリキー以外の属性でも効率的な検索が可能です。
最後までお読みいただきましてありがとうございました!
「AWS認定資格試験テキスト&問題集 AWS認定ソリューションアーキテクト - プロフェッショナル 改訂第2版」という本を書きました。
「AWS認定資格試験テキスト AWS認定クラウドプラクティショナー 改訂第3版」という本を書きました。
「AWS認定資格試験テキスト AWS認定AIプラクティショナー」という本を書きました。
「ポケットスタディ AWS認定 デベロッパーアソシエイト [DVA-C02対応] 」という本を書きました。
「要点整理から攻略するAWS認定ソリューションアーキテクト-アソシエイト」という本を書きました。
「AWSではじめるLinux入門ガイド」という本を書きました。
開発ベンダー5年、ユーザ企業システム部門通算9年、ITインストラクター5年目でプロトタイプビルダーもやりだしたSoftware Engineerです。
質問はコメントかSNSなどからお気軽にどうぞ。
出来る限りなるべく答えます。
このブログの内容/発言の一切は個人の見解であり、所属する組織とは関係ありません。
このブログは経験したことなどの共有を目的としており、手順や結果などを保証するものではありません。
ご参考にされる際は、読者様自身のご判断にてご対応をお願いいたします。
また、勉強会やイベントのレポートは自分が気になったことをメモしたり、聞いて思ったことを書いていますので、登壇者の意見や発表内容ではありません。
関連記事
-
-
php-fpm で Out of memoryが発生した際にメール通知する(AWS CloudWatch , Amazon SNS)
AWS CloudWatch LogsエージェントでAmazon EC2上のNg …
-
-
EC2インスタンスを必要最小限のパラメータでCLIとSDKから起動する
EC2インスタンスをCLIとSDKから起動するデモで、パラメータを必要最小限にし …
-
-
EC2 Ubuntu DesktopにRDP
Ubuntu Desktopが必要になりましたので、こちらのAWS EC2でデス …
-
-
AWS Service Catalogポートフォリオを他のアカウントと共有する
AWS Service Catalogチュートリアルで作成したポートフォリオの他 …
-
-
Apple Silicon M1 MacBook ProにAWS CLI v2をインストール
公式手順どおりにインストールしました。 macOS での AWS CLI バージ …
-
-
WordPressのwp-login.php , xmlrpc.phpへのアクセスをAWS WAFで接続元IPアドレスを制限する
AWS CloudWatch LogsエージェントでAmazon EC2上のNg …
-
-
静的と動的って何ですか?と営業さんに聞かれたので端的に説明してみました
AWS認定クラウドプラクティショナーの勉強をしている営業さんに、「S3で静的オブ …
-
-
RDSスナップショットをS3にエクスポートする新機能を試そうかと思った
やったこと RDSスナップショットをS3にエクスポートできる、という新機能が追加 …
-
-
AWS CDKでクロススタックリファレンスをする
CloudFormationで複数のスタックで参照することがあります。 それをC …
-
-
VPN接続先のADで管理されているドメインにEC2 Windowsインスタンスから参加する
オンプレミスに見立てたオハイオリージョンにVyOSインスタンスを起動して東京リー …

