AWS IoT トピックペイロードによる Kinesis シャードの分離

AWS IoT トピックペイロードによる Kinesis シャードの分離

IoT トピックのペイロードを顧客 ID ごとに異なる Kinesis シャードへルーティングし、後続処理でも顧客ごとの順序を保つ方法。

Takahiro Iwasa
4 min read

Kinesis のシャードは IoT トピックのペイロードによって分離できます。これは、異なる顧客のレコードを後続処理のために別々のシャードへ振り分けたい場合に役立ちます。以下の例では、customer_id によってレコードをルーティングしています。

前提条件

以下があらかじめマシンにインストールされている必要があります。

構築

AWS IoT のトピックルールでは PartitionKey: ${customer_id}(37 行目)を指定しており、これによってレコードがどのようにシャードへ振り分けられるかが決まります。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
Lambda:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src/
Events:
Kinesis:
Type: Kinesis
Properties:
BatchSize: 100
BisectBatchOnFunctionError: true
Enabled: true
StartingPosition: LATEST
Stream: !GetAtt Kinesis.Arn
FunctionName: aws_iot_kinesis_partition_lambda
Handler: lambda_function.lambda_handler
Role: !GetAtt IamRole.Arn
Runtime: python3.8
Kinesis:
Type: AWS::Kinesis::Stream
Properties:
Name: aws_iot_kinesis_partition_stream
RetentionPeriodHours: 24
ShardCount: 1
TopicRule:
Type: AWS::IoT::TopicRule
Properties:
RuleName: aws_iot_kinesis_partition_topic_rule
TopicRulePayload:
Actions:
- Kinesis:
PartitionKey: ${customer_id}
RoleArn: !GetAtt IamRole.Arn
StreamName: aws_iot_kinesis_partition_stream
AwsIotSqlVersion: 2016-03-23
RuleDisabled: false
Sql: SELECT * FROM 'aws-iot-kinesis-partition-topic'
IamRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
- Effect: Allow
Principal:
Service: iot.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- kinesis:GetShardIterator
- kinesis:GetRecords
- kinesis:DescribeStream
- kinesis:PutRecord
Resource:
- !GetAtt Kinesis.Arn
PolicyName: policy
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/aws_iot_kinesis_partition_lambda
RetentionInDays: 1

ストリームされたレコードを処理する Lambda 関数は、短い Python スクリプトです。

lambda_function.py
import json
def lambda_handler(event, context):
for record in event['Records']:
print(json.dumps(record['kinesis']))

SAM を使ってスタックをビルド・デプロイします。

Terminal window
sam build
sam deploy \
--stack-name aws-iot-kinesis-partition-lambda \
--capabilities CAPABILITY_NAMED_IAM

テスト

テスト用のペイロードは、AWS IoT のトピックに直接送信できます。

Terminal window
aws iot-data publish \
--topic aws-iot-kinesis-partition-topic \
--payload '{"customer_id": 1, "message": "Hello from AWS IoT"}' \
--cli-binary-format raw-in-base64-out

Lambda のログを確認し、partitionKey がペイロード内の customer_id と一致していることを確認します。

後片付け

この例で作成したリソースは、以下のコマンドで削除します。

Terminal window
sam delete --stack-name aws-iot-kinesis-partition-lambda

まとめ

IoT トピックルールで PartitionKey: ${customer_id} を設定したことで、Lambda コンシューマー側に何のパーティショニングロジックを持たせることなく、同一顧客のレコードを同じ Kinesis シャードへルーティングできました。ルーティングはこの一つの式だけで完結しており、レコードが event['Records'] に到達する時点ですでに同じシャードへ届いています。だからこそ後続処理で顧客ごとの順序を維持でき、顧客のイベントを順番に処理する必要がある処理にとっては重要なポイントになります。ただし注意すべき点として、これは customer_id の値がある程度均等に分布している場合にのみ負荷を均等に分散できるという前提があります。パーティションキーを正しく設定していても、特定の顧客の量が突出して多ければ、そのトラフィックは一つのシャードに集中してしまいます。

About the author

Takahiro Iwasa

Takahiro Iwasa

Software Developer

This blog shares technical notes from hands-on projects—architecture, implementation, and AWS service integrations.