【入門】Amazon SNS入門:高信頼性メッセージング・通知サービスの活用術のヒーロー画像

【入門】Amazon SNS入門:高信頼性メッセージング・通知サービスの活用術


はじめに

Amazon Simple Notification Service(SNS)は、AWSが提供するフルマネージドなメッセージング・通知サービスです。アプリケーション間の連携やユーザーへの通知配信において、高い信頼性とスケーラビリティを実現します。

この記事では、SNSの基本概念から実用的な活用方法まで、実際の設定例と共に詳しく解説します。

Amazon SNSとは

Amazon SNSは、**Pub/Sub(パブリッシュ/サブスクライブ)**モデルを基盤としたメッセージングサービスです。メッセージの送信者(パブリッシャー)が特定の受信者を意識することなく、複数の受信者(サブスクライバー)に同時にメッセージを配信できます。

主な特徴

  1. フルマネージド: インフラストラクチャの管理が不要
  2. 高可用性: マルチAZ構成による自動冗長化
  3. スケーラビリティ: 秒間数百万メッセージの処理が可能
  4. 多様な配信先: SMS、Email、HTTP/HTTPS、AWS Lambda、SQSなど
  5. メッセージフィルタリング: 条件に基づく配信制御
  6. 暗号化対応: データの保護とセキュリティ確保

SNSの基本構成要素

トピック(Topic)

トピックは、メッセージを受け取る論理的なエンドポイントです。メッセージの集約点として機能し、複数のサブスクライバーに同じメッセージを配信できます。

  • Standard Topic: 一般的な用途、高いスループット
  • FIFO Topic: 順序保証とメッセージ重複排除

サブスクリプション(Subscription)

サブスクリプションは、トピックからメッセージを受信するための設定です。各サブスクリプションは特定のプロトコルとエンドポイントを持ちます。

サポートされるプロトコル

  • HTTP/HTTPS: WebAPIへの配信
  • Email/Email-JSON: メール通知
  • SMS: SMS配信
  • SQS: Amazon SQSキューへの配信
  • Lambda: AWS Lambda関数の実行
  • Application: モバイルアプリへのプッシュ通知
  • Firehose: Amazon Data Firehoseへのストリーム配信

SNSの実装例

1. 基本的なトピック作成とサブスクリプション

まず、AWS CLIを使ってトピックを作成し、サブスクリプションを設定してみましょう。

# トピックの作成
aws sns create-topic --name user-notifications

# サブスクリプションの作成(Email)
aws sns subscribe \
    --topic-arn arn:aws:sns:ap-northeast-1:123456789012:user-notifications \
    --protocol email \
    --notification-endpoint user@example.com

# サブスクリプションの作成(SQS)
aws sns subscribe \
    --topic-arn arn:aws:sns:ap-northeast-1:123456789012:user-notifications \
    --protocol sqs \
    --notification-endpoint arn:aws:sqs:ap-northeast-1:123456789012:notification-queue

2. CloudFormationテンプレート例

Infrastructure as Codeでの実装例:

AWSTemplateFormatVersion: '2010-09-09'
Description: 'SNS Topic with multiple subscriptions'

Resources:
  UserNotificationTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: user-notifications
      DisplayName: ユーザー通知
      KmsMasterKeyId: !Ref SNSEncryptionKey

  # Email サブスクリプション
  EmailSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref UserNotificationTopic
      Protocol: email
      Endpoint: admin@example.com

  # SQS サブスクリプション
  SQSSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref UserNotificationTopic
      Protocol: sqs
      Endpoint: !GetAtt NotificationQueue.Arn
      FilterPolicy:
        event_type: ["order", "payment"]

  # Lambda サブスクリプション
  LambdaSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref UserNotificationTopic
      Protocol: lambda
      Endpoint: !GetAtt ProcessingFunction.Arn

  # 通知処理用キュー
  NotificationQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: notification-queue
      VisibilityTimeoutSeconds: 300

  # 暗号化キー
  SNSEncryptionKey:
    Type: AWS::KMS::Key
    Properties:
      Description: SNS Topic encryption key
      KeyPolicy:
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'

Outputs:
  TopicArn:
    Description: SNS Topic ARN
    Value: !Ref UserNotificationTopic
    Export:
      Name: !Sub '${AWS::StackName}-TopicArn'

3. メッセージフィルタリングの活用

特定の条件でメッセージを配信したい場合、フィルターポリシーを設定できます:

{
  "event_type": ["order_completed", "payment_failed"],
  "priority": ["high", "critical"],
  "region": ["ap-northeast-1"],
  "price": [{"numeric": [">", 10000]}]
}

高度な活用例

1. マイクロサービス間の非同期通信

import boto3
import json

def publish_order_event(order_data):
    sns = boto3.client('sns')

    message = {
        "event_type": "order_completed",
        "order_id": order_data["id"],
        "customer_id": order_data["customer_id"],
        "amount": order_data["total_amount"],
        "timestamp": order_data["created_at"]
    }

    # メッセージ属性を設定
    message_attributes = {
        'event_type': {
            'DataType': 'String',
            'StringValue': 'order_completed'
        },
        'priority': {
            'DataType': 'String',
            'StringValue': 'high' if order_data["total_amount"] > 10000 else 'normal'
        }
    }

    response = sns.publish(
        TopicArn='arn:aws:sns:ap-northeast-1:123456789012:order-events',
        Message=json.dumps(message),
        MessageAttributes=message_attributes,
        Subject='新規注文完了'
    )

    return response['MessageId']

2. ファンアウトパターンの実装

一つのメッセージを複数のサービスで異なる処理を行う場合:

# CloudFormation例
OrderProcessingTopic:
  Type: AWS::SNS::Topic
  Properties:
    TopicName: order-processing

# 在庫管理システム
InventorySubscription:
  Type: AWS::SNS::Subscription
  Properties:
    TopicArn: !Ref OrderProcessingTopic
    Protocol: sqs
    Endpoint: !GetAtt InventoryQueue.Arn
    FilterPolicy:
      event_type: ["order_created", "order_cancelled"]

# 配送システム
ShippingSubscription:
  Type: AWS::SNS::Subscription
  Properties:
    TopicArn: !Ref OrderProcessingTopic
    Protocol: lambda
    Endpoint: !GetAtt ShippingFunction.Arn
    FilterPolicy:
      event_type: ["order_confirmed"]

# 請求システム
BillingSubscription:
  Type: AWS::SNS::Subscription
  Properties:
    TopicArn: !Ref OrderProcessingTopic
    Protocol: sqs
    Endpoint: !GetAtt BillingQueue.Arn
    FilterPolicy:
      event_type: ["order_completed", "order_cancelled"]

3. モバイルプッシュ通知

import boto3

def setup_mobile_push():
    sns = boto3.client('sns')

    # プラットフォームアプリケーションの作成
    platform_app = sns.create_platform_application(
        Name='MyMobileApp',
        Platform='GCM',  # Android用、iOS用の場合は'APNS'
        Attributes={
            'PlatformCredential': 'YOUR_FIREBASE_SERVER_KEY'
        }
    )

    # エンドポイントの作成(ユーザーのデバイストークン)
    endpoint = sns.create_platform_endpoint(
        PlatformApplicationArn=platform_app['PlatformApplicationArn'],
        Token='DEVICE_TOKEN'
    )

    # プッシュ通知の送信
    message = {
        "GCM": json.dumps({
            "data": {
                "title": "新しいメッセージ",
                "body": "お客様宛に新しいメッセージが届きました",
                "click_action": "FLUTTER_NOTIFICATION_CLICK"
            }
        })
    }

    sns.publish(
        TargetArn=endpoint['EndpointArn'],
        Message=json.dumps(message),
        MessageStructure='json'
    )

Amazon SNS vs Amazon SQS

SNSとSQSの使い分けを理解することは、適切なアーキテクチャ設計に重要です。

配信モデルの違い

特徴Amazon SNSAmazon SQS
配信モデルPush(プッシュ)Pull(プル)
配信先数1対多(ファンアウト)1対1
メッセージ保持配信時のみ最大14日間
順序保証FIFO Topicで対応FIFO Queueで対応
用途即座の通知・イベント配信非同期処理・ワークキュー

組み合わせパターン

SNSとSQSを組み合わせることで、より柔軟なアーキテクチャを構築できます:

# SNS + SQS の組み合わせ例
EventTopic:
  Type: AWS::SNS::Topic
  Properties:
    TopicName: user-events

# 即座に処理が必要なタスク用キュー
HighPriorityQueue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: high-priority-tasks
    VisibilityTimeoutSeconds: 30

HighPrioritySubscription:
  Type: AWS::SNS::Subscription
  Properties:
    TopicArn: !Ref EventTopic
    Protocol: sqs
    Endpoint: !GetAtt HighPriorityQueue.Arn
    FilterPolicy:
      priority: ["high", "critical"]

# バッチ処理用キュー
BatchProcessingQueue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: batch-processing
    VisibilityTimeoutSeconds: 900
    MessageRetentionPeriod: 1209600  # 14日

BatchSubscription:
  Type: AWS::SNS::Subscription
  Properties:
    TopicArn: !Ref EventTopic
    Protocol: sqs
    Endpoint: !GetAtt BatchProcessingQueue.Arn
    FilterPolicy:
      processing_type: ["batch"]

セキュリティベストプラクティス

1. アクセス制御

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPublishFromSpecificRoles",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::123456789012:role/OrderProcessingRole",
          "arn:aws:iam::123456789012:role/NotificationRole"
        ]
      },
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:ap-northeast-1:123456789012:user-notifications"
    },
    {
      "Sid": "DenyUnencryptedPublish",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "sns:Publish",
      "Resource": "*",
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}

2. 暗号化設定

EncryptedTopic:
  Type: AWS::SNS::Topic
  Properties:
    TopicName: encrypted-notifications
    KmsMasterKeyId: !Ref SNSKMSKey

SNSKMSKey:
  Type: AWS::KMS::Key
  Properties:
    Description: SNS encryption key
    KeyPolicy:
      Statement:
        - Effect: Allow
          Principal:
            AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
          Action: 'kms:*'
          Resource: '*'
        - Effect: Allow
          Principal:
            Service: sns.amazonaws.com
          Action:
            - kms:Decrypt
            - kms:GenerateDataKey
          Resource: '*'

3. メッセージの署名検証

HTTP/HTTPSエンドポイントでメッセージを受信する際の検証例:

import json
import base64
import urllib.request
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_public_key

def verify_sns_message(message_data):
    # 署名URLから証明書を取得
    cert_response = urllib.request.urlopen(message_data['SigningCertURL'])
    cert = cert_response.read().decode('utf-8')

    # 公開鍵を抽出
    public_key = load_pem_public_key(cert.encode('utf-8'))

    # 署名文字列を構築
    string_to_sign = construct_string_to_sign(message_data)

    # 署名を検証
    try:
        public_key.verify(
            base64.b64decode(message_data['Signature']),
            string_to_sign.encode('utf-8'),
            padding.PKCS1v15(),
            hashes.SHA1()
        )
        return True
    except Exception:
        return False

監視とログ記録

1. CloudWatchメトリクス

重要な監視メトリクス:

  • NumberOfMessagesPublished: 発行されたメッセージ数
  • NumberOfNotificationsDelivered: 配信されたメッセージ数
  • NumberOfNotificationsFailed: 配信に失敗したメッセージ数
  • PublishSize: メッセージサイズ

2. アラーム設定例

FailedDeliveryAlarm:
  Type: AWS::CloudWatch::Alarm
  Properties:
    AlarmName: SNS-Failed-Deliveries
    AlarmDescription: SNS配信失敗の監視
    MetricName: NumberOfNotificationsFailed
    Namespace: AWS/SNS
    Statistic: Sum
    Period: 300
    EvaluationPeriods: 2
    Threshold: 5
    ComparisonOperator: GreaterThanThreshold
    Dimensions:
      - Name: TopicName
        Value: !GetAtt UserNotificationTopic.TopicName
    AlarmActions:
      - !Ref AlertTopic

3. Dead Letter Queue(DLQ)の設定

NotificationQueue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: notification-queue
    RedrivePolicy:
      deadLetterTargetArn: !GetAtt NotificationDLQ.Arn
      maxReceiveCount: 3

NotificationDLQ:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: notification-dlq
    MessageRetentionPeriod: 1209600  # 14日間

料金最適化

東京リージョン(ap-northeast-1)の料金

  • リクエスト料金:

    • 最初の100万リクエスト: $0.50/100万リクエスト
    • 100万~10億リクエスト: $0.50/100万リクエスト
    • 10億リクエスト以降: $0.30/100万リクエスト
  • 配信料金:

    • HTTP/HTTPS: $0.60/100万配信
    • Email/Email-JSON: $2.00/100万配信
    • SMS: 国内$0.0945/メッセージ(キャリア料金別途)
    • モバイルプッシュ通知: $0.50/100万配信

コスト最適化のコツ

  1. メッセージフィルタリングの活用

    # 必要なサブスクライバーのみに配信
    sns.publish(
        TopicArn=topic_arn,
        Message=message,
        MessageAttributes={
            'customer_tier': {
                'DataType': 'String',
                'StringValue': 'premium'
            }
        }
    )
  2. バッチ処理の検討

    # 複数のメッセージをまとめて送信
    def batch_notifications(notifications):
        sns = boto3.client('sns')
        batch_messages = []
    
        for notification in notifications:
            batch_messages.append({
                'Id': notification['id'],
                'Message': json.dumps(notification['data'])
            })
    
            # 10件ごとにバッチ送信
            if len(batch_messages) == 10:
                sns.publish_batch(
                    TopicArn=topic_arn,
                    PublishRequestEntries=batch_messages
                )
                batch_messages = []
  3. 不要なサブスクリプションの削除

    # 使用されていないサブスクリプションを特定
    aws sns list-subscriptions-by-topic \
        --topic-arn arn:aws:sns:ap-northeast-1:123456789012:topic-name \
        --query 'Subscriptions[?SubscriptionArn==`PendingConfirmation`]'

トラブルシューティング

よくある問題と解決方法

  1. メッセージが配信されない

    • サブスクリプションの確認状態をチェック
    • フィルターポリシーの設定を確認
    • IAMロールの権限を確認
  2. 配信の遅延

    • エンドポイントの応答時間を確認
    • リトライポリシーの設定を調整
    • DLQの設定を確認
  3. 高い料金

    • 不要なサブスクリプションを削除
    • メッセージサイズを最適化
    • フィルタリング条件を見直し

まとめ

Amazon SNSは、現代のクラウドアプリケーションにおいて欠かせないメッセージングサービスです。Pub/Subモデルによる柔軟な配信、豊富な配信先プロトコル、強力なフィルタリング機能により、複雑な通知要件にも対応できます。

SQSとの適切な使い分けや組み合わせにより、信頼性の高いイベント駆動アーキテクチャを構築できます。セキュリティ設定や監視体制を整えて、スケーラブルで効率的なシステムを実現しましょう。

次回はAmazon SQSについて詳しく解説予定です。SNSとSQSを組み合わせたより高度なメッセージングパターンもご紹介しますので、ぜひご期待ください。