概要
以前の記事でLocalStackをインストールしました。 今回はLocalStackを使用してGoで書いたLambdaからDynamoDBを呼び出す方法を紹介します。
DynamoDBの操作
LocalStackはAWSのマネジメントコンソールと違い、各サービスに対しての操作はCLIから行います。 まずは、DynamoDBに対してテーブルとデータを登録します。
テーブル作成
LocalStackを立ち上げた直後はテーブルが存在しません。
$ aws --endpoint-url=http://localhost:4569 dynamodb list-tables { "TableNames": [] }
テーブル生成コマンドは下記の通りです。 このコマンドでは、「Nameという文字列のカラムがキー」である「testテーブル」を作成しています。
aws --endpoint-url=http://localhost:4569 dynamodb create-table \ --table-name test \ --attribute-definitions AttributeName=Name,AttributeType=S \ --key-schema AttributeName=Name,KeyType=HASH \ --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
次にデータを1つ登録します。
aws --endpoint-url=http://localhost:4569 dynamodb put-item \ --table-name test \ --item '{ "Name": { "S": "testtest" } }'
最後に、登録したデータが取得できるかを確認します。
aws --endpoint-url=http://localhost:4569 dynamodb scan --table-name test
GoでLambdaの処理を書く
続いて、DynamoDBに登録したデータを呼び出すLambdaの処理を書きます。 今回はGo言語で書きます。
Go言語の環境構築
環境構築はbrewで済ませることができます。
brew install go
続いて、Lambda関数で使用するパッケージをインストールしていきます。
go get -u github.com/aws/aws-sdk-go/aws \ github.com/aws/aws-sdk-go/aws/session \ github.com/aws/aws-sdk-go/aws/credentials \ github.com/aws/aws-lambda-go/lambda \ github.com/guregu/dynamo
DynamoDBへの操作はSDKをそのまま使う操作がだいぶ辛いです。
そこで、DynamoDBへの操作を容易に行えるgithub.com/guregu/dynamo
を使います。
具体的にどれだけ楽になるかを同じデータ更新処理で比較してみます。
SDKの場合
param := &dynamodb.UpdateItemInput{ TableName: aws.String("TableName"), Key: map[string]*dynamodb.AttributeValue{ "id": { N: aws.String("123"), }, }, ExpressionAttributeNames: map[string]*string{ "#username": aws.String("username"), }, ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{ ":username_value": { S: aws.String("hoge"), }, }, UpdateExpression: aws.String("set #username = :username_value"), ReturnConsumedCapacity: aws.String("NONE"), ReturnItemCollectionMetrics: aws.String("NONE"), ReturnValues: aws.String("NONE"), } resp, err := ddb.UpdateItem(param)
guregu/dynamoの場合
type User struct { id int `dynamo:"id"` username string } db := dynamo.New(session.New(), &aws.Config{ Region: aws.String("us-east-1"), }) table := db.Table("TableName") user := User{id: 123, username: "hoge"} err := table.Put(user).Run()
guregu/dynamo
を使用した方が簡潔に書けますね!
処理を書く
先述のguregu/dynamo
を使ってDynamoDBに接続する処理を書きます。
ポイントは、エンドポイントをLocalStackのエンドポイントに変更することです。
package main import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-lambda-go/lambda" "github.com/guregu/dynamo" ) type Event struct { Name string `json:"name"` } type Response struct { Result string `json:"Name:"` } type Test struct { Name string `dynamo:"Name"` } func handler(event Event) (Response, error) { conf := &aws.Config{ Credentials: credentials.NewStaticCredentials("dumy", "dumy", ""), Region: "us-east-1", Endpoint: "http://localhost:4569", } sess, err := session.NewSession(conf) db := dynamo.New(sess) table := db.Table("test") var result Test err = table.Get("Name", "testtest").One(&result) println(result.Name) return Response{Result: result.Name}, err } func main(){ //handler(Event{Name: "testtest"}) lambda.Start(handler) }
デプロイパッケージの作成
スクリプトを書いたあとは、デプロイパッケージの作成をしてLambdaにデプロイします。
macOSを使用している場合は、ハンドラ関数がLambda実行コンテキストと互換性を持たせなければなりません。そのため、コンパイルするときにLinux用のGOOS*1環境変数を設定する必要があります。
GOOS=linux GOARCH=amd64 go build -o handler zip handler.zip ./handler
Lambdaへのデプロイは下記コマンドです。
# 新規作成(コンテナ立ち上げ直後) aws lambda create-function \ --endpoint-url http://localhost:4574 \ --region us-east-1 \ --profile localstack \ --function-name handler \ --runtime go1.x \ --role r1 \ --handler handler \ --zip-file fileb://handler.zip # 既存ハンドラ関数の更新 aws lambda update-function-code \ --endpoint-url http://localhost:4574 \ --region us-east-1 \ --profile localstack \ --function-name handler \ --zip-file fileb://handler.zip --publish
動作確認
デプロイまでしておきつつ、動作確認はLocalStackを経由せずに手元から直接Goを動かすのが速くて楽です。
実行は下記コマンドで行います。
go run handler.go
LocalStackに登録したLambda関数を呼び出す時の問題点
せっかくデプロイしたので、LocalStack上のLambdaを呼び出したいですよね。 実行自体は下記のコマンドで実行します。
aws lambda --endpoint-url=http://localhost:4574 invoke \ --function-name handler \ --payload '{ "name": "testtest"}'\ result.log
しかし、LocalStack上のLambdaからDynamoDBに接続できない問題があります。 エンドポイントを変えずにAWSにデプロイし、実行すると問題なく接続できるので、LocalStack固有の問題なのではないかと思います。
参考までに、発生したエラーはこちらです。
HTTPConnectionPool(host='localhost', port=4574): Read timed out. (read timeout=60) localstack_1 | START RequestId: b7879ca4-4d51-15b6-ea01-1145f4c29e0c Version: $LATEST localstack_1 | RequestError: send request failed localstack_1 | caused by: Post http://localhost:4569//: dial tcp 127.0.0.1:4569: connect: connection refused localstack_1 | END RequestId: b7879ca4-4d51-15b6-ea01-1145f4c29e0c localstack_1 | REPORT RequestId: b7879ca4-4d51-15b6-ea01-1145f4c29e0c Duration: 51179.39 ms Billed Duration: 51200 ms Memory Size: 1536 MB Max Memory Used: 9 MB localstack_1 | { localstack_1 | "errorMessage": "RequestError: send request failed\ncaused by: Post http://localhost:4569//: dial tcp 127.0.0.1:4569: connect: connection refused", localstack_1 | "errorType": "baseError" localstack_1 | }
まとめ
今回はGoで書いたLambdaからDynamoDBを呼び出す方法を紹介しました。
DynamoDBに接続するためのGoの処理はguregu/dynamo
を使用することで、複雑な処理を書かなくて済みました。
LocalStackはCLIでしか操作できない点は難点であるものの、Lambdaを手元ですぐに実行できるのは大きなメリットだと思います。ただし、LocalStackに登録したLambda関数からDynamoDBへはうまく接続できないので、go run
などで直接goの関数を叩かなければならないという手間も発生します。*2