AWS LambdaでGolangのWebフレームワークGinを利用してみた
最近は体調管理に一番困っているやっさんでございます。
頭痛や肩こり、そして腰痛。
年齢が上がると人には話しにくい悩みが増えていきますね。
さて、今回はAWS Lambdaで
GolangのWebフレームワークGinを利用してみました。
Ginとは
GinはGo(Golang)で記述されたWebフレームワークです。
パフォーマンスと優れた生産性が必要な場合は、Ginが気に入るはずです。
AWS Lambda × Ginのメリット
AWS LambdaでGinを利用することのメリットを考えてみます。
再利用性と可搬性の向上
Ginを利用することで、再利用性と可搬性が向上します。
GinはWebフレームワークですので、サーバーでも動かすことができます。
少しの修正でAWS Lamdaからコンテナアーキテクチャに変更することも可能になります。
フレームワークの恩恵を享受できる
GolangではAWS Lambdaに特化したフレームワークはありません。
他の言語であれば、以下のようなフレームワークが存在します。
- Ruby:Ruby on Jets
- Python:Chalice
「LambdaでSpring Bootを使えるか」という話を勉強会で耳にしました。
コミュニティから信頼されているフレームワークをLambdaで利用できれば、
生産性も上がり、コミュニティから多くの技術的知見も得られる ...
このような話が出るのは当然です。
(LambdaでSpring Bootを使うのが本番ワークロードに適しているかは、別の話ですが。)
Ginも人気のあるWebフレームワークで、スター数は30,000を超えています。
LambdaでWebフレームワークに必要な機能をフルスクラッチで作るより、
Ginなどの有名なフレームワークを利用したほうが生産性は上がるでしょう。
このようなアプローチは他にもあります。
- LambdaでExpress(Node.js)を利用する
- LambdaでFlask(Python)を利用する
AWS LambdaでGinを利用してみる
以下の前提条件で構築しました。
- Golang : 1.14.3
- Serverless Framework : 1.67.1
ディレクトリ構成
今回はServerless Frameworkを利用します。
- src
- hanlder
- main.go
- Makefile
- serverless.yml
main.goの内容
awslabsが提供しているaws-lambda-go-api-proxyを利用して、
Lambdaのリクエスト情報をGinの機能に受け渡します。
package main
import (
"log"
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/awslabs/aws-lambda-go-api-proxy/gin"
"github.com/gin-gonic/gin"
)
var ginLambda *ginadapter.GinLambda
func init() {
// stdout and stderr are sent to AWS CloudWatch Logs
log.Printf("Gin cold start")
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
ginLambda = ginadapter.New(r)
}
func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// If no name is provided in the HTTP request body, throw an error
return ginLambda.ProxyWithContext(ctx, req)
}
func main() {
lambda.Start(Handler)
}
serverless.ymlの内容
記述内容にGin特有の変更はありません。
普段通り記述します。
service: aws-lambda-go-api-proxy-gin
provider:
name: aws
runtime: go1.x
stage: ${opt:stage, self:custom.defaultStage}
region: ap-northeast-1
iamRoleStatements:
- Effect: "Allow"
Action:
- "logs:*"
Resource: "*"
package:
exclude:
- ./**
include:
- ./bin/**
custom:
defaultStage: dev
functions:
api:
handler: bin/main
timeout: 900
events:
- http:
path: ping
method: get
デプロイ
以下のコマンドをMakefileに用意しました。
build:
go mod download
env GOOS=linux go build -ldflags="-s -w" -o bin/main src/handler/main.go
deploy: build
sls deploy --verbose --aws-profile $(PROFILE)
実行してみる
実行できました!
パフォーマンスを測ってみる
Heyというツールを使って測ってみました。
コールドスタートで0.4秒ならよさそうです!
Summary:
Total: 0.5447 secs
Slowest: 0.4115 secs
Fastest: 0.0246 secs
Average: 0.1200 secs
Requests/sec: 367.1871
Total data: 3600 bytes
Size/request: 18 bytes
Response time histogram:
0.025 [1] |
0.063 [145] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.102 [1] |
0.141 [2] |■
0.179 [2] |■
0.218 [0] |
0.257 [0] |
0.295 [1] |
0.334 [8] |■■
0.373 [32] |■■■■■■■■■
0.411 [8] |■■
Latency distribution:
10% in 0.0328 secs
25% in 0.0381 secs
50% in 0.0442 secs
75% in 0.1740 secs
90% in 0.3583 secs
95% in 0.3691 secs
99% in 0.3913 secs
Details (average, fastest, slowest):
DNS+dialup: 0.0151 secs, 0.0246 secs, 0.4115 secs
DNS-lookup: 0.0030 secs, 0.0000 secs, 0.0124 secs
req write: 0.0000 secs, 0.0000 secs, 0.0010 secs
resp wait: 0.1048 secs, 0.0245 secs, 0.3504 secs
resp read: 0.0000 secs, 0.0000 secs, 0.0002 secs
Status code distribution:
[200] 200 responses
いとも簡単にLambdaでGinを利用できました。
ここからにさらにデータソースをDynamoDBからRDBに変更しやすい設計とすれば
より再利用性と可搬性が向上しますね。
今回試したコードは以下にあります。
x-blood / aws-lambda-go-api-proxy-gin
よろしければご覧になってみてください。
以上です!