「AWS無料相談会」をオンラインで開催中

AWS SAMとGo言語で体感するサーバーレス開発

今回はAWS SAMを使用してサーバーレスアプリケーションを構築していきます。

API GatewayをトリガーとしてLambda関数が発火するという非常にシンプルな内容です。そのためサーバーレス開発のイメージを掴みやすいのではないかと思います。

AWS SAMとは

AWS SAM(Serverless Application Model)とは、AWS上にサーバーレスアプリケーションを構築することができるフレームワークです。YAMLまたはJSON形式で構成を記述することができます。

コマンドラインツールとしてAWS SAM CLIも提供されており、ローカル環境でもLambda関数を実行することができます。

準備

AWS CLIとAWS SAM CLIを導入します。pipやbrewといった、お好きなパッケージ管理ツールでインストールしてください。

$ pip install awscli
$ pip install aws-sam-cli
$ brew install awscli
$ brew install aws-sam-cli

AWS CLIで利用するプロファイルも設定しておきましょう。すでに設定済みの場合は不要です。

$ aws configure --profile iamSAM
AWS Access Key ID [None]: ******************
AWS Secret Access Key ID [None]: ******************
Default region name [None]: ap-northeast-1
Default output format [None]: json

サーバーレスアプリケーションのベースを作成

sam init コマンドでサーバーレスアプリケーションの雛形を作成します。

今回はGoで関数を作成していきます。

$ sam init --runtime go1.x --name aws-sam-golang

Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location
Choice: 1

Cloning app templates from https://github.com/awslabs/aws-sam-cli-app-templates.git

-----------------------
Generating application:
-----------------------
Name: aws-sam-golang
Runtime: go1.x
Dependency Manager: mod
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./aws-sam-golang/README.md

$ cd aws-sam-golang

$ tree
.
├── Makefile
├── README.md
├── hello-world
│   ├── main.go
│   └── main_test.go
└── template.yaml

1 directory, 5 files

hello-world/main.go が、今回のLambda関数の実装を行うファイルです。 template.yaml にはサーバーレスアプリケーションの関数定義が記述されています。

また、今回はGoのライブラリバージョン管理にModulesを使います。
Modulesについてはこちらの記事を御覧ください。

$ go mod init
go: creating new go.mod: module hoge/fuga/aws-sam-golang

雛形の実行テスト

sam local start-api コマンドで、ローカルでもLambda関数を実行することができます(Docker上で動作します)。

Lambda関数の実行にはバイナリが必要ですので、makeコマンドでビルドしましょう。

$ make build
GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world

$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [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
2020-01-19 20:38:36  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Invoking hello-world (go1.x)

この状態で、curlコマンドでAPIを叩いてみましょう。

$ curl http://localhost:3000/hello
Hello, 118.***.***.**

雛形のhello-world/main.goに従い、自分のグローバルIPアドレスが返ってきました。

SAM Local側では次のようなログが確認できます。

Fetching lambci/lambda:go1.x Docker container image......
Mounting /Users/daisakuhazui/develop/aws-sam-golang/hello-world as /var/task:ro,delegated inside runtime container
START RequestId: c0308535-1ed4-15d2-a58d-ff1ac09907ad Version: $LATEST
END RequestId: c0308535-1ed4-15d2-a58d-ff1ac09907ad
REPORT RequestId: c0308535-1ed4-15d2-a58d-ff1ac09907ad    Init Duration: 169.68 ms    Duration: 891.58 ms    Billed Duration: 900 ms    Memory Size: 128 MB    Max Memory Used: 27 MB
No Content-Type given. Defaulting to 'application/json'.
2020-01-19 20:38:58 127.0.0.1 - - [19/Jan/2020 20:38:58] "GET /hello HTTP/1.1" 200 -

Lambda関数の修正

雛形の動作確認が取れたので、次はLambda関数に手を加えましょう。

先ほど確認した通り、現状のコードはhttp://localhost:3000/helloへのリクエストに対しIPアドレスを返す実装になっています。

この実装をhttp://localhost:3000/hello/{user}に対してHello, {user}と返すよう、これから修正していきます。

`main.go`

package main

(中略)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    user, _ := request.PathParameters["user"]

    return events.APIGatewayProxyResponse{
        Body:       fmt.Sprintf("Hello, %s", user),
        StatusCode: 200,
    }, nil
}

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

関数を修正したらtemplate.yamlにも修正が必要です。

`template.yaml`
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  aws-sam-golang

  Sample SAM Template for aws-sam-golang

Globals:
  Function:
    Timeout: 5

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: hello-world
      Runtime: go1.x
      Tracing: Active
      Events:
        CatchAll:
          Type: Api
          Properties:
            Path: /hello/{user} # パスパラメータをセット
            Method: GET
      Environment:
        Variables:
          PARAM1: VALUE
・
・
・

Type: AWS::Serverless::FunctionでLambda関数であることを表し、Events配下のCatchAllでAPIの定義を行っています。

今回はパスパラメータを受け取れるようにPathの値を修正しました。

動作確認

関数とテンプレートの修正が終わったのでローカルで動作確認していきます。

$ make build
GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world

$ sam local start-api
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello/{user} [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
2020-01-19 20:47:07  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
Invoking hello-world (go1.x)

今度はパスパラメータを与えてcurlコマンドを叩いてみましょう。

$ curl http://localhost:3000/hello/Daisaku
Hello, Daisaku

OKです。想定通りの動きになっています。

AWS上にデプロイ

ローカルで動作確認を終えたので、実際にAWS上にデプロイしてみましょう。

$ sam validate --profile iamSAM
/Users/daisakuhazui/develop/aws-sam-golang/template.yaml is a valid SAM Template

$ sam package 
    --template-file template.yaml 
    --s3-bucket <YOUR BUCKET NAME> 
    --output-template-file packaged.yaml 
    --profile iamSAM

Uploading to a1342a2f62dce02aba5dfa11e11a6d1d  5023955 / 5023955.0  (100.00%)

Successfully packaged artifacts and wrote output template to file packaged.yaml.
Execute the following command to deploy the packaged template
sam deploy --template-file /Users/daisakuhazui/develop/aws-sam-golang/packaged.yaml --stack-name <YOUR STACK NAME>

$ sam deploy 
    --profile iamSAM 
    --region ap-northeast-1  
    --template-file /Users/daisakuhazui/develop/aws-sam-golang/packaged.yaml 
    --stack-name aws-sam-golang 
    --capabilities CAPABILITY_IAM

    Deploying with following values
    ===============================
    Stack name                 : aws-sam-golang
    Region                     : ap-northeast-1
    Confirm changeset          : False
    Deployment s3 bucket       : None
    Capabilities               : ["CAPABILITY_IAM"]
    Parameter overrides        : {}

Initiating deployment
=====================

Waiting for changeset to be created..

(中略)

Stack aws-sam-golang outputs:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OutputKey-Description                                                                    OutputValue
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HelloWorldFunctionIamRole - Implicit IAM Role created for Hello World function           arn:aws:iam::************:role/aws-sam-golang-HelloWorldFunctionRole-*************
HelloWorldAPI - API Gateway endpoint URL for Prod environment for First Function         https://**********.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
HelloWorldFunction - First Lambda Function ARN                                           arn:aws:lambda:ap-northeast-1:************:function:aws-sam-golang-
                                                                                         HelloWorldFunction-*************
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - aws-sam-golang in ap-northeast-1

これでAWS上へのデプロイが完了しました。

最後にAPI Gatewayのエンドポイントにアクセスしてみましょう。(API Gateway endpoint URL for Prod environment for First Functionの右にあるURL)

$ curl https://**********.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/Daisaku
Hello, Daisaku

API Gateway経由でも期待通りのレスポンスが返ってきました。

まとめ

AWS SAMとGo言語を使って非常にシンプルなサーバーレス構成を構築しました。

昨年のアップデートでAWS SAMのデプロイ方法が非常に簡単になり、とても使いやすくなったんじゃないかなと思います。(sam packagesam deployのパラメータは複雑ですが...)

参考になれば幸いです。

近年、MMMはAWS Lambdaに力を入れています。ぜひ以下のページもご覧ください。

サーバーレスアーキテクチャ(AWS Lambda)