はじめに
サーバレスで手軽にWeb APIを立てたい場合、**API Gatewayのproxy integrationとlambdaアプリケーションソースでのルーティングでこんなに簡単にお安く構築できるんだな〜**と感心してしまったのでそのご紹介と、そのリソース群のTerraform moduleを作ってTerraform Registryに公開したのでその内容の紹介で記事を書いてみました。
(しかも最近VPC Lambdaのコールドスタートが大幅に改善されたばかり -> [発表] Lambda 関数が VPC 環境で改善されます | Amazon Web Services ブログ )
lambda proxy integrationのmodule,S3のartifact指定できるやつなかったので作って、Terraform Registryに公開した🎉https://t.co/jJxfruBDQ6
— nari@BOOTHで好評発売中「GoとAWS CDKで作る本格SlackBot入門」 (@fukubaka0825) October 22, 2019
後はついでに、Terraform入門資料(v0.12.0対応) ~基本知識から設計や運用、知っておくべきtipsまで~ - Qiitaでは詳しく書けなかったlambdaリソースのアプリとインフラのデプロイの疎結合化についてもSampleを紹介しています。
対象
- AWS Lambdaを使ってWeb API立ててみたいなと思っていた方
- AWS Lambdaリソースのアプリとインフラのデプロイの疎結合化のサンプルに興味がある方
前提
- 標準のnet/httpを用いたルーティングでも可能ですが、今回はフレームワークとしてechoを使用しています
- Lambda proxy integrationとは? -> API Gateway の Lambda プロキシ統合をセットアップする - Amazon API Gateway
- 今回作成したterraformのmoduleはこちら
開発環境
- Terraform v0.12.6
- Go v1.12.4
システム全体像
リポジトリ構成
├── Makefile
├── README.md
├── go.mod
├── go.sum
├── main.tf
├── modules
│ ├── iam_role
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ └── valiables.tf
│ └── sg
│ ├── main.tf
│ ├── outputs.tf
│ └── valiables.tf
├── outputs.tf
└── source
└── main.go
また、サンプルコードは、公開したmoduleのexampleとして公開しています
terraform-aws-lambda-proxy-integration/go1.x_examples at master · fukubaka0825/terraform-aws-lambda-proxy-integration · GitHub
Terraformのコード紹介
module "sample_api"{
source = "fukubaka0825/lambda-proxy-integration/aws" //❶
version = "1.0.2"
apigw_name = "sample-api"
function_name = "sample-api"
lambda_role = module.lambda_role.iam_role_arn
// The repo's name and object key where source code artifact exists
s3_bucket = data.terraform_remote_state.storage.outputs.s3_serverless_app_bucket.name
s3_key = "lambda/sample.zip"
// If you want to use vpc lambda,required
security_group_ids = [module.http_sg.security_group_id] //❷
subnet_ids = module.vpc.app_subnets
}
❶ 今回API proxy integration+S3のartifactをlambdaのソースとして指定できるmoduleを作成したので、そちらを指定しています(Terraform Registry)
❷ security_group_idsとsubnet_idsは、VPC lambaのモードならば指定します。
Goのコード紹介
package main
import (
"github.com/labstack/echo"
"log"
"context"
"net/http"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
echolamda"github.com/awslabs/aws-lambda-go-api-proxy/echo"
)
var echoLambda *echolamda.EchoLambda //❶
func init() {
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("Echo cold start")
e := echo.New() //❷
//ここでルーティング
e.GET("/ping", func(c echo.Context) error{
return c.NoContent(http.StatusNoContent)
})
echoLambda = echolamda.New(e)
}
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// If no name is provided in the HTTP request body, throw an error
// 中でServeHTTPを叩いている
return echoLambda.ProxyWithContext(ctx, req) //❸
}
func main() {
lambda.Start(Handler)
}
❶ コールドスタートの関係上、globalにechoLambdaを宣言するのがセオリーっぽいです。
❷ initでroutingを設定します。ここではechoを使っていますが、もちろん自由なフレームワークなりnet/httpなりで設定可能です。
❸ 本来ならば、proxyrequestからhttp.requstを取り出して、ServeHTTPメソッドで${path}に対応する関数を発火し、http.responseをproxyresponseに変換して返す
処理を自前で書く必要がありますが、GitHub - awslabs/aws-lambda-go-api-proxy: lambda-go-api-proxy makes it easy to port APIs written with Go frameworks such as Gin (https://gin-gonic.github.io/gin/ ) to AWS Lambda and Amazon API Gateway.という素晴らしいawslabsが提供しているmoduleで同じようなことをやってくれるものがあるのでそれを利用しています。(なんとgin,echo,net/httpなど主要なパッケージには対応してくれている、最高)
lambdaリソースのアプリとインフラのデプロイを疎結合にする運用
Terraform入門資料(v0.12.0対応) ~基本知識から設計や運用、知っておくべきtipsまで~ - Qiitaの3.4 lambdaリソースを扱う場合のインフラとアプリの密結合問題でも扱いましたが、Terraformのみでデプロイを完結させるとインフラとアプリが密結合になってしまいます。その解消法の具体例の話をここではしようと思います。
Makefileを作成する
LAMDA_SOURRE_REPO=serverless-app
LAMDA_SOURRE_KEY=lambda/sample.zip
LAMDA_SOURCE_PATH=s3://${LAMDA_SOURRE_REPO}/${LAMDA_SOURRE_KEY}
deploy_dummy_code: //❶
aws s3 cp .build/dummy.zip ${LAMDA_SOURCE_PATH}
build_update: //❷
GOOS=linux GOARCH=amd64 go build -o .build/main source/main.go
cd .build && zip -r sample.zip ./
aws s3 cp .build/sample.zip ${LAMDA_SOURCE_PATH}
aws lambda update-function-code --function-name sample-search-api --s3-bucket ${LAMDA_SOURRE_REPO} --s3-key ${LAMDA_SOURRE_KEY}
echo 'Success to lambda update!'
❶ dummyのzipをLAMDA_SOURCE_PATH
にあげる処理をdeploy_dummy_code
コマンドに仕込んでいます。
❷ 本当のlambdaソースのコードを、buildしてzipにし、LAMDA_SOURCE_PATH
にあげて、aws lambda update-function-code
コマンドでlambdaのソースをアップデートする処理をbuild_update
コマンドに仕込んでいます。
Makefileを用いたデプロイ運用
// Put dummy code into S3 repo.
make deploy_dummy_code //❶
// deploy infra
terraform init //❷
terraform plan
terraform apply
// deploy app(lambda)
make build_update //❸
❶ まずdeploy_dummy_code
コマンドで、S3にdummyのコードをあげます。
❷ ❶のコードを参照させる形で、lambdaのインフラ部分をデプロイします。
❸ 最後に、build_update
で本当のlambdaのアプリ部分をデプロイします。
終わりに
VPC lambdaはコールドスタートの改善によって、レスポンスの要件が非常にシビアでないWeb APIの構築ツールとして、かなり魅了的なものになってきたと思います。
データソースとのつなぎこみは、RDSのようにスケールしづらく、コネクション制限があるものとのやりとりはまだまだ使い勝手が悪い(internalなAPI経由にすれば解決しますが)ですが、スケールしやすい分散型のデータソース(DynamoDB、ElasticSearch,redis)とのやりとりなら全然使っていけそうだなという印象です。
また、この記事で使ったmoduleは、初めてPublic ModuleをTerraform Registry公開してみましたが、これからもどんどん公開できるものは公開していきたいと思います。