My Note

自己理解のためのブログ

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

はじめに

前回の記事では、サーバレス環境をローカルに用意するためにserverless frameworkを利用しました。そこで今回はSAMを利用して同じ環境を構築してみます。 基本的にServerlessFrameworkを利用してきたのでSAMを使うのは今回が初めてでした。 構成は Client --> APIGW --> Lambdaで進めます。

yhidetoshi.hatenablog.com

ローカル環境構築

aws-cliがインストールされている前提で進めます。

brew tap aws/tap
brew install aws-sam-cli

今回はGoで環境構築するので go1.xをruntimeに指定してプロジェクトを作成します。

❯ sam init --runtime go1.x --name serverless
Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
    1 - Hello World Example
    2 - Infrastructure event management
    3 - Multi-step workflow
Template: 1

Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.

Based on your selections, the only dependency manager available is mod.
We will proceed copying the template using mod.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: n

Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)

sam-initすると自動でディレクトリとファイルが作成されます。今回は hello-world を削除して echo ディレクトリを配置しました。 こちらは前回の記事と同じもので、echoフレームワークでヘルスチェックAPIを実装しています。 ソースコードはこちらです。

github.com

ディレクトリ構成

serverless
├── README.md
├── echo
│   ├── Makefile
│   ├── api
│   │   └── healthcheck
│   │       └── healthcheck.go
│   ├── conf
│   │   └── config.go
│   ├── echo
│   ├── go.mod
│   ├── go.sum
│   ├── handler
│   │   └── auth
│   │       └── auth.go
│   └── main.go
├── events
│   └── event.json
└── template.yaml

echoフレームワークの部分は前回の記事になどに記載していますので今回は省略します。 goをコンパイルするときに利用するMakefileと samを実行する template.yaml のコードを記載します。

PHONY: deps clean build

deps:
    go get -u ./...

clean:
    rm -rf echo # 変更

build:
    GOOS=linux GOARCH=amd64 go build -o echo ./main.go 
  • template.yaml (# 修正 の部分を自動生成されたファイルを修正しました)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  description

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 30 # 修正

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: echo # 修正
      Handler: echo # 修正
      Runtime: go1.x
      Architectures:
        - x86_64
      Events:
        CatchAll:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /api/{proxy+} # 修正
            Method: GET
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          PARAM1: VALUE

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldAPI:
    Description: "API Gateway endpoint URL for Prod environment for First Function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "First Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

このtemplate.yamlですが、AWSに展開することも考えて以下のように書き換えました。どちらもローカルでは同じ動きをします。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  description

Globals:
  Function:
    Timeout: 30

Resources:
  MyAPI:
    Type: AWS::Serverless::Api
    Properties:
      Name: sam-echo-test
      StageName: v1
      EndpointConfiguration: REGIONAL
  
  SAMEchoFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: echo
      Handler: echo
      Runtime: go1.x
      Architectures:
        - x86_64
      Events:
        GetApi:
          Type: Api
          Properties:
            Path: /api/{proxy+}
            Method: get
            RestApiId: !Ref MyAPI
            Auth:
              ResourcePolicy:
                CustomStatements: [{
                  "Effect": "Allow",
                  "Principal": "*",
                  "Action": "execute-api:Invoke",
                  "Resource": "arn:aws:execute-api:ap-northeast-1:*:*/*",
                  "Condition": {
                    "IpAddress": {
                      "aws:SourceIp": "X.X.X.X/32"
                    }
                  } 
                }]
      #Environment:
      #  Variables:
      #    PARAM1: VALUE

デプロイと動作確認

❯ make build
GOOS=linux GOARCH=amd64 go build -o echo ./main.go
  • ローカルでsam実行
❯ sam local start-api

Mounting HelloWorldFunction at http://127.0.0.1:3000/api/{proxy+} [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2022-11-03 16:35:45  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

Invoking echo (go1.x)
Skip pulling image and use local one: public.ecr.aws/sam/emulation-go1.x:rapid-1.61.0-x86_64.

Mounting /Users/hidetoshi/github-repos/github.com/yhidetoshi/serverless-sam-go-local/serverless/echo as /var/task:ro,delegated inside runtime container
START RequestId: 22427e50-7eb8-425a-a06e-2739449f6d7a Version: $LATEST
2022/11/03 07:38:23 echo cold start
END RequestId: 22427e50-7eb8-425a-a06e-2739449f6d7a
REPORT RequestId: 22427e50-7eb8-425a-a06e-2739449f6d7a  Init Duration: 113.63 ms        Duration: 735.62 ms     Billed Duration: 736 ms Memory Size: 128 MB     Max Memory Used: 128 MB
2022-11-03 16:38:23 127.0.0.1 - - [03/Nov/2022 16:38:23] "GET /api/healthcheck HTTP/1.1" 200 -
  • clientからヘルスチェクAPIを実行
❯ curl http://127.0.0.1:3000/api/healthcheck
{"status":200,"message":"Success to connect echo"}

リクエスト実行中には以下のコンテナが起動して lambdaをエミュレートして処理終了したらコンテナは削除されました。

❯ docker ps
CONTAINER ID   IMAGE                                                    COMMAND                  CREATED        STATUS                  PORTS                      NAMES
7eb1599fd838   public.ecr.aws/sam/emulation-go1.x:rapid-1.61.0-x86_64   "/var/rapid/aws-lamb…"   1 second ago   Up Less than a second   127.0.0.1:5959->8080/tcp   focused_wilson

(追記) ローカルではなく、AWSにデプロイする方法については、↓の記事にまとめました。

yhidetoshi.hatenablog.com

さいごに

前回の記事と同様にサーバーレス環境(APIGW + Lambda + Go(echo))のローカル環境を構築することができました。 この程度のローカル環境を構築するだけだと SAMとServerlessFrameworkはほとんど負担感は変わらない感じでした。 ServerlessFrameworkとSAMで機能や特徴が異なると思うので構築する環境で使い分けていければと思います。