概要
以前の記事で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(){
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