aws sam cli で ローカルの lambda から 別の ローカルの lambda を叩く

おはようございます。watura です。 最近、aws sam cli を使って遊び始めました。使った事はありますか?

aws sam cli というのは AWS SAM を build, test, debug, deploy できる cli ツールです。

このツールの導入とかは公式のドキュメントをみてほしいです。
なお、動かすのには、aws cli と Docker は必須です。

ざっくりとした仕組みですが、Docker を使って Lambda を一瞬起動するみたいなことをしています。
なので、実際の Lambda に近い動きをしてくれるので、ローカルで Lambda の開発できます。

さて、Lambda を使っていると、Lambda から別の Lambda を呼び出したいみたいなことをやりたくなります。
いい感じなサンプルが見つからなかったので作ってみました。
今回は、Golang で作ってみています。

完全に別の Lambda を呼び出すだけのサンプルです。
HelloWorldFunction から、HelloInvokedFunction を呼び出しています。

HelloInvokedFunction

簡単なほうから、説明します。HelloInvokedFunction は受け取った Input に[Invoked] って書いて返すだけの簡単なプログラムです。

package main
import (
	"context"
	"fmt"

	"github.com/aws/aws-lambda-go/lambda"
)

// Response for Lambda
type Response struct {
	Message string `json:"body"`
}

// Lambda で実行される関数
// 引数として、context と Response を受け取ります。
func handler(ctx context.Context, input Response) (Response, error) {
	// input の message に [Invoked] を追加する
	msg := fmt.Sprintf("[Invoked] %v", input.Message)
	return Response{msg}, nil
}
	  
func main() {
	// Lambda で呼び出された時に実行される handler を登録します。
	lambda.Start(handler)
}

HelloWorldFunction

では、HelloWorldFunction をみていきます。

func main() {
	l.Start(handler)
}

lambda で呼び出された時に実行されるhandlerを登録します。

// Response for lambda

type Response struct {
	Message string `json:"body"`
}
// こちらは、contextだけ受け取るようにしています。
// 使っていないので、あれですが。
func handler(ctx context.Context) (Response, error) {
	// Local な場合はlocal用のconfigを使う
	var lmd *lambda.Lambda
	
	// SAMで実行すると、環境変数に AWS_SAM_LOCAL というのが追加されます。
	// これが true だったら、ローカルで実行しているということがわかります。
	if os.Getenv("AWS_SAM_LOCAL") == "true" {
		// ここが重要なところです。
		lmd = lambda.New(session.New(localConfig()))
	} else {
		lmd = lambda.New(session.New())
	}
	
	j, err := json.Marshal(Response{"Hello World"})
	if err != nil {
		return Response{}, err
	}
	// 別のlambda を呼び出す時に使う設定です。
	// まだ、実際にdeployしたらどうなるかとか試していないです。
	input := &lambda.InvokeInput{
		// 呼び出したい関数名を指定します。
		FunctionName:   aws.String("HelloInvokedFunction"),
		// 呼び出されるlambdaに渡すpayloadです。
		Payload:        j,
		// デフォルトがRequestResponseなので、指定不要です
		// 実際には &lambda.InvokeInput{FunctionName: aws.String("HelloInvokedFunction")}  だけで十分なはずです。
		InvocationType: aws.String("RequestResponse"),
	}
	// 別のlambdaを上で作った設定で呼び出します。
	resp, err := lmd.Invoke(input)
	if err != nil {
		return Response{}, err
	}
	var r Response
	// HelloInvokeのresponseのpayloadをUnmarshalして、使えるようにします。
	err = json.Unmarshal(resp.Payload, &r)
	if err != nil {
		return Response{}, err
	}
	return Response{r.Message}, nil
}
func localConfig() *aws.Config {
	c := aws.NewConfig()
	// ここ重要
	c.Endpoint = aws.String("host.docker.internal:3001")
	// ここも重要
	c.DisableSSL = aws.Bool(true)
	return c
}

Lambda は docker で実行されます。なので、endpoint として、 localhost とか 127.0.0.1 を指定しても動きません。

Docker の Client 自身が呼び出されてしまいます。なので、 host.docker.internal を呼び出す必要があります。
また、ローカルで動かすときは、sslを使っていないので、オフにします。

動かす

sample プロジェクトでは、

make start
make run

で、試せるようになっています。
run した結果は、out.txt に書き込まれるので、
{"body":"[Invoked] Hello World"}
と表示されていたら、成功です。

この時実行しているコマンドは、


build:
	GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world
	GOOS=linux GOARCH=amd64 go build -o hello-invoked/hello-invoked ./hello-invoked
start:
	sam local start-lambda
run: build
	aws lambda invoke --function-name "HelloWorldFunction" --endpoint-url "http://127.0.0.1:3001" --no-verify-ssl out.txt

hello-world と hello-invoked をビルドしたり、aws lambda invoke を呼び出すようにしています。

以上で、ざっくりですが aws sam cli  で Lambda から Lambda を呼び出す手順です。


noteでコード書いた時にハイライトさせるのどうやるんだろう。