3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ARISE analyticsAdvent Calendar 2023

Day 24

Serverless Framework ローカル環境でGoをLambdaで実行する例

Last updated at Posted at 2023-12-23

Serverlessめっちゃ便利

ローカルでLambdaの実行環境を構築する場合に使うツールにServerless Frameworkがあります。

いつもローカル環境ではDocker、サーバーはECSなりEC2などを使っていたところからマイクロサービスを構築できるように勉強したので

それを共有します。

今回のお題

  • Goを使ってAPIを構築する
  • パッケージはGinを使う
  • APIをLambdaを使ってコールできるようにする

これらを満たすやり方を紹介していきます。

環境

  • M2 Macbook Air
  • Go v1.21.4 darwin/arm64
  • npm 10.2.4
  • Docker 24.0.6, build ed223bc

お急ぎの方はこちらから

コードたち

Go

まずはGoでAPIを構築していきましょう。

GinとLambdaを使うのでそれぞれインポートします。

今回はついでにenvの読み取り方も示すのでenvパッケージも使います。

main.go
package main

import (
	"context"
	"log"

	env "github.com/caarlos0/env/v10"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	ginadapter "github.com/awslabs/aws-lambda-go-api-proxy/gin"
	"github.com/gin-gonic/gin"
)

type config struct {
	SAMPLE_ENV string `env:"SAMPLE_ENV"`
}

var ginLambda *ginadapter.GinLambda

func init() {
	// stdout and stderr are sent to AWS CloudWatch Logs
	log.Printf("Gin cold start")

	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		log.Fatalf("%+v\n", err)
	}
	r := gin.Default()
	r.GET("/ping", func(ctx *gin.Context) {
		ctx.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.GET("/hello", func(ctx *gin.Context) {
		ctx.JSON(200, gin.H{
			"message": "world",
		})
	})

	ginLambda = ginadapter.New(r)
}

func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	return ginLambda.ProxyWithContext(ctx, req)
}

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

エントリーポイントであるmain関数にはlambda.Startをコールして、Handler関数を呼び出します。

mainよりも先にinit関数が呼ばれて各種設定を行います。

envの設定についてはconfig構造体を定義して、env.Parseをして値を入れます。

SAMPLE_ENVについてはserverless.yamlで定義するので後ほど。

r := gin.Default()以降はでルーティングを行います。

シンプルにするためにエンドポイントのレスポンスは固定の文字列にしました。

設定が終わったらginとLambdaをつなぐためにginLambda = ginadapter.New(r)としてHandlerで利用します。

serverless.yaml

serverless.yaml
service: aws-lambda-go-api-proxy-gin

provider:
  name: aws
  architecture: arm64
  environment:
    SAMPLE_ENV: SAMPLE_ENV
  runtime: provided.al2
  stage: ${opt:stage, self:custom.defaultStage}
  region: ap-northeast-1
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - "logs:*"
          Resource: "*"

plugins:
  - serverless-go-plugin
  - serverless-offline

custom:
  defaultStage: api
  serverless-offline:
    useDocker: true

package:
  individually: true
  exclude:
    - "./**"

functions:
  api:
    handler: bootstrap
    timeout: 100
    events:
      - http:
          path: ping
          method: get
      - http:
          path: hello
          method: get

ポイントは

  • ENVの設定
    ENVはprovider > environmentで定義します。

今回はSAMPLE_ENV: SAMPLE_ENVとしてセットしました。

ここはyamlファイルに載せていい内容にする、もしくはParameter Storeなどのサービスを使って設定するようにしましょう。

  • Runtimeの設定
    provider > runtimeで設定します。

ここで注意ですが、go1.xのランタイムはサポートが終了するのでprovided.al2を使いましょう。

こちらはビルドしたバイナリが必要になるので別途ビルドできるようにする必要があります。

  • serverless-offlineのオプション
    provided.al2を利用する際にはdockerを使用します。

custom > serverless-offline > useDockerをtrueにしておきましょう

ローカル環境で実行

ビルドしてserverless offlineコマンドを実行してみよう。

(参考のGitHubにはnpmでビルドとserverless offlineコマンドは実行できるようにしてあります。)

% serverless offline

> go_lambda_serverless@1.0.0 dev
> serverless offline

~中略~

   ┌───────────────────────────────────────────────────────────────────────┐
   │                                                                       │
   │   GET | http://localhost:3000/api/ping                                │
   │   POST | http://localhost:3000/2015-03-31/functions/api/invocations   │
   │   GET | http://localhost:3000/api/hello                               │
   │   POST | http://localhost:3000/2015-03-31/functions/api/invocations   │
   │                                                                       │
   └───────────────────────────────────────────────────────────────────────┘

Server ready: http://localhost:3000 🚀

これで起動できました。

curl http://localhost:3000/api/pingでリクエストしてみると

GET /api/ping (λ: api)
✖ Lambda API listening on port 9001...

✖ 2023/12/09 12:09:51 Gin cold start

✖ [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

  [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
   - using env: export GIN_MODE=release
   - using code:        gin.SetMode(gin.ReleaseMode)

  [GIN-debug] GET    /ping                     --> main.init.0.func1 (3 handlers)

✖ START RequestId: 01490924-e0e4-1335-a097-7dd6f3cfdc51 Version: $LATEST

✖ [GIN] 2023/12/09 - 12:09:51 | 200 |      17.208µs |                 | GET      "/ping"

✖ END RequestId: 01490924-e0e4-1335-a097-7dd6f3cfdc51

✖ REPORT RequestId: 01490924-e0e4-1335-a097-7dd6f3cfdc51        Init Duration: 94.07 ms     Duration: 12.88 ms      Billed Duration: 13 ms  Memory Size: 1024 MB    Max Memory Used: 61 MB

のように表示され、レスポンスは{"message":"pong"}となっています。

みんなもServerlessでGoのAPIを動かしてみてください。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?