はじめに
「1回メッセージを送るだけで、複数のシステムに同時に通知したい」——そんな要件を実現する設計パターンが Pub/Sub(パブリッシュ・サブスクライブ) と ファンアウト です。
この記事では、AWSコンソールのみを使って、SNS + SQS + Lambda による通知システムをゼロから構築するハンズオンを紹介します。
Publisher(aws sns publish)
↓ メッセージを1回発行するだけ
SNS Topic(NotificationTopic)
│
├─ サブスクリプション(フィルターなし)
│ → LogQueue → LogFunction → CloudWatch Logs(全メッセージを記録)
│
└─ サブスクリプション(level=warning/critical のみ)
→ AlertQueue → AlertFunction → CloudWatch Logs(アラート処理)
↓ 処理失敗(3回)
AlertDLQこのハンズオンで体験できること:
- Pub/Sub パターン — Publisher と Subscriber が疎結合で通信する仕組み
- ファンアウト — 1回の publish で複数の Lambda が並行して動作する体験
- SNS フィルターポリシー — メッセージ属性で配信先を絞り込む設定
- DLQ(デッドレターキュー) — 処理失敗メッセージの退避先を実際に確認
この記事は SAM版ハンズオン の比較記事です。
コンソール操作で SNS・SQS・Lambda の連携を視覚的に学び、SAM と何が違うのかを比較したい方向けです。
キーワード解説
| 用語 | 意味 |
|---|---|
| SNS Topic | メッセージの送信先。複数のサブスクライバーに同時配信できる |
| サブスクリプション | Topic からメッセージを受け取るエンドポイントの設定 |
| Pub/Sub | Publisher(発行者)と Subscriber(購読者)が疎結合で通信するパターン |
| ファンアウト | 1つのメッセージを複数の宛先に同時配信するパターン |
| フィルターポリシー | メッセージ属性に基づき、配信するかどうかを制御するルール |
| DLQ(デッドレターキュー) | 処理に失敗したメッセージを退避させるキュー |
| SNS エンベロープ | SNS が SQS に配信する際にメッセージを包む JSON 構造 |
| 可視性タイムアウト | SQS がメッセージを処理中に他のコンシューマーから見えなくする時間 |
使用するAWSサービス
| サービス | 役割 | 料金 |
|---|---|---|
| SNS | メッセージの発行・ファンアウト配信 | 月100万リクエストまで無料 |
| SQS | メッセージキューイング(LogQueue / AlertQueue / AlertDLQ) | 月100万リクエストまで無料 |
| Lambda | キューからメッセージを受信して処理(LogFunction / AlertFunction) | 月100万リクエスト・400,000 GB-秒まで無料 |
| IAM | Lambda の実行権限管理 | 無料 |
| CloudWatch Logs | Lambda の実行ログ | 月5GBまで無料 |
全体の作業順序
① SNS Topic を作成する
↓
② SQS キューを3つ作成する(AlertDLQ / LogQueue / AlertQueue)
↓
③ SNS → SQS サブスクリプションを2つ作成する
(LogQueue: フィルターなし / AlertQueue: フィルターあり)
↓
④ Lambda 関数を2つ作成する
↓
⑤ SQS トリガーを各 Lambda に追加する
↓
⑥ 動作テスト(3パターン)
↓
⑦ リソースの削除AlertDLQ を先に作成する理由:
AlertQueue の作成時に DLQ として AlertDLQ の ARN を指定するため、AlertDLQ を先に作る必要があります。
① SNS Topic を作成する
AWSコンソール → SNS → 「トピック」→「トピックを作成」
| 設定項目 | 値 |
|---|---|
| タイプ | スタンダード |
| 名前 | NotificationTopic |
その他の設定はデフォルトのまま。「トピックを作成」をクリック。
控えておく情報:
- トピック ARN(例:
arn:aws:sns:ap-northeast-1:123456789012:NotificationTopic)
スタンダードとFIFOの違い:
スタンダードは順序保証なし・高スループット。FIFO は順序保証あり・1回限りの配信が必要な場合に使用します。今回のログ/アラート通知にはスタンダードが適しています。
② SQS キューを3つ作成する
AWSコンソール → SQS → 「キューを作成」
2-1. AlertDLQ(デッドレターキュー)を先に作成する
| 設定項目 | 値 |
|---|---|
| タイプ | スタンダード |
| 名前 | AlertDLQ |
| メッセージ保持期間 | 1 日(学習用なので短く設定) |
「キューを作成」をクリック。
控えておく情報:
- AlertDLQ の ARN(例:
arn:aws:sqs:ap-northeast-1:123456789012:AlertDLQ)
2-2. LogQueue を作成する(フィルターなし・全メッセージ用)
| 設定項目 | 値 |
|---|---|
| タイプ | スタンダード |
| 名前 | LogQueue |
| 可視性タイムアウト | 180 秒 |
「キューを作成」をクリック。
可視性タイムアウトを 180 秒にする理由:
Lambda のデフォルトタイムアウト(3秒)より長く設定することで、処理中に他のコンシューマーがメッセージを二重取得するのを防ぎます。DLQ テストでは 180秒 × 3回 ≒ 9分待つことになります。
2-3. AlertQueue を作成する(フィルターあり・アラート用)
| 設定項目 | 値 |
|---|---|
| タイプ | スタンダード |
| 名前 | AlertQueue |
| 可視性タイムアウト | 180 秒 |
「デッドレターキュー」セクション:
| 設定項目 | 値 |
|---|---|
| デッドレターキューを有効にする | オン |
| デッドレターキューの ARN | AlertDLQ の ARN を貼り付ける |
| 最大受信数 | 3 |
「キューを作成」をクリック。
控えておく情報:
- LogQueue の ARN
- AlertQueue の ARN
③ SNS → SQS サブスクリプションを2つ作成する
SQS キューポリシーの自動追加について:
コンソールで SNS → SQS サブスクリプションを作成すると、
SNS が SQS キューのアクセスポリシーにsns.amazonaws.comからのsqs:SendMessage権限を自動追加します。
手動でのポリシー設定は不要です(SAM版ではAWS::SQS::QueuePolicyを明示的に定義する必要があります)。
3-1. LogQueue サブスクリプション(フィルターなし)
SNS → NotificationTopic → 「サブスクリプション」タブ → 「サブスクリプションを作成」
| 設定項目 | 値 |
|---|---|
| プロトコル | Amazon SQS |
| エンドポイント | LogQueue の ARN |
| サブスクリプションフィルターポリシー | 設定しない(空欄のまま) |
「サブスクリプションを作成」をクリック。
3-2. AlertQueue サブスクリプション(フィルターあり)
SNS → NotificationTopic → 「サブスクリプション」タブ → 「サブスクリプションを作成」
| 設定項目 | 値 |
|---|---|
| プロトコル | Amazon SQS |
| エンドポイント | AlertQueue の ARN |
「サブスクリプションフィルターポリシー」セクション:
| 設定項目 | 値 |
|---|---|
| サブスクリプションフィルターポリシー | 有効にする |
| フィルターポリシーのスコープ | メッセージ属性(デフォルト) |
| JSON エディタ | 以下を入力 |
{
"level": ["warning", "critical"]
}フィルターポリシーの意味:
levelメッセージ属性の値が"warning"または"critical"のメッセージのみ AlertQueue に配信します。level=infoのメッセージは AlertQueue に届かない(LogQueue には届く)。
よくある間違い(フィルターが動作しない場合):
- スコープが「メッセージ本文」になっている → **「メッセージ属性」**に変更する
- JSON のスペルミス(
"warning"のクォートが抜けている等)
「サブスクリプションを作成」をクリック。
④ Lambda 関数を2つ作成する
AWSコンソール → Lambda → 「関数の作成」(共通設定)
| 設定項目 | 値 |
|---|---|
| 作成方法 | 一から作成 |
| ランタイム | Python 3.12 |
| アーキテクチャ | x86_64 |
| 実行ロール | 「基本的な Lambda アクセス権限で新しいロールを作成」(デフォルトのまま) |
コードを貼り付けたあと、必ず 「Deploy」 ボタンを押してコードを保存します。
4-1. LogFunction(全ログ処理)
関数名: LogFunction
コードエディタで lambda_function.py を開き、以下を貼り付けて「Deploy」をクリック。
import json
def lambda_handler(event, context):
results = []
for record in event["Records"]:
sns_envelope = json.loads(record["body"])
message = sns_envelope["Message"] # 実際のメッセージ本文
attributes = sns_envelope.get("MessageAttributes", {})
level = attributes.get("level", {}).get("Value", "info")
result = {
"queue": "log",
"level": level,
"message": message,
}
print(json.dumps(result, ensure_ascii=False))
results.append(result)
print(json.dumps({"processedCount": len(results)}, ensure_ascii=False))
return {"processedCount": len(results)}
json.loads(record["body"])が必要な理由:
SNS → SQS 配信では、SQS のメッセージボディに SNS エンベロープ(JSON文字列)が格納されます。
そのためrecord["body"]はそのまま使えず、json.loadsでパースする必要があります。{ "Type": "Notification", "TopicArn": "arn:aws:sns:...:NotificationTopic", "Message": "警告メッセージ", "MessageAttributes": { "level": {"Type": "String", "Value": "warning"} } }
4-2. AlertFunction(アラート処理・DLQ テスト機能付き)
関数名: AlertFunction
import json
def lambda_handler(event, context):
results = []
for record in event["Records"]:
sns_envelope = json.loads(record["body"])
message = sns_envelope["Message"]
attributes = sns_envelope.get("MessageAttributes", {})
level = attributes.get("level", {}).get("Value", "unknown")
# DLQテスト用: "error" というメッセージを受信したら意図的に例外発生
if message.strip().lower() == "error":
raise ValueError(
f"アラート処理に失敗: level={level}, message={message}"
)
result = {
"queue": "alert",
"level": level,
"message": message,
"alert_triggered": True,
}
print(json.dumps(result, ensure_ascii=False))
results.append(result)
print(json.dumps({"processedCount": len(results)}, ensure_ascii=False))
return {"processedCount": len(results)}DLQ テストのしくみ:
message == "error"のとき意図的にValueErrorを発生させています。
SQS は処理失敗を検知してメッセージをキューに戻し、可視性タイムアウト(180秒)後に再試行します。
これを3回繰り返すとAlertDLQに移動します。
⑤ SQS トリガーを各 Lambda に追加する
5-1. LogFunction に LogQueue トリガーを追加する
Lambda → LogFunction → 「設定」タブ → 「トリガー」→「トリガーを追加」
| 設定項目 | 値 |
|---|---|
| ソース | SQS |
| SQS キュー | LogQueue |
| バッチサイズ | 5 |
| トリガーの有効化 | オン |
「追加」をクリック。
権限の追加:
Lambda → LogFunction → 「設定」タブ → 「アクセス権限」→ 実行ロール名をクリック(IAM コンソールへ)
→「許可を追加」→「ポリシーをアタッチ」→ AWSLambdaSQSQueueExecutionRole をアタッチ。
AWSLambdaSQSQueueExecutionRoleが必要な理由:
Lambda が SQS からメッセージを受信(sqs:ReceiveMessage)・削除(sqs:DeleteMessage)するために必要な権限がまとまったポリシーです。これがないと Lambda がメッセージを取得できません。
5-2. AlertFunction に AlertQueue トリガーを追加する
Lambda → AlertFunction → 「設定」タブ → 「トリガー」→「トリガーを追加」
| 設定項目 | 値 |
|---|---|
| ソース | SQS |
| SQS キュー | AlertQueue |
| バッチサイズ | 1 |
| トリガーの有効化 | オン |
「追加」をクリック。権限の追加(AWSLambdaSQSQueueExecutionRole)も同様に実施する。
AlertFunction のバッチサイズを 1 にする理由:
1件ずつ処理することで、1件が失敗しても他のメッセージに影響が出ません。
DLQ へ移動するメッセージを1件に限定できるため、テストが分かりやすくなります。
⑥ 動作テスト
3つのシナリオで SNS フィルター・ファンアウト・DLQ の動作を確認します。
| テスト | メッセージ属性 | LogFunction | AlertFunction | 確認ポイント |
|---|---|---|---|---|
| テスト1 | level=info | 処理 ✓ | 処理しない | フィルター動作 |
| テスト2 | level=warning | 処理 ✓ | 処理 ✓ | ファンアウト(1発行→2Lambda) |
| テスト3 | level=warning + body=error | 処理 ✓ | 失敗→DLQ | DLQ 動作 |
テスト1: level=info(LogFunction のみ受信)
aws sns publish ^
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:NotificationTopic ^
--message "情報メッセージ" ^
--message-attributes "{\"level\":{\"DataType\":\"String\",\"StringValue\":\"info\"}}" ^
--region ap-northeast-1
123456789012を自分のアカウントIDに置き換えてください。
期待する動作:
LogFunctionが処理 → CloudWatch Logs にログが記録されるAlertFunctionは動作しない(フィルターで除外)
CloudWatch Logs で確認:
Lambda → LogFunction → 「モニタリング」タブ → 「CloudWatch Logs を表示」
{"queue": "log", "level": "info", "message": "情報メッセージ"}
{"processedCount": 1}テスト2: level=warning(両 Lambda が受信 = ファンアウト)
aws sns publish ^
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:NotificationTopic ^
--message "警告メッセージ" ^
--message-attributes "{\"level\":{\"DataType\":\"String\",\"StringValue\":\"warning\"}}" ^
--region ap-northeast-1期待する動作:
LogFunctionが処理(level=warningを受信)AlertFunctionも処理(フィルターを通過)- 1回の publish で 2つの Lambda が並行して動作する ← ファンアウトのポイント
LogFunction のログ:
{"queue": "log", "level": "warning", "message": "警告メッセージ"}AlertFunction のログ:
{"queue": "alert", "level": "warning", "message": "警告メッセージ", "alert_triggered": true}テスト3: level=warning + body=error(AlertFunction 失敗 → DLQ)
aws sns publish ^
--topic-arn arn:aws:sns:ap-northeast-1:123456789012:NotificationTopic ^
--message "error" ^
--message-attributes "{\"level\":{\"DataType\":\"String\",\"StringValue\":\"warning\"}}" ^
--region ap-northeast-1期待する動作:
LogFunction→ 正常処理(message=errorでも例外なし)AlertFunction→ValueError発生- AlertQueue の可視性タイムアウト(180秒)後に再試行 × 3回 →
AlertDLQに移動
DLQ に移動するまでの待機時間: 最大 180秒 × 3回 ≒ 9分かかります。
CloudWatch Logs でAlertFunctionに[ERROR] ValueErrorが 3回記録されたことを確認してから DLQ をポーリングするとよいです。
DLQ にメッセージが届いたか確認:
SQS → AlertDLQ → 「メッセージを送受信」→「メッセージをポーリング」
"error" のメッセージ(SNS エンベロープ形式)が表示されれば DLQ 動作確認完了。
⑦ リソースの削除
課金を止めるために、ハンズオン完了後は必ず削除してください。
1. Lambda のトリガーを削除する
- Lambda →
LogFunction→ 「設定」→「トリガー」→ SQS トリガーを削除 - Lambda →
AlertFunction→ 同様に削除
2. Lambda 関数を削除する
LogFunction/AlertFunctionを削除
3. SNS サブスクリプションを削除する
SNS → NotificationTopic → 「サブスクリプション」タブ → 2つのサブスクリプションを削除
4. SNS Topic を削除する
SNS → トピック → NotificationTopic → 「削除」
5. SQS キューを3つ削除する
SQS → LogQueue / AlertQueue / AlertDLQ を各々削除
6. IAM ロールを削除する(任意)
IAM → ロール → LogFunction-role-XXXX / AlertFunction-role-XXXX を削除
7. CloudWatch Logs のロググループを削除する(任意)
CloudWatch → ロググループ → /aws/lambda/LogFunction / /aws/lambda/AlertFunction を削除
SAM版との大きな違い(SAMのメリット):
SAM版ではsam delete1コマンドで上記すべてのリソースを一括削除できます。
SAMとの対比
| SAMの記述 | コンソールでやること |
|---|---|
AWS::SNS::Topic | SNS → トピックを作成 |
AWS::SQS::QueuePolicy | SNS サブスクリプション作成時に自動追加される |
AWS::SNS::Subscription | SNS → サブスクリプションを作成(フィルターポリシー含む) |
FilterPolicy: '{"level": ...}' | サブスクリプション作成時の「フィルターポリシー」に JSON を入力 |
SQSPollerPolicy × 2 | Lambda の IAM ロールに AWSLambdaSQSQueueExecutionRole をアタッチ |
sam delete | SNS / SQS × 3 / Lambda × 2 / IAM / ロググループを個別に削除 |
コンソール版の最大のつまずきポイント:
SNS の「サブスクリプション」を作り忘れると、SNS publish しても SQS キューにメッセージが届きません。
「キューにメッセージが来ない」と感じたら、まずサブスクリプションが正しく作成されているか確認しましょう。
トラブルシューティング
| 症状 | 原因 | 対処 |
|---|---|---|
| SNS publish してもキューにメッセージが来ない | SNS サブスクリプションが未作成 | SNS → NotificationTopic → 「サブスクリプション」タブで2つ作成されているか確認 |
| SNS publish してもキューにメッセージが来ない(その2) | SQS キューポリシーが設定されていない | SQS → キュー → 「アクセスポリシー」タブで sns.amazonaws.com からの sqs:SendMessage があるか確認 |
level=warning でも AlertQueue に届かない | フィルターポリシーのスコープが「メッセージ本文」になっている | SNS → サブスクリプション → フィルターポリシーのスコープを**「メッセージ属性」**に変更 |
| Lambda が実行されない | SQS トリガーが設定されていない | ⑤ の手順でトリガーを追加する |
| Lambda が実行されない(SQS権限エラー) | AWSLambdaSQSQueueExecutionRole がアタッチされていない | ⑤ の権限追加手順を実施する |
| DLQ にメッセージが届かない | 可視性タイムアウト(180秒)× 3回 の待機中 | 最大 9分待つ。CloudWatch Logs で 3回の ERROR ログを確認してからポーリング |
| SNS エンベロープの構造が分からない | Lambda のコードで print(record["body"]) して CloudWatch Logs で確認する |
まとめ
今回のハンズオンで体験できたこと:
| 確認項目 | 内容 |
|---|---|
| Pub/Sub パターン | Publisher は誰が受け取るか知らずに SNS Topic に発行するだけ。Subscriber(SQS)が購読して処理 |
| ファンアウト | level=warning の1回の publish で LogFunction と AlertFunction の両方が並行して動作 |
| SNS フィルターポリシー | level=info は LogQueue のみ配信、level=warning/critical は両キューに配信 |
| DLQ | AlertFunction が 3回失敗したメッセージが AlertDLQ に退避 |
コンソール版で実感できたポイント
- SNS Topic / SQS キュー / Lambda という3つのリソースの組み合わせ方が視覚的に理解できる
- SNS サブスクリプションのフィルターポリシーJSON を直接入力することで、ルールの書き方が身につく
- リソースを1つずつ作成することで、各サービスの役割と依存関係が明確になる
コンソール版と SAM 版を比較してみる
コンソールで SNS + SQS + Lambda の連携を理解したら、SAM で同じ構成をコードで定義することで「SAM が何を自動化しているか」が明確になります。AWS::SQS::QueuePolicy の明示的な記述や SQSPollerPolicy によるワンライン権限付与など、コンソールでの手動操作がコードに対応しています。
コメント