目的
SNSとLambdaを用いたマイクロサービスの環境を構築したときのメモです。
今回のアーキテクチャがハマるケースは少ないかもしれませんが、もし誰かの役に立てばと思い残します。
経緯
- デバイス(IoT)から呼び出されるクラウドサービスを構築した
- デバイスのバージョンごとに機能の互換性を担保する必要がある
- 互換性のロジックをLambda関数のコードに全部入れるとメンテナンスが大変
- MQTTトピックは既に決定している
- IoTルールは気軽に修正できない
構成
- IoT Core
- SNS
- Lambda
構成図
特徴
簡単に表現すると Fan-out + Message Broker です。
- 1つのSNSトピックに対して複数のLambda関数がサブスクライブ
- サブスクリプションフィルターでLambda関数が起動する条件を定義
- SNSトピックにメッセージを発行する際にメッセージ属性を設定する
ポイントは バージョン を数値として設定することで、特定のバージョン以上/以下といったサブスクリプションフィルターを設定することが出来ます。
メリット
- 特定の機能(Lamnbda関数)を新旧バージョンがそれぞれ独立して状態で混在させることができる
- SNSの配信失敗やLambda関数の実行失敗の際にはDLQ経由で簡単に再実行をすることができる
- 標準的な機能のため今回の記事の範囲には含めていません。
- あるリクエストに対してサブスクリプションフィルターを設定するだけで機能を追加/拡張できる
- 既存機能に一切の影響を与えずにお試し機能を実装して試してみる
- ある特定機能が呼び出された時だけSlackに通知する
デメリット
- サブスクリプションフィルターの設定を誤ると新旧の機能が両方実行されてしまう
- メッセージ属性の設計をしないと複雑な定義に成長してしまう
サンプルコード
環境
sample.cf.yaml
Parameters:
BrokerTopic:
Description: Amazon SNS Topic
Type: String
SourceCodeBucket:
Type: String
SourceCodeObject:
Type: String
Resources:
Function:
Properties:
Code:
S3Bucket:
Ref: SourceCodeBucket
S3Key:
Ref: SourceCodeObject
Handler: main
Role:
Fn::GetAtt:
- Role
- Arn
Runtime: go1.x
Type: AWS::Lambda::Function
Permission: # サブスクリプションフィルターでLambda関数を起動する場合に必要
Properties:
Action: lambda:InvokeFunction
FunctionName:
Ref: Function
Principal: sns.amazonaws.com
SourceAccount:
Ref: AWS::AccountId
SourceArn:
Ref: BrokerTopic
Type: AWS::Lambda::Permission
Trigger: # サブスクリプションフィルターでLambda関数を起動する場合に必要
Properties:
Endpoint:
Ref: Function
FilterPolicy:
application:
- bizlogic
version:
- numeric:
- '>'
- 0
Protocol: lambda
TopicArn:
Ref: BrokerTopic
Type: AWS::SNS::Subscription
Role:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Version: 2012-10-17
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Path: /
Policies:
- PolicyDocument: # SNSトピック経由で次のLambda関数を呼ぶ場合に必要な権限
Statement:
- Action:
- sns:Publish
Effect: Allow
Resource:
- Ref: BrokerTopic
Sid: SNS
Version: 2012-10-17
PolicyName: Broker
Type: AWS::IAM::Role
SNSトピックにメッセージを発行するコード
sample.go
req := &sns.PublishInput{
TopicArn: &topic,
Message: &message,
MessageAttributes: map[string]types.MessageAttributeValue{
"application": {DataType: aws.String("String"), StringValue: "bizlogic"},
"version": {DataType: aws.String("Number"), StringValue: "20"}, // バージョンは数値に変換出来る必要がある
},
}
所感
IoT Core を管理するチームが別チームのため好きに管理することができなかったため、バージョン互換性の仕組みを考えてみました。
何気にSNSを使う設計を自分でするのは初めてだったので色々と勉強になりました。