やりたいこと・背景
Lambdaをコンテナから作成する場合、Lambda関数が複数ある場合は関数ごとにイメージをビルドしなければならないのか?という単純な疑問が出てきた。その場合、イメージの管理が複雑になりそうなので、1つのイメージで複数のLabda関数を管理できないかと考えた。
結論
ランタイムイメージをビルドする際、各Lambda関数ごとにディレクトリを分け
、関数作成時にENTRYPOINT
を指定することで実現可能。
同一ディレクトリに main関数 を持つバイナリが複数存在すると、実行時にどの main関数 を呼び出すべきかが不明瞭になり、うまく動作しない。そのため、各Lambda関数は独自のディレクトリに配置される必要がある。
ランタイムイメージで関数ごとにディレクトリを分けることで、各Lambda関数が独立して実行されるようになる。
概要図
ローカル環境の準備
具体的な手順を以下のとおり
- フォルダ構成
root/
├ Dockerfile
├ main1.go
├ main2.go
└ main3.go
- Dockerfile作成
# ベースイメージの指定とビルドステージの開始
FROM golang:1.17-buster as builder
# 作業ディレクトリの設定
WORKDIR /go/src/lambda
# 依存関係のダウンロード
COPY go.mod go.sum ./
RUN go mod download
# ソースコードのコピー
COPY ./ ./
# ビルドの実行
RUN go build -o function1 ./main1.go
RUN go build -o function2 ./main2.go
RUN go build -o function3 ./main3.go
# ランナーステージの開始
FROM debian:buster as runner
# 作業ディレクトリの設定
WORKDIR /app/
# ビルドステージからバイナリのコピー
COPY --from=builder /go/src/lambda/function1 /app/function1
COPY --from=builder /go/src/lambda/function2 /app/function2
COPY --from=builder /go/src/lambda/function3 /app/function3
- main1.go, main2.go, main3.go
package main
import (
"fmt"
"context"
"github.com/aws/aws-lambda-go/lambda"
)
type MyEvent struct {
Name string `json:"name"`
}
func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
return fmt.Sprintf("Hello! for func1", name.Name ), nil
}
func main() {
lambda.Start(HandleRequest)
}
-
モジュール初期化
$ go mod init example.com/hello
-
モジュールの依存関係を最適化
$ go mod tidy
イメージビルド
$ docker build --no-cache -t lambda-go-multibuild .
※no-cacheオプションでキャッシュを無効化
バイナリ名の指定
RUN go build -o function1 ./main1.go
main1.go がコンパイルされると通常は main という名前でバイナリが作成されるが、-oオプションを利用して自分で指定する
ECRプッシュ
ECRリポジトリからプッシュコマンドを表示させて、ECRにイメージをプッシュする
※詳細な手順は下記を参照
【Amazon ECR】AWS CLI を使ってリポジトリへプッシュする
Lambda関数の作成
ECRのイメージを使ってLamdaを作成します。
関数名: 他の関数と重複しなければ好きな名前でOK
コンテナイメージURI:先ほどプッシュしたECRイメージを選択
ENTRYPOINT: /app/function1
Dockerfile ランナーステージでコピーした goバイナリのパスを指定