My Note

自己理解のためのブログ

APIGateway + Lambda + Go(Echo)で開発するためのローカル環境をServerlessFrameworkで再現する

はじめに

以前の記事で APIGW + Lambdaで GoのフレームワークであるEchoを動かす検証を実施しました。 実際にデプロイすることができたので今回は開発を行うためにローカル環境をServerlessFrameworkで構築しました。

yhidetoshi.hatenablog.com

構成

Client --> APIGW --> Lambda(Goコンテナ)

をローカル環境に構築します。ServerlessFrameworkで実行すれば、この構成を簡単に用意できます。

ローカル環境

  • serverless-offline をインストールする
npm install --save-dev serverless-offline
  • プロジェクトを作成する
    • sls create -t <テンプレート> -n <プロジェクト名>
sls create -t aws-go -n serverless-echo-lambda

*) テンプレートについては以下が対応していました。色々ありますね。

Supported templates are: "aws-clojure-gradle", "aws-clojurescript-gradle", "aws-nodejs", "aws-nodejs-docker", "aws-nodejs-typescript", "aws-alexa-typescript", "aws-nodejs-ecma-script", "aws-python", "aws-python3", "aws-python-docker", "aws-groovy-gradle", "aws-java-maven", "aws-java-gradle", "aws-kotlin-jvm-maven", "aws-kotlin-jvm-gradle", "aws-kotlin-jvm-gradle-kts", "aws-kotlin-nodejs-gradle", "aws-scala-sbt", "aws-csharp", "aws-fsharp", "aws-go", "aws-go-dep", "aws-go-mod", "aws-ruby", "aws-provided", "tencent-go", "tencent-nodejs", "tencent-python", "tencent-php", "azure-csharp", "azure-nodejs", "azure-nodejs-typescript", "azure-python", "cloudflare-workers", "cloudflare-workers-enterprise", "cloudflare-workers-rust", "fn-nodejs", "fn-go", "google-nodejs", "google-nodejs-typescript", "google-python", "google-go", "kubeless-python", "kubeless-nodejs", "knative-docker", "openwhisk-java-maven", "openwhisk-nodejs", "openwhisk-php", "openwhisk-python", "openwhisk-ruby", "openwhisk-swift", "spotinst-nodejs", "spotinst-python", "spotinst-ruby", "spotinst-java8", "twilio-nodejs", "aliyun-nodejs", "plugin", "hello-world".

プロジェクト作成コマンドを実行すると自動で以下が作成されました。

❯ tree .
.
├── Makefile
├── hello
│   └── main.go
├── serverless.yml
└── world
    └── main.go

今回は、前回の記事で作成したコードを利用するので、サンプルで自動作成された hello/worldディレクトリを削除しました。 今回用意したディレクトリ構成は以下になります。Echoで ヘルスチェックのAPIを実装しており、ソースコードはこちらのGitHubにあります。

github.com

❯ tree .
.
├── Makefile
├── README.md
├── api
│   └── healthcheck
│       └── healthcheck.go
├── bin
│   └── main
├── conf
│   └── config.go
├── go.mod
├── go.sum
├── handler
│   └── auth
│       └── auth.go
├── main.go
└── serverless.yml
  • serverless.ymlも自動で作成されますが、以下のとおりに変更しました。
    • "追記" の部分を追加しています。自動作成されたコメント部分は削除しています。
    • pluginsserverless-offline を指定します
    • customuseDocker で trueを指定します
      • 設定しないと以下のエラーで apiを実行できませんでした
        • GET /v1/api/healthcheck (λ: main) ✖ Unhandled exception in handler 'main'. ✖ ENOENT: no such file or directory, open '.go'

    • timeout はデフォルト値では timeoutになってしまったので定義しました
    • 主な原因は、Lambda(go) のコンテナを起動するのに時間がかかるためでした
service: serverless-echo-lambda
frameworkVersion: '3'

# 追記
custom:
  serverless-offline:
    useDocker: true

provider:
  name: aws
  runtime: go1.x
  stage: v1 #追記
  timeout: 30 #追記

package:
  patterns:
    - '!./**'
    - ./bin/**

functions:
  main:
    handler: bin/main
    events:
      - http: # RESTAPI
          path: /api/{proxy+}
          method: get
# 追記
plugins:
  - serverless-offline

デプロイと動作確認

.PHONY: build clean deploy deploy-local

build:
    env GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -o bin/main ./main.go

clean:
    rm -rf ./bin

deploy-local: build
    sls offline

deploy: clean build
    sls deploy --verbose
  • ローカル環境を起動します。
    • sls offline
    • goのコードをコンパイルしてデプロイする場合は make deploy-local でもOK
❯ sls offline


Starting Offline at stage v1 (us-east-1)

Offline [http for lambda] listening on http://localhost:3002
Function names exposed for local invocation by aws-sdk:
           * main: serverless-echo-lambda-v1-main

   ┌────────────────────────────────────────────────────────────────────────┐
   │                                                                        │
   │   GET | http://localhost:3000/v1/api/{proxy*}                          │
   │   POST | http://localhost:3000/2015-03-31/functions/main/invocations   │
   │                                                                        │
   └────────────────────────────────────────────────────────────────────────┘

Server ready: http://localhost:3000 🚀
  • ヘルスチェックAPIをコールする
❯ curl http://localhost:3000/v1/api/healthcheck
{"status":200,"message":"Success to connect echo"}

リクエストを投げると Dockerイメージを起動してからリクエストを受け付けてくれました。リクエスト終了後に確認したら割とすぐにコンテナは停止していました。

❯ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

❯ curl http://localhost:3000/v1/api/healthcheck
{"status":200,"message":"Success to connect echo"}%

❯ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                     NAMES
8179a26361e2   lambci/lambda:go1.x   "/var/runtime/aws-la…"   4 seconds ago   Up 3 seconds   0.0.0.0:53718->9001/tcp   great_grothendieck
  • APIGWの機能はServerlessFrameworkで再現してくれています。
    • serverlessFrameworkが 3000番Listenして apigw --> lambdaコンテナに接続されています
❯ sudo lsof -i -P | grep "LISTEN" | grep 3000
node      44358       hidetoshi   32u  IPv6 0x8764e5xxxxxxxxxx      0t0    TCP localhost:3000 (LISTEN)

さいごに

今回は、ServerlessFrameworkで Client --> APIGW --> Lambda(Echo) の環境をローカルに再現しました。 ローカルでAWS環境を再現して開発できるのはとても便利ですね。