Lambda Layerでバイナリやライブラリを切り出す

golangaws

Lambdaで実行したい外部コマンドがある場合、通常バイナリをパッケージに含めることになりデプロイに時間がかかってしまう。

package main

import (
	"fmt"
	"os/exec"

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

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	cmd := exec.Command("git", "clone", "https://github.com/sambaiz/foobar.git", "/tmp/repo")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return events.APIGatewayProxyResponse{
			Body:       fmt.Sprintf("%s %s", string(output), err.Error()),
			StatusCode: 500,
		}, nil
	}
	return events.APIGatewayProxyResponse{
		Body:       string(output),
		StatusCode: 200,
	}, nil
}

func main() {
	lambda.Start(handler)
}
exec: "git": executable file not found in $PATH

Lambda Layerを使うと ライブラリやバイナリを切り出すことができ、複数Functionで共有することもできる。 ディレクトリをzipにしてLayerに指定すると中身が/optに展開され、/opt/binにはPATHが、/opt/libにはLD_LIBRARY_PATHが通るほか、 言語ごとのパッケージ置き場がある。

$ tree layer/
layer/
└── bin
    └── some-command

zipはS3に上げておくこともできるが、 SAMではAWS::Lambda::LayerVersionのContentUriでローカルのパスを指定することもでき、 sam local でも適用される。 そうする場合packageするのにAWS CLIのバージョンが1.16.67以降である 必要がある

AWS SAMでLambdaの関数をデプロイしServerless Application Repositoryに公開する - sambaiz-net

$ cat template.yaml 
...
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: hello-world/
      Handler: hello-world
      Runtime: go1.x
      Layers:
        - !Ref ExampleLayer
      Events:
        CatchAll:
          Type: Api
          Properties:
            Path: /hello
            Method: GET
  ExampleLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: SomeCommandLayer
      Description: some-command binary is included
      ContentUri: './layer'
      CompatibleRuntimes:
        - go1.x
      RetentionPolicy: Delete

というこどでlambci/git-lambda-layerを入れてgitを使えるようにしようと思ったが、 調べているうちにsrc-d/go-gitを見つけたのでこちらを使うことにした。

_, err := git.PlainClone("/tmp/repo", false, &git.CloneOptions{
	URL:      "https://github.com/sambaiz/foobar.git",
	Progress: os.Stdout,
})