🌊

AWS CDK (Go) で Lambda (Go) をデプロイする

2022/11/07に公開約2,600字

今回の成果物

https://github.com/kasegao/cdk-lambda-go

背景

  • AWS CDK が v2 から Go を正式サポートしたのでせっかくなら Go で書きたい
  • Lambda も Go で書けるので Go で書きたい

⇒ 全部 Go で書きたい

という感じで Go でゴリゴリ書いていたのですが、皆さんご存じのとおり Go 製 Function を Lambda にデプロイするにはあらかじめ linux/amd64 向けにビルドしておく必要があります。これを CDK で実現するにはどうすればよいのか分からなかったので、色々と調べながら何とか動くものを作れたのでまとめます。

Function のビルドはローカルで実行される

cdk deploy すると初めにローカルで Lambda Function のビルドが実行され、その後成果物のバイナリを用いて Stack をデプロイという流れになるようです。したがって、事前にローカルで Go 製 Function をビルドするための設定を CDK 上で規定する必要があります。

このビルドですが、純粋なローカル環境を用いたビルド( GOOS=linux GOARCH=amd64 go build )と Docker コンテナを用いたビルドの二種類が用意されています。詳しくは公式ドキュメントを読んでください。

各ビルド方法での設定は次のように指定することができます。 Bundling 内で指定している Command が Docker を用いたビルド、 Local がローカル環境でのビルドに対応します(LocalBundling の詳細は後述します)。両方設定した場合は Local が優先されます。

fn := awslambda.NewFunction(stack, jsii.String("Lambda"), &awslambda.FunctionProps{
    Runtime: awslambda.Runtime_GO_1_X(),
    Code: awslambda.AssetCode_FromAsset(jsii.String("lambda"), &awss3assets.AssetOptions{
        Bundling: &awscdk.BundlingOptions{
            Image:   awslambda.Runtime_GO_1_X().BundlingImage(),
            Command: jsii.Strings("bash", "-c", "GOOS=linux GOARCH=amd64 go build -o handler"),
            Local:   &LocalBundling{},
        },
    }),
    Handler: jsii.String("handler"),
    Timeout: awscdk.Duration_Seconds(jsii.Number(30)),
})

Local ビルド

上記の実装について Command の方は特に説明することはないと思うので、 Local について解説していきます。内部実装を読むと、 Local には ILocalBundling というインターフェス型の変数を渡すことになっています(BundlingOptions)。

.....が、この ILocalBundling が何者なのかよくわからず、調べてみてもびっくりするぐらい情報が出てきません。ネットの海を彷徨ったり、公式ドキュメントと睨めっこしたりしているうちに、どうやら

func TryBundle(outputDir *string, options *awscdk.BundlingOptions) *bool

という関数が実装された何らかのオブジェクトを渡せば良いっぽいことが分かりました。

そこで、下記のような LocalBundling 型を実装しました。TryBundle の引数の outputDir にはバイナリの吐き出し先(通常は cdk.out/<id>/ )が渡されてくるので、素直にそこに向けてビルドします。

type LocalBundling struct{}

func (*LocalBundling) TryBundle(outputDir *string, options *awscdk.BundlingOptions) *bool {
	path := filepath.Join(*outputDir, "handler")
	var err error
	if runtime.GOOS == "windows" {
		err = exec.Command("cmd", "/c", fmt.Sprintf("set GOOS=linux&set GOARCH=amd64&cd lambda&go build -o %s", path)).Run()
	} else {
		err = exec.Command("bash", "-c", fmt.Sprintf("GOOS=linux GOARCH=amd64 cd lambda && go build -o %s", path)).Run()
	}
	return jsii.Bool(err == nil)
}

これで cdk synthcdk deploy を実行すると、無事ローカル環境でビルドしたバイナリをデプロイすることができました。

Discussion

ログインするとコメントできます