search
LoginSignup
9

More than 3 years have passed since last update.

posted at

updated at

Organization

Lambda proxy integration×Go(echo)×Terraformでお手軽Web API(サーバレス)

はじめに

サーバレスで手軽にWeb APIを立てたい場合、API Gatewayのproxy integrationとlambdaアプリケーションソースでのルーティングでこんなに簡単にお安く構築できるんだな〜と感心してしまったのでそのご紹介と、そのリソース群のTerraform moduleを作ってTerraform Registryに公開したのでその内容の紹介で記事を書いてみました。
(しかも最近VPC Lambdaのコールドスタートが大幅に改善されたばかり -> [発表] Lambda 関数が VPC 環境で改善されます | Amazon Web Services ブログ )

後はついでに、Terraform入門資料(v0.12.0対応) ~基本知識から設計や運用、知っておくべきtipsまで~ - Qiitaでは詳しく書けなかったlambdaリソースのアプリとインフラのデプロイの疎結合化についてもSampleを紹介しています。

対象

  • AWS Lambdaを使ってWeb API立ててみたいなと思っていた方
  • AWS Lambdaリソースのアプリとインフラのデプロイの疎結合化のサンプルに興味がある方

前提

開発環境

  • Terraform v0.12.6
  • Go v1.12.4

システム全体像

lambda proxy 全体像.png

リポジトリ構成

├── 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のコード紹介

main.tf
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のコード紹介

main.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を作成する

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公開してみましたが、これからもどんどん公開できるものは公開していきたいと思います。

参考文献

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
9