こんにちは。
入社一年目のT.A.です。
初の実務案件でLambda + API GatewayのAPIに触れる機会がありました。
作成したLambda関数の動作確認は、実際にデプロイして確認する必要があり、
即時確認できないことに煩わしさを感じていました。
そこで見つけたのが、AWS SAMというフレームワークです。
Lambdaの動作確認をローカルで完結させることができます。
今回は「AWS SAM」のローカルテストを試していきたいと思います。
AWS SAM 概要
※詳細は下記をご参照ください
参考文献:AWS サーバーレスアプリケーションモデル
aws.amazon.com
AWS SAMの利点
- 設計したサーバレスアプリケーションをローカルのDockerコンテナ上で動作させ、テストすることができる。
- API Gateway等の周辺リソースも同時に構築することができる。
- SAMで構築したリソースをCloudFotmationを介してデプロイすることができる。
- DynamoDB Local等を併用することで、それらのリソース含めた動作確認ができる。
インストール
SAM LocalはDockerコンテナ上で動作するため、
Dockerも併せてインストールする必要があります。
SAM CLIのインストール手順は公式の記述がありますので、
ここでは割愛いたします。
参考文献:Installing the AWS SAM CLI
試してみること
API Gateway + Lambda のREST API今回はお試しで、以下二種の単純なAPIを作成します。
今回は、Lambda関数にGo言語を使用します。
実践
構築
アプリケーション作成
プロジェクト名は"hellosam"にします。
$ sam init -r go1.x -n hellosam 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
AWS quick start application templates: 1 - Hello World Example 2 - Step Functions Sample App (Stock Trader) Template selection: 1
Generating application:
Name: hellosam Runtime: go1.x Dependency Manager: mod Application Template: hello-world Output Directory: .
Next steps can be found in the README file at ./hellosam/README.md
テンプレートのファイル確認
sam initで以下のファイル群が作成されました。
テンプレートの動作確認
テンプレートをそのままビルド・実行します。
その後、APIにGETリクエストを投げてみます。
ビルド・実行
sam build
でビルドします。
$ sam build Building function 'HelloWorldFunction' Running GoModulesBuilder:BuildBuild Succeeded
Built Artifacts : .aws-sam\build Built Template : .aws-sam\build\template.yaml
Commands you can use next
[] Invoke Function: sam local invoke [] Deploy: sam deploy --guided
sam local start-api
でローカルでAPIを実行します。$ 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-10-05 11:28:00 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
※Dockerを起動せずにsam local start-api
を行うと下記のエラーが出ます。
先にDockerを起動しておきましょう。
$ sam local start-api Error: Running AWS SAM projects locally requires Docker. Have you got it installed and running?
GETリクエスト送信
http://127.0.0.1:3000/hello
上記アドレスにGETリクエストを投げてみます。
Hello, ... (グローバルIPアドレス)
上記のレスポンスが返ってきました。
ソースコード確認
ここでhello-world/main.goを確認してみます。
package mainimport ( "errors" "fmt" "io/ioutil" "net/http"
"github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" )
var ( // DefaultHTTPGetAddress Default Address DefaultHTTPGetAddress = "https://checkip.amazonaws.com"
// ErrNoIP No IP found in response ErrNoIP = errors.New("No IP in HTTP response")
// ErrNon200Response non 200 status code in response ErrNon200Response = errors.New("Non 200 Response found") )
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { resp, err := http.Get(DefaultHTTPGetAddress) if err != nil { return events.APIGatewayProxyResponse{}, err }
if resp.StatusCode != 200 { return events.APIGatewayProxyResponse{}, ErrNon200Response }
ip, err := ioutil.ReadAll(resp.Body) if err != nil { return events.APIGatewayProxyResponse{}, err }
if len(ip) == 0 { return events.APIGatewayProxyResponse{}, ErrNoIP }
return events.APIGatewayProxyResponse{ Body: fmt.Sprintf("Hello, %v", string(ip)), StatusCode: 200, }, nil }
func main() { lambda.Start(handler) }
処理内容をざっくり要約すると以下のような流れです。
- https://checkip.amazonaws.comにGETリクエスト。
これによってローカルコンピュータのグローバルIPアドレスを取得。 - 取得したIPアドレスの検証。
- 取得したIPアドレスをレスポンスボディに組み込む。
- レスポンスを返す。
レスポンスを確認し、正常に動作していることがわかりました。
参考文献:https://qiita.com/G-awa/items/b28bd16c95ff4ecfe441
API作成
APIの作成と動作確認を行います。
Lambda関数作成
ファイル構成を以下のように変更します。
- Makefile
- README.md
- hello-world/
- main.go
- main_test.go
- go.mod
- hello-str/ (新規作成)
- main.go (新規作成)
- go.mod (新規作成)
- hello-req/ (新規作成)
- main.go (新規作成)
- go.mod (新規作成)
- template.yaml
以下をそれぞれ記述します。
- hello-str/main.go...クエリストリングを取得、レスポンスボディに含めて返す関数
- hello-req/main.go...パスパラメータを取得、レスポンスボディに含めて返す関数
hello-str/main.go
package mainimport ( "fmt"
"github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{ Body: fmt.Sprintf("Hello, %v", request.QueryStringParameters["str"]), StatusCode: 200, }, nil } func main() { lambda.Start(handler) }
hello-req/main.go
package mainimport ( "fmt"
"github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" )
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{ Body: fmt.Sprintf("Hello, %v", request.PathParameters["req"]), StatusCode: 200, }, nil }
func main() { lambda.Start(handler) }
go.modはhello-world/直下のものをコピーします。
その上でmodule
の項目をそれぞれのディレクトリ名に変更します。
hello-str/go.mod
require github.com/aws/aws-lambda-go v1.13.3module hello-str // 変更箇所
go 1.15
hello-req/go.mod
require github.com/aws/aws-lambda-go v1.13.3module hello-req // 変更箇所
go 1.15
※main_test.goは単体テストコードです
今回は省略します。
template.yamlの編集
template.yamlを以下のように編集します。
ここでAPI Gatewayのパス等を設定します。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > hellosamSample SAM Template for hellosam
application-model/blob/master/docs/globals.rst Globals: Function: Timeout: 5
Resources:
# 変更なし HelloWorldFunction: # 省略
# 新規追加 HelloStrFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello-str/ Handler: hello-str Runtime: go1.x Tracing: Active Events: CatchAll: Type: Api Properties: Path: /hello/str Method: GET RequestParameters: "method.request.path.str": Required: true Caching: false Environment: Variables: PARAM1: VALUE
# 新規追加 HelloReqFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello-req/ Handler: hello-req Runtime: go1.x Tracing: Active Events: CatchAll: Type: Api Properties: Path: /hello/req/{req} Method: GET RequestParameters: "method.request.path.req" : "integration.request.path.req" Environment: Variables: PARAM1: VALUE
#以後省略
動作確認
テンプレートの動作確認と同様の手順でビルド・実行・動作確認を行います。
ビルド
sam build
でビルドします。
$ sam build Building function 'HelloWorldFunction' Running GoModulesBuilder:Build Building function 'HelloStrFunction' Running GoModulesBuilder:Build Building function 'HelloReqFunction' Running GoModulesBuilder:BuildBuild Succeeded
Built Artifacts : .aws-sam\build Built Template : .aws-sam\build\template.yaml
Commands you can use next
[] Invoke Function: sam local invoke [] Deploy: sam deploy --guided
実行
sam local start-api
でAPIを実行します。
$ sam local start-api Mounting HelloReqFunction at http://127.0.0.1:3000/hello/req/{req} [GET] Mounting HelloStrFunction at http://127.0.0.1:3000/hello/str [GET] 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-10-06 10:51:27 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
動作確認
GETリクエストを作成したそれぞれのAPIに送信します。
今回は文字列を返すだけですので、クエリストリングおよびパスパラメータは適当な文字列で構いません。
http://127.0.0.1:3000/hello/str?str=[任意の文字列]
http://127.0.0.1:3000/hello/req/[任意の文字列]
それぞれ以下のようなレスポンスになれば成功です。
Hello, [入力した任意の文字列]
終わりに
SAMを用いてLambda + API Gatewayの動作確認をローカルで行ってみました。
テンプレート記述のため、SAMを用いる際は最低限CloudFormationの知識およびテンプレートの書き方を学ぶ必要があります。
しかし、ローカルで動作確認しながら構築できるのでAWS上の環境を壊す心配が減ります。これは大きなメリットです。
また、単体テストであればテストコードで済みますが、
デプロイ前にAPI Gateway等周辺リソースを含めた動作確認ができる点で役に立ちます。