🐴

【Go言語 / コンテナ】Lambda+DynamoDBをServerless Frameworkで環境構築

2023/02/07に公開

Go言語でスケジュール実行するプログラムを組むことになり、実行環境を検討することに。

過去にServerless Framework[1]を利用し、AWS Lambda(以下、Lambda)で定期実行する関数を作成したことがあった。

LambdaとDynamoDBとの連携も過去に経験はあったが、当時のLambdaはzip形式のみサポート。

Lambdaのコンテナサポートは、リリース当時に試しただけで実務で導入することはなかったので、当時の知見を活かしつつ、Lambdaのコンテナ対応を取り入れることにした。

この記事の対象者

  • 「Lambda(コンテナ)+DynamoDB」の開発環境、環境構築方法を知りたい方
  • Lambdaをコンテナイメージで開発したい、開発方法を知りたい方
  • DynamoDBを開発環境で利用したい方(dynamodb-localを利用)
  • Go言語でLambda(コンテナ)の開発方法を知りたい方

TL;DR

tree -a .
.
├── .air.toml
├── Dockerfile
├── Dockerfile.dev
├── cmd
│   └── example
│       └── main.go
├── data
│   └── create_table.json
├── docker-compose.yml
├── go.mod
├── go.sum
└── serverless.yml
.air.toml
root = "."
tmp_dir = ".build"

[build]
  args_bin = []
  bin = "/usr/bin/aws-lambda-rie /main"
  cmd = "go build -o /main ./cmd/example"
  delay = 1000
  exclude_dir = ["assets", "tmp", "vendor", "testdata", ".build"]
  exclude_file = []
  exclude_regex = ["_test.go"]
  exclude_unchanged = false
  follow_symlink = false
  full_bin = ""
  include_dir = []
  include_ext = ["go", "tpl", "tmpl", "html"]
  kill_delay = "0s"
  log = "build-errors.log"
  send_interrupt = false
  stop_on_error = true

[color]
  app = ""
  build = "yellow"
  main = "magenta"
  runner = "green"
  watcher = "cyan"

[log]
  time = false

[misc]
  clean_on_exit = false

[screen]
  clear_on_rebuild = false
Dockerfile
FROM golang:1.18-bullseye as builder
WORKDIR /go/src/lambda
COPY go.mod go.sum ./
RUN go mod download
COPY ./ .
RUN GOARCH=amd64 GOOS=linux go build -a -o /main ./cmd/example

FROM debian:bullseye as runner
WORKDIR /app/
COPY --from=builder /main /main
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT [ "/main" ]
Dockerfile.dev
FROM golang:1.18-bullseye
RUN go install github.com/cosmtrek/air@latest
WORKDIR /go/src/lambda
COPY go.mod go.sum ./
RUN go mod download
COPY ./ .
ENV GOOS=linux GOARCH=amd64

ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie
RUN chmod 755 /usr/bin/aws-lambda-rie
CMD ["air", "-c", ".air.toml"]
cmd/example/main.go
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"log"
	"os"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/guregu/dynamo"
)

type item struct {
	Name     string    `dynamo:"Name,hash"`
	Link     string    `dynamo:"Link"`
	Datetime time.Time `dynamo:"Datetime"`
}

type Response events.APIGatewayProxyResponse

func Handler(ctx context.Context) (Response, error) {
	/**
	 * DynamoDB初期設定
	 */
	dynamoDbEndpoint := os.Getenv("DYNAMODB_ENDPOINT")
	dynamoDbTable := os.Getenv("DYNAMODB_TABLE")

	disableSsl := false
	if len(dynamoDbEndpoint) != 0 {
		disableSsl = true
	}

	dynamoDbRegion := os.Getenv("AWS_REGION")
	if len(dynamoDbRegion) == 0 {
		dynamoDbRegion = "ap-northeast-1"
	}

	sess := session.Must(session.NewSession())
	db := dynamo.New(sess, &aws.Config{Endpoint: aws.String(dynamoDbEndpoint), DisableSSL: aws.Bool(disableSsl), Region: aws.String(dynamoDbRegion)})
	table := db.Table(dynamoDbTable)

	/**
	 * 保存するデータを生成
	 */
	// JSTで現在時刻を取得
	jst, err := time.LoadLocation("Asia/Tokyo")
	if err != nil {
		log.Fatal(err)
		return Response{StatusCode: 500}, err
	}
	now := time.Now().In(jst) // ex. 2023-02-01T12:30:00+09:00

	item := item{
		Name:     "テスト",
		Link:     "https://zenn.dev",
		Datetime: now,
	}

	/**
	 * DynamoDBにPUT
	 */
	if err := table.Put(item).Run(); err != nil {
		log.Fatal(err)
		return Response{StatusCode: 500}, err
	}

	/**
	 * 結果をレスポンス
	 */
	var buf bytes.Buffer
	body, err := json.Marshal(map[string]interface{}{
		"message": "function executed successfully!",
	})
	if err != nil {
		return Response{StatusCode: 404}, err
	}
	json.HTMLEscape(&buf, body)

	resp := Response{
		StatusCode:      200,
		IsBase64Encoded: false,
		Body:            buf.String(),
		Headers: map[string]string{
			"Content-Type": "application/json",
		},
	}

	return resp, nil
}

func main() {
	lambda.Start(Handler)
}

data/create_table.json
{
    "TableName": "example-dev",
    "AttributeDefinitions": [
      {
        "AttributeName": "Name",
        "AttributeType": "S"
      }
    ],
    "KeySchema": [
      {
        "AttributeName": "Name",
        "KeyType": "HASH"
      }
    ],
    "ProvisionedThroughput": {
      "ReadCapacityUnits": 1,
      "WriteCapacityUnits": 1
    }
  }
  
docker-compose.yml
version: '3'
services:
  lambda:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - 9000:8080
    environment:
      DYNAMODB_ENDPOINT: dynamodb:8000
      DYNAMODB_TABLE: example-dev
      AWS_ACCESS_KEY_ID: DUMMYIDEXAMPLE
      AWS_SECRET_ACCESS_KEY: DUMMYEXAMPLEKEY
    depends_on:
      - dynamodb
    volumes:
      - .:/go/src/lambda
  dynamodb:
    image: amazon/dynamodb-local
    user: root
    command: -jar DynamoDBLocal.jar -sharedDb -dbPath ./data
    ports:
      - 8000:8000
    volumes:
      - dynamodb-data:/home/dynamodblocal/data

volumes:
  dynamodb-data:
    driver: local
go.mod
module example.com/go-lambda-dynamodb

go 1.18

require github.com/aws/aws-lambda-go v1.37.0

require (
	github.com/aws/aws-sdk-go v1.44.191 // indirect
	github.com/cenkalti/backoff/v4 v4.1.2 // indirect
	github.com/gofrs/uuid v4.2.0+incompatible // indirect
	github.com/guregu/dynamo v1.18.0 // indirect
	github.com/jmespath/go-jmespath v0.4.0 // indirect
	golang.org/x/net v0.1.0 // indirect
)
serverless.yml
service: go-lambda-dynamo

frameworkVersion: '3'

useDotenv: true

provider:
  name: aws
  region: ap-northeast-1
  stage: ${opt:stage, 'dev'}
  environment:
    DYNAMODB_TABLE: example-${opt:stage, self:provider.stage}
  ecr:
    images:
      go-lambda-dynamo_example:
        path: ./
        file: Dockerfile
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
      Resource:
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"

functions:
  example:
    image:
      name: go-lambda-dynamo_example
    environment:
      DYNAMODB_TABLE: ${self:provider.environment.DYNAMODB_TABLE}

resources:
  Resources:
    DynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        TableName: ${self:provider.environment.DYNAMODB_TABLE}
        AttributeDefinitions:
          - AttributeName: Name
            AttributeType: S
        KeySchema:
          - AttributeName: Name
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1          

開発環境構築

はじめに開発環境を構築するまでの流れをおさらいする。

まず、LambdaのコンテナサポートについてはAWS公式ドキュメントに記載があるので、こちらを参考に実行環境を整えていく。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-image.html

DynamoDBをローカル環境で再現する方法を調べると、dynamodb-localというDockerイメージがAWSから提供されているのでコレを利用する。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.html

Lambda用とDynamoDB用のコンテナを作成し、データのやり取りができるよう開発環境を整えていこう。

Serverless Framework

「Serverless Framework golang」等で検索すると、Serverlesss Frameworkの初期設定から始まる記事がほとんどである。

しかし、開発環境ではServerless Frameworkを一切使わない。

zip形式のLambda関数を実装する際は、ローカル環境でsls invoke localとコマンドを実行するとServerless Frameworkで定義された関数をローカル環境でテストすることができる。

これはバックグラウンドでLambdaをエミュレータするDockerイメージを実行することで実現している。

しかし、コンテナでLambda関数を実装する場合、AWSから提供されているRIE(Runtime Interface Emulator) というエミュレータをDockerイメージに追加し、テストを行うことになる。

そのため、Serverless Frameworkのエミュレータが不要になり、開発時は全く利用することがない。

AWS環境にdeployするまでは利用することがないので、本番環境構築時に改めてセッティングを行う。

Lambdaの開発用Dockerイメージ

LambdaのDockerイメージを下記のように定義する。

Dockerfile.dev
FROM golang:1.18-bullseye
RUN go install github.com/cosmtrek/air@latest
WORKDIR /go/src/lambda
COPY go.mod go.sum ./
RUN go mod download
COPY ./ .
ENV GOOS=linux GOARCH=amd64

ADD https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie /usr/bin/aws-lambda-rie
RUN chmod 755 /usr/bin/aws-lambda-rie
CMD ["air", "-c", ".air.toml"]

なるべく本番と開発環境で実行環境を揃えたかったが、開発に使いたいツールやライブラリの都合上、完全に分けてイメージを作るようにした。

そのため、ファイル名をDockerfile.devと開発用Dockerfileとしている。

前述のRIEを追加している点は公式ドキュメント[2]にならっており、1点大きく異なる点が air というパッケージを利用している点である。

https://github.com/cosmtrek/air

通常、Go言語でLambdaを開発していると、ソースコードを修正するたびにコンテナを再起動しないと修正内容が反映されない。

airを利用することでホットリロードできるようになり、いちいち再起動する必要がなくなる。

最終行のCMD ["air", "-c", ".air.toml"]airを起動しており、.air.tolmが設定ファイルとなる。

.air.toml
root = "."
tmp_dir = ".build"

[build]
  args_bin = []
  bin = "/usr/bin/aws-lambda-rie /main"
  cmd = "go build -o /main ./cmd/example"
  delay = 1000
  exclude_dir = ["assets", "tmp", "vendor", "testdata", ".build"]
  exclude_file = []
  exclude_regex = ["_test.go"]
  exclude_unchanged = false
  follow_symlink = false
  full_bin = ""
  include_dir = []
  include_ext = ["go", "tpl", "tmpl", "html"]
  kill_delay = "0s"
  log = "build-errors.log"
  send_interrupt = false
  stop_on_error = true

[color]
  app = ""
  build = "yellow"
  main = "magenta"
  runner = "green"
  watcher = "cyan"

[log]
  time = false

[misc]
  clean_on_exit = false

[screen]
  clear_on_rebuild = false

基本はairのサンプルファイル[3]と同じだが、binの指定に通常バイナリファイルだけを書くところ、RIEを間にかませるようにしている点が大きく異なるので注意。

bin = "/usr/bin/aws-lambda-rie /main"

開発環境のDynamoDB

開発環境ではAWSから提供されているdynamodb-localというイメージを使い開発を行う。

docker composeで立ち上げるが、それも公式ドキュメントにならって定義するのみである。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html

AWS様にお膳立てしてもらっていることがわかったので、次の工程に映る。

docker compose

前述のLambda、DynamoDB用のコンテナを実行するため、docker-compose.ymlを下記のように定義する。

docker-compose.yml
version: '3'
services:
  lambda:
    build:
      context: .
      dockerfile: Dockerfile.dev
    ports:
      - 9000:8080
    environment:
      DYNAMODB_ENDPOINT: dynamodb:8000
      DYNAMODB_TABLE: example-dev
      AWS_ACCESS_KEY_ID: DUMMYIDEXAMPLE
      AWS_SECRET_ACCESS_KEY: DUMMYEXAMPLEKEY
    depends_on:
      - dynamodb
    volumes:
      - .:/go/src/lambda
  dynamodb:
    image: amazon/dynamodb-local
    user: root
    command: -jar DynamoDBLocal.jar -sharedDb -dbPath ./data
    ports:
      - 8000:8000
    volumes:
      - dynamodb-data:/home/dynamodblocal/data

volumes:
  dynamodb-data:
    driver: local

DynamoDBのデータは永続化するためにvolumeをマウントさせている。
※参考:https://developers.freee.co.jp/entry/dynamodb-local

環境変数

DynamoDBの接続設定は環境に応じて変わるため、設定値を環境変数として渡している。

dynamodb-localの仕様として、AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYは何らかの値を設定しておく必要がある。

また、接続先は dynamodb:8000 となる。

記事によってはlocalhost:8000と指定している場合もあるが、Lambdaをzip形式で実行するかコンテナ形式で実行するかで変わるので要注意。

ソースコード(Go言語)

サンプルとして「DynamoDBにデータを追加するLambda関数」をGo言語で実装する。

※実装するファイルは/cmd/example/main.goの1ファイル

はじめに下記コマンドで初期設定&必要なライブラリを追加する。

go mod init example.com/go-lambda-dynamodb
go get github.com/aws/aws-lambda-go/lambda github.com/aws/aws-sdk-go/aws github.com/guregu/dynamo

DynamoDBを操作するパッケージは色々選択肢があるが、下記の記事を参考にguregu/dynamoを利用することにした。

https://future-architect.github.io/articles/20220601b/

処理の大まかなフローは下記のようにし、実装をしてみる。

  1. DynamoDBの初期設定
  2. 保存するデータを生成
  3. DynamoDBにデータを保存
  4. 結果をレスポンス

※DynamoDBのスキーマ定義は動作確認の説明で記載

/cmd/example/main.go
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"log"
	"os"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/guregu/dynamo"
)

type item struct {
	Name     string    `dynamo:"Name,hash"`
	Link     string    `dynamo:"Link"`
	Datetime time.Time `dynamo:"Datetime"`
}

type Response events.APIGatewayProxyResponse

func Handler(ctx context.Context) (Response, error) {
	/**
	 * DynamoDB初期設定
	 */
	dynamoDbEndpoint := os.Getenv("DYNAMODB_ENDPOINT")
	dynamoDbTable := os.Getenv("DYNAMODB_TABLE")

	disableSsl := false
	if len(dynamoDbEndpoint) != 0 {
		disableSsl = true
	}

	dynamoDbRegion := os.Getenv("AWS_REGION")
	if len(dynamoDbRegion) == 0 {
		dynamoDbRegion = "ap-northeast-1"
	}

	sess := session.Must(session.NewSession())
	db := dynamo.New(sess, &aws.Config{Endpoint: aws.String(dynamoDbEndpoint), DisableSSL: aws.Bool(disableSsl), Region: aws.String(dynamoDbRegion)})
	table := db.Table(dynamoDbTable)

	/**
	 * 保存するデータを生成
	 */
	// JSTで現在時刻を取得
	jst, err := time.LoadLocation("Asia/Tokyo")
	if err != nil {
		log.Fatal(err)
		return Response{StatusCode: 500}, err
	}
	now := time.Now().In(jst) // ex. 2023-02-01T12:30:00+09:00

	item := item{
		Name:     "テスト",
		Link:     "https://zenn.dev",
		Datetime: now,
	}

	/**
	 * DynamoDBにPUT
	 */
	if err := table.Put(item).Run(); err != nil {
		log.Fatal(err)
		return Response{StatusCode: 500}, err
	}

	/**
	 * 結果をレスポンス
	 */
	var buf bytes.Buffer
	body, err := json.Marshal(map[string]interface{}{
		"message": "function executed successfully!",
	})
	if err != nil {
		return Response{StatusCode: 404}, err
	}
	json.HTMLEscape(&buf, body)

	resp := Response{
		StatusCode:      200,
		IsBase64Encoded: false,
		Body:            buf.String(),
		Headers: map[string]string{
			"Content-Type": "application/json",
		},
	}

	return resp, nil
}

func main() {
	lambda.Start(Handler)
}

動作確認

開発環境に必要な下記ファイルが揃ったら、動作確認をしてみよう。

tree -a .
.
├── .air.toml
├── Dockerfile.dev
├── cmd
│   └── example
│       └── main.go
├── docker-compose.yml
├── go.mod
└── go.sum

はじめにdocker compose up -dを実行しコンテナを起動する。

docker compose up -d

初期設定

次にDynamoDBのテーブルを作成する。

AWSで実行する環境はServerless Frameworkがよしなに管理してくれるが、開発環境の場合は手動で作成する必要がある。

テーブル作成用のjsonファイル(/data/create_table.json)を予め作成しておき、AWS CLIでテーブルを作成する。

/data/create_table.json
{
    "TableName": "example-dev",
    "AttributeDefinitions": [
      {
        "AttributeName": "Name",
        "AttributeType": "S"
      }
    ],
    "KeySchema": [
      {
        "AttributeName": "Name",
        "KeyType": "HASH"
      }
    ],
    "ProvisionedThroughput": {
      "ReadCapacityUnits": 1,
      "WriteCapacityUnits": 1
    }
  }
aws dynamodb create-table --cli-input-json file://data/create_table.json --endpoint-url http://localhost:8000 

実行

準備が整ったのでLambda関数をテストする。

テスト方法はcurl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'と実行するのみである。

curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
{"statusCode":200,"headers":{"Content-Type":"application/json"},"multiValueHeaders":null,"body":"{\"message\":\"function executed successfully!\"}"}%

正常に処理されたら上記のようにレスポンスが返ってくる。

DynamoDBにデータが保存されているか確認するにはscanコマンドを実行すると、保存されている全データを閲覧できる。

aws dynamodb scan --table-name example-dev --endpoint-url http://localhost:8000
{
    "Items": [
        {
            "Link": {
                "S": "https://zenn.dev"
            },
            "Datetime": {
                "S": "2023-02-01T11:32:43.146708001+09:00"
            },
            "Name": {
                "S": "テスト"
            }
        }
    ],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}

トラブルシューティング

context deadline exceeded

Lambda関数を実行した際にcontext deadline exceededとエラーなる場合、DynamoDBへの接続先が誤っている可能性が高い。

2023/02/01 13:37:34 RequestCanceled: request context canceled
caused by: context deadline exceeded

実行直後に接続エラーとはならず、しばらく待たされてレスポンスされるので気づきにくい。。。

僕はdocker-compose.ymlで設定する環境変数DYNAMODB_ENDPOINTdynamodb:8000と指定すべきところ、dynamodbとだけ指定してたためこのエラーに当たった。

原因に関連しないレスポンスが返ってくるので、かなりハマってしまった。

dlv

Go言語でデバッグする際、dlvというデバッグツールを利用するのが一般的だが、Lambda(コンテナ)の開発環境で利用する方法が調べきれておらず、導入に至っていない。

APIサーバを構築した時はairdlvを導入し、.air.tomlfull_bindlvのコマンドを設定していた。

.air.toml
# full_bin = ""
full_bin = "dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec /main"

ただ、上記のように設定した場合、RIEを間にかますことができない。

RIEをかましてバイナリを実行するか、dlvをかましてバイナリを実行するかになるが、RIEがないとそもそもローカル環境で実行できないので選択の余地がない。

違う方法でdlvを実行することでいけるのかも知れないが。。。(わかる方いればコメント頂けると助かります)

本番環境構築

開発環境で動作確認できたら、AWSに実行環境を作成する。

Lambdaの本番用Dockerイメージ

はじめに本番用のDockerイメージを作成する。

開発用のDockerイメージはテスト用のエミュレータ(RIE)や開発に便利なパッケージを入れていたが、それらが一切不要となるので別途軽量なイメージを作成する。

Dockerfile
FROM golang:1.18-bullseye as builder
WORKDIR /go/src/lambda
COPY go.mod go.sum ./
RUN go mod download
COPY ./ .
RUN GOARCH=amd64 GOOS=linux go build -a -o /main ./cmd/example

FROM debian:bullseye as runner
WORKDIR /app/
COPY --from=builder /main /main
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT [ "/main" ]

Serverless Framework

最後にServerless Frameworkで実行環境を下記のように定義する。

serverless.yml
service: go-lambda-dynamo

frameworkVersion: '3'

useDotenv: true

provider:
  name: aws
  region: ap-northeast-1
  stage: ${opt:stage, 'dev'}
  environment:
    DYNAMODB_TABLE: example-${opt:stage, self:provider.stage}
  ecr:
    images:
      go-lambda-dynamo_example:
        path: ./
        file: Dockerfile
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
      Resource:
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"

functions:
  example:
    image:
      name: go-lambda-dynamo_example
    environment:
      DYNAMODB_TABLE: ${self:provider.environment.DYNAMODB_TABLE}

resources:
  Resources:
    DynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        TableName: ${self:provider.environment.DYNAMODB_TABLE}
        AttributeDefinitions:
          - AttributeName: Name
            AttributeType: S
        KeySchema:
          - AttributeName: Name
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

Lambdaの設定

Lambdaに関する設定は下記の部分である。

  ecr:
    images:
      go-lambda-dynamo_example:
        path: ./
        file: Dockerfile
	
  :
  :
 
functions:
  example:
    image:
      name: go-lambda-dynamo_example
    environment:
      DYNAMODB_TABLE: ${self:provider.environment.DYNAMODB_TABLE}

Dockerfileのディレクトリとファイル名を指定するだけで、buildしたイメージをECRへpushまでしてくれる。

go-lambda-dynamo_exampleがイメージタグになり、functions部分でそのイメージタグを指定するだけである。

必要であればDYNAMODB_TABLEのように環境変数を設定することが可能。

ちなみにLambdaがコンテナサポートされた際にServerless Frameworkもそれに追従する形で対応されたが、その当時は事前に手動でECRへpushしておき、イメージのdigestをserverless.ymlに記載する形だった。

過去の設定方法
functions:
  app:
    image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/example@sha256:1dfac5075ffc3b8beec1b509797ba8028a3b29e7847413e000dfas42345

その後、現在の設定方法にバージョンアップされている。

https://zenn.dev/qazx7412/articles/75862c72d6effa

DynamoDBの設定

DynamoDBに関する設定は下記の部分である。

  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
      Resource:
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"
  
  :
  :
  
resources:
  Resources:
    DynamoDbTable:
      Type: 'AWS::DynamoDB::Table'
      Properties:
        TableName: ${self:provider.environment.DYNAMODB_TABLE}
        AttributeDefinitions:
          - AttributeName: Name
            AttributeType: S
        KeySchema:
          - AttributeName: Name
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

resources部分で作成するテーブルの定義を記載する。

テーブル定義は開発環境で作成したcreate_table.jsonと同じ構成となる。

iamRoleStatements部分ではLambdaからDynamoDBへのアクセス許可を設定する。

deploy

本番環境の構成を定義できたので、AWSにdeployする。

sls deploy

deployできたら定義したresourceがAWSで作成されていることを確認しよう。

試しにECRを確認すると指定したタグでイメージがpushされていることを確認できる。

動作確認

最後に本番環境で動作確認を行う。

AWSのコンソール上からLambda関数をテストすると、成功していることが確認できる。

また、slsコマンドでもテストすることができ、その場合下記のように関数を指定して実行する。

serverless invoke -f example
{
    "statusCode": 200,
    "headers": {
        "Content-Type": "application/json"
    },
    "multiValueHeaders": null,
    "body": "{\"message\":\"function executed successfully!\"}"
}

DynamoDBに登録されているかコンソール上で確認するとソースコード内で生成した情報が登録されていることが確認できる。

脚注
  1. https://www.serverless.com/ ↩︎

  2. https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-image.html ↩︎

  3. https://github.com/cosmtrek/air/blob/master/air_example.toml ↩︎

Discussion

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