kakakakakku blog

Weekly Tech Blog: Keep on Learning!

LocalStack と samlocal コマンドで SQS x Lambda 構成をローカル環境で実行する

開発中に AWS Lambda 関数をローカル環境で実行するなら AWS SAM CLI の sam local invoke -e event.json コマンドを使えば良く,また AWS Lambda 関数のベストプラクティスに載っている Lambda ハンドラーをコアロジックから分離します。 を意識して実装すれば,単体テストを軸に動作確認を進めることもできる.

とは言え,実際に AWS Lambda 関数のイベントソースマッピング(トリガー設定)を確認したいこともあり,今回は AWS SAM を使って構築する Amazon SQS キューと AWS Lambda 関数の組み合わせを samlocal コマンドを使って LocalStack にデプロイして動作確認をしてみた.結果的に期待した通りに動いた❗️

\( 'ω')/ LocalStack 便利すぎ〜

🚀 アーキテクチャ図

🚀 検証環境

template.yaml

検証に使った AWS SAM テンプレート(AWS CloudFormation テンプレート)を以下に載せておく.特別なことはなく AWS::SQS::QueueAWS::Serverless::Function を構築して,イベントソースマッピングを設定してある.ランタイムは何でも良く,今回は Python 3.9 にしてある.

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  Queue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: kakakakakku-queue
      ReceiveMessageWaitTimeSeconds: 20
  Function:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: kakakakakku-function
      CodeUri: app/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        SqsEvent:
          Type: SQS
          Properties:
            Queue: !GetAtt Queue.Arn

app/app.py

AWS Lambda 関数で動かす Python コードはあくまで必要最低限にして,Amazon SQS キューのメッセージをログ出力(AWS CloudWatch Logs)している🐍

def lambda_handler(event, context):
    for record in event['Records']:
        print(record['body'])

GitHub にも置いておいた💡

github.com

🚀 samlocal コマンドを準備する

今回は AWS SAM (AWS CloudFormation) と LocalStack を組み合わせるため,LocalStack AWS CLI (awslocal コマンド) に加えて AWS SAM CLI for LocalStack (samlocal コマンド) をセットアップしておく.samlocal --version コマンドを実行して,通常の sam コマンドにバイパスされていれば OK❗️

$ pip install aws-sam-cli-local

$ samlocal --version
SAM CLI, version 1.81.0

github.com

🚀 LocalStack にデプロイする

次はさっそく LocalStack にデプロイする.AWS SAM CLI を使ったことがあれば「今まで通り」と言えば伝わると思う.sam コマンドではなく samlocal コマンドに置き換えて,あとは同じく builddeploy を実行する❗️すると samlocal コマンドによって,LocalStack にデプロイできる.

samlocal deploy コマンドの実行結果をできる限りそのまま載せておく.見慣れたログではあるけど,最後の arn:aws:cloudformation:ap-northeast-1:000000000000:changeSet/samcli-deploy1682430872/b79df315 を見て「ああ!LocalStack にデプロイしてたんだ!」と気付くレベル✌️

  • samconfig.toml を作る
  • AWS SAM 専用の Amazon S3 バケットを作る
    • 今回だと aws-sam-cli-managed-default-samclisourcebucket-055e11c4
  • AWS リソースを作る
    • AWS::SQS::Queue
    • AWS::Lambda::Function
    • AWS::IAM::Role
    • AWS::Lambda::EventSourceMapping
$ export DEFAULT_REGION=ap-northeast-1

$ samlocal build

$ samlocal deploy --guided

Configuring SAM deploy
======================

    Looking for config file [samconfig.toml] :  Not found

    Setting default arguments for 'sam deploy'
    =========================================
    Stack Name [sam-app]: sandbox-app
    AWS Region [ap-northeast-1]:
    #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
    Confirm changes before deploy [y/N]:
    #SAM needs permission to be able to create roles to connect to the resources in your template
    Allow SAM CLI IAM role creation [Y/n]:
    #Preserves the state of previously provisioned resources when an operation fails
    Disable rollback [y/N]:
    Save arguments to configuration file [Y/n]:
    SAM configuration file [samconfig.toml]:
    SAM configuration environment [default]:

    Looking for resources needed for deployment:
    Creating the required resources...
    Successfully created!

    Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-055e11c4
    A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False

    Saved arguments to config file
    Running 'sam deploy' for future deployments will use the parameters saved above.
    The above parameters can be changed by modifying samconfig.toml
    Learn more about samconfig.toml syntax at
    https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

    Uploading to sandbox-app/62f50535d41ccb37bda86914d0d680f3  405 / 405  (100.00%)

    Deploying with following values
    ===============================
    Stack name                   : sandbox-app
    Region                       : ap-northeast-1
    Confirm changeset            : False
    Disable rollback             : False
    Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-055e11c4
    Capabilities                 : ["CAPABILITY_IAM"]
    Parameter overrides          : {}
    Signing Profiles             : {}

Initiating deployment
=====================

    Uploading to sandbox-app/12ff669083061914f8a1286e9c315653.template  781 / 781  (100.00%)


Waiting for changeset to be created..

CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation                                                           LogicalResourceId                                                   ResourceType                                                        Replacement
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add                                                               Queue                                                               AWS::SQS::Queue                                                     N/A
+ Add                                                               Function                                                            AWS::Lambda::Function                                               N/A
+ Add                                                               FunctionRole                                                        AWS::IAM::Role                                                      N/A
+ Add                                                               FunctionSqsEvent                                                    AWS::Lambda::EventSourceMapping                                     N/A
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:000000000000:changeSet/samcli-deploy1682430872/b79df315


2023-04-25 00:00:00 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                                                      ResourceType                                                        LogicalResourceId                                                   ResourceStatusReason
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_COMPLETE                                                     AWS::SQS::Queue                                                     Queue                                                               -
CREATE_COMPLETE                                                     AWS::IAM::Role                                                      FunctionRole                                                        -
CREATE_COMPLETE                                                     AWS::Lambda::Function                                               Function                                                            -
CREATE_COMPLETE                                                     AWS::Lambda::EventSourceMapping                                     FunctionSqsEvent                                                    -
CREATE_COMPLETE                                                     AWS::CloudFormation::Stack                                          sandbox-app                                                         -
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Successfully created/updated stack - sandbox-app in ap-northeast-1

🚀 デプロイ結果を確認する

awslocal コマンドを使って LocalStack にデプロイされた AWS リソースを確認する(日付は適当にマスキングしてある).

Amazon SQS キュー

$ awslocal sqs list-queues
{
    "QueueUrls": [
        "http://localhost:4566/000000000000/kakakakakku-queue"
    ]
}

AWS Lambda 関数

$ awslocal lambda list-functions
{
    "Functions": [
        {
            "FunctionName": "kakakakakku-function",
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:kakakakakku-function",
            "Runtime": "python3.9",
            "Role": "arn:aws:iam::000000000000:role/sandbox-app-FunctionRole-c5268ca9",
            "Handler": "app.lambda_handler",
            "CodeSize": 405,
            "Description": "",
            "Timeout": 3,
            "MemorySize": 128,
            "LastModified": "2023-04-25T00:00:00.000000+09:00",
            "CodeSha256": "fpUHNomors6Sbn3Ii0zpVUYGBpzxbmxSXB950QZmisg=",
            "Version": "$LATEST",
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "ee14fbd2-1412-431b-b615-7c87676f4330",
            "PackageType": "Zip",
            "Architectures": [
                "x86_64"
            ],
            "EphemeralStorage": {
                "Size": 512
            },
            "SnapStart": {
                "ApplyOn": "None",
                "OptimizationStatus": "Off"
            }
        }
    ]
}

AWS Lambda 関数(イベントソースマッピング)

$ awslocal lambda list-event-source-mappings
{
    "EventSourceMappings": [
        {
            "UUID": "98e14881-4802-4e3d-be48-ffdf6766a455",
            "BatchSize": 10,
            "MaximumBatchingWindowInSeconds": 0,
            "EventSourceArn": "arn:aws:sqs:ap-northeast-1:000000000000:kakakakakku-queue",
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:kakakakakku-function",
            "LastModified": "2023-04-25T00:00:00.000000+09:00",
            "State": "Enabled",
            "StateTransitionReason": "USER_INITIATED",
            "FunctionResponseTypes": []
        }
    ]
}

Amazon S3 バケット

$ awslocal s3api list-buckets
{
    "Buckets": [
        {
            "Name": "aws-sam-cli-managed-default-samclisourcebucket-055e11c4",
            "CreationDate": "2023-04-25T00:00:00+00:00"
        },
        {
            "Name": "awslambda-ap-northeast-1-tasks",
            "CreationDate": "2023-04-25T00:00:00+00:00"
        }
    ],
    "Owner": {
        "DisplayName": "webfile",
        "ID": "75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a"
    }
}

Amazon CloudWatch Logs ロググループ

Amazon CloudWatch Logs ロググループは AWS SAM (AWS CloudFormation) で構築していないので1個もなくて OK👌この後 AWS Lambda 関数を実行してログを確認するため,実行前の状況を確認しておきたかった.

$ awslocal logs describe-log-groups
{
    "logGroups": []
}

🚀 動作確認をする

次は awslocal コマンドを使って Amazon SQS キューにメッセージを送信して,AWS Lambda 関数のイベントソースマッピングの動作確認をする.

$ MESSAGE='{ "my-key": "my-value" }'
$ awslocal sqs send-message --queue-url http://localhost:4566/000000000000/kakakakakku-queue --message-body ${MESSAGE}
{
    "MD5OfMessageBody": "3234172c8554dd70c6b2dbaac5acf11e",
    "MessageId": "678a5cd4-8849-4969-bbd7-0b9df93703d3"
}

すると Amazon CloudWatch Logs ロググループに "{ \"my-key\": \"my-value\" }" というログを確認できて,期待通り AWS Lambda 関数が実行されたことがわかる❗️

$ awslocal logs describe-log-groups
{
    "logGroups": [
        {
            "logGroupName": "/aws/lambda/kakakakakku-function",
            "creationTime": 1682431237731,
            "metricFilterCount": 0,
            "arn": "arn:aws:logs:ap-northeast-1:000000000000:log-group:/aws/lambda/kakakakakku-function:*",
            "storedBytes": 286
        }
    ]
}

$ awslocal logs describe-log-streams --log-group-name /aws/lambda/kakakakakku-function
{
    "logStreams": [
        {
            "logStreamName": "2023/04/25/[$LATEST]fe281c36f2654eb40cce1a30fbcfd8eb",
            "creationTime": 1682431237736,
            "firstEventTimestamp": 1682431237714,
            "lastEventTimestamp": 1682431237728,
            "lastIngestionTime": 1682431237742,
            "uploadSequenceToken": "1",
            "arn": "arn:aws:logs:ap-northeast-1:000000000000:log-group:/aws/lambda/kakakakakku-function:log-stream:2023/04/25/[$LATEST]fe281c36f2654eb40cce1a30fbcfd8eb",
            "storedBytes": 286
        }
    ]
}

$ awslocal logs get-log-events --log-group-name /aws/lambda/kakakakakku-function --log-stream-name '2023/04/25/[$LATEST]fe281c36f2654eb40cce1a30fbcfd8eb'
{
    "events": [
        {
            "timestamp": 1682431237714,
            "message": "START RequestId: 93a8f37b-5d8b-4c96-81fc-4e7bc3d3239e Version: $LATEST",
            "ingestionTime": 1682431237742
        },
        {
            "timestamp": 1682431237718,
            "message": "{ \"my-key\": \"my-value\" }",
            "ingestionTime": 1682431237742
        },
        {
            "timestamp": 1682431237723,
            "message": "END RequestId: 93a8f37b-5d8b-4c96-81fc-4e7bc3d3239e",
            "ingestionTime": 1682431237742
        },
        {
            "timestamp": 1682431237728,
            "message": "REPORT RequestId: 93a8f37b-5d8b-4c96-81fc-4e7bc3d3239e\tDuration: 13.08 ms\tBilled Duration: 14 ms\tMemory Size: 128 MB\tMax Memory Used: 128 MB\t",
            "ingestionTime": 1682431237742
        }
    ],
    "nextForwardToken": "f/00000000000000000000000000000000000000000000000000000003",
    "nextBackwardToken": "b/00000000000000000000000000000000000000000000000000000000"
}

もちろん AWS CloudFormation スタック sandbox-app もできあがっている✌️

$ awslocal cloudformation list-stacks
{
    "StackSummaries": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-sam-cli-managed-default/5f4cfa71",
            "StackName": "aws-sam-cli-managed-default",
            "CreationTime": "2023-04-25T00:00:00.000000+00:00",
            "LastUpdatedTime": "2023-04-25T00:00:00.000000+00:00",
            "StackStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/sandbox-app/d0a1ffad",
            "StackName": "sandbox-app",
            "CreationTime": "2023-04-25T00:00:00.000000+00:00",
            "LastUpdatedTime": "2023-04-25T00:00:00.000000+00:00",
            "StackStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

🚀 お掃除

使わなくなったら awslocal cloudformation delete-stack コマンドで AWS CloudFormation スタックを削除できる.ちゃんと削除後は StackStatus が DELETE_COMPLETE になっている.

$ awslocal cloudformation delete-stack --stack-name sandbox-app

$ awslocal cloudformation list-stacks
{
    "StackSummaries": [
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/aws-sam-cli-managed-default/5f4cfa71",
            "StackName": "aws-sam-cli-managed-default",
            "CreationTime": "2023-04-25T00:00:00.000000+00:00",
            "LastUpdatedTime": "2023-04-25T00:00:00.000000+00:00",
            "StackStatus": "CREATE_COMPLETE",
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        },
        {
            "StackId": "arn:aws:cloudformation:ap-northeast-1:000000000000:stack/sandbox-app/d0a1ffad",
            "StackName": "sandbox-app",
            "CreationTime": "2023-04-25T00:00:00.000000+00:00",
            "LastUpdatedTime": "2023-04-25T00:00:00.000000+00:00",
            "DeletionTime": "2023-04-25T00:00:00.000000+00:00",
            "StackStatus": "DELETE_COMPLETE",
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

$ awslocal lambda list-functions
{
    "Functions": []
}

🚀 まとめ

Amazon SQS キューと AWS Lambda 関数の組み合わせなど「イベントソースマッピング(トリガー設定)」の動作確認をローカル環境で行うために samlocal コマンドを使って LocalStack にデプロイしてみた.最終的には LocalStack でサポートされている API 次第ではあるけど,今回の簡単なシナリオでは期待通りに動いた❗️

ちなみに LocalStack を使うときは,基本 awslocal コマンドを使うけど,LocalStack Web Application の Resource Browser を使うと,一部の AWS リソースであれば画面から確認できて便利なときもある❗️最初はウェブサイトからどうやってローカル環境の LocalStack にアクセスしてるんだろ?と疑問だったけど,ちょっと調べてみたら https://localhost.localstack.cloud:4566/ に対して API を呼び出す仕組みになっていた💨

docs.localstack.cloud

\( 'ω')/ あ〜 LocalStack 便利〜