Lambda Web Adapter + SAMで既存のGo Fiberアプリをサーバーレス化する
背景
既存のGoアプリをAWS Lambda上で実行するためには、
aws-lambda-go、aws-lambda-go-api-proxyなどのライブラリーを使用しなければならず、
それに伴い、アプリケーションコードの侵入性が高く、移行するためには不便な状況がありました。
そこで、AWS Labsが提供するAWS Lambda Web Adapterを使用することで、既存のGoアプリを簡単にサーバーレス化することができるということです。
AWS Lambda Web Adapterは、HTTPリクエストをAWS Lambdaのイベントとして処理することで、既存のGoアプリをAWS Lambda上で実行するために必要なコードの侵入性を最小限に抑え、移行するために必要な変更量を減らします。
これにより、既存のGoアプリをサーバーレスアーキテクチャに移行するために不便なことがなくなり、AWS Lambda上でのスケーリングや管理の問題を解決することができます。
この記事では、AWS Lambda Web AdapterとSAMを使用して既存のGo Fiberアプリケーションをサーバーレスアーキテクチャに移行し、SAMでデプロイする方法を紹介します。
既存のGoアプリをサーバーレス化するための手順
1. SAMテンプレートの作成
次に、AWS Lambda Web Adapterを使用するためのSAMテンプレートを作成します。
長くなるので、アコーディオンで隠しています。
下記の順でSAM+Goのテンプレートを作成します。
-> sam init --name sam-app --runtime go1.x
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Infrastructure event management
3 - Multi-step workflow
Template: 1
Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.
Based on your selections, the only dependency manager available is mod.
We will proceed copying the template using mod.
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]:
Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]:
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)
-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: go1.x
Architectures: x86_64
Dependency Manager: mod
Application Template: hello-world
Output Directory: .
Next steps can be found in the README file at ./sam-app/README.md
Commands you can use next
=========================
[*] Create pipeline: cd sam-app && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-app && sam validate
[*] Test Function in the Cloud: cd sam-app && sam sync --stack-name {stack-name} --watch
at …/lambda-web-adapter-example ( ) on 🅰 (jp) underwent 35s
sam init --name sam-app
You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Multi-step workflow
3 - Serverless API
4 - Scheduled task
5 - Standalone function
6 - Data processing
7 - Infrastructure event management
8 - Serverless Connector Hello World Example
9 - Multi-step workflow with Connectors
10 - Lambda EFS example
11 - Machine Learning
Template: 1
Use the most popular runtime and package type? (Python and zip) [y/N]:
Which runtime would you like to use?
1 - aot.dotnet7 (provided.al2)
2 - dotnet6
3 - dotnet5.0
4 - dotnetcore3.1
5 - go1.x
6 - go (provided.al2)
7 - graalvm.java11 (provided.al2)
8 - graalvm.java17 (provided.al2)
9 - java11
10 - java8.al2
11 - java8
12 - nodejs18.x
13 - nodejs16.x
14 - nodejs14.x
15 - nodejs12.x
16 - python3.9
17 - python3.8
18 - python3.7
19 - ruby2.7
20 - rust (provided.al2)
Runtime: 5
What package type would you like to use?
1 - Zip
2 - Image
Package type: 2
Based on your selections, the only dependency manager available is mod.
We will proceed copying the template using mod.
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]:
Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]:
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)
-----------------------
Generating application:
-----------------------
Name: sam-app
Base Image: amazon/go1.x-base
Architectures: x86_64
Dependency Manager: mod
Output Directory: .
Next steps can be found in the README file at ./sam-app/README.md
Commands you can use next
=========================
[*] Create pipeline: cd sam-app && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-app && sam validate
[*] Test Function in the Cloud: cd sam-app && sam sync --stack-name {stack-name} --watch
2. アプリケーションのセットアップ
次に、 sam-app/hello-world/main.go
を作成したアプリケーションにそのまま置き換えます。
今回使用するサンプルアプリケーションこちらです。
GitHub: https://github.com/the-exile-110/lambda-web-adapter-example
package main
import (
"github.com/gofiber/fiber/v2"
"net/http"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.Status(http.StatusOK).JSON(fiber.Map{
"Message": "Hello, World 👋!",
})
})
app.Listen(":3000")
}
3. template.yamlの編集
今回はAPI Gatewayを使わず、Lambda Function URLを利用するため、 template.yaml
を編集します。
M1 Macのため、Architecturesをarm64
に変更しています。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 5
MemorySize: 128
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
PackageType: Image
Architectures:
- - x86_64
+ - arm64
- Events:
- CatchAll:
- Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
- Properties:
- Path: /hello
- Method: GET
Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
Variables:
PARAM1: VALUE
Metadata:
DockerTag: go1.x-v1
DockerContext: ./hello-world
Dockerfile: Dockerfile
+ LambdaFunctionUrl:
+ Type: AWS::Lambda::Url
+ Properties:
+ TargetFunctionArn: !GetAtt HelloWorldFunction.Arn
+ AuthType: NONE
+ Cors:
+ AllowMethods: [ 'GET' ]
+ AllowOrigins: [ '*' ]
+ AllowHeaders: [ 'Content-Type', 'Authorization', 'X-Amz-Date', 'X-Api-Key', 'X-Amz-Security-Token', 'X-Amz-User-Agent' ]
Outputs:
- HelloWorldAPI:
- Description: "API Gateway endpoint URL for Prod environment for First Function"
- Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
- HelloWorldFunction:
- Description: "First Lambda Function ARN"
- Value: !GetAtt HelloWorldFunction.Arn
- HelloWorldFunctionIamRole:
- Description: "Implicit IAM Role created for Hello World function"
- Value: !GetAtt HelloWorldFunctionRole.Arn
+ Endpoint:
+ Description: URL for function
+ Value: !GetAtt LambdaFunctionUrl.FunctionUrl
4. Dockerfileの編集
次に、aws-lambda-adapter
を使うようにhello-world/Dockerfile
下記のDockerfileに変えます。
Dockerfileの書き方はそれぞれですが、
重要なのはlambda-adapter
のコピーとポート公開の部分です。
FROM arm64v8/golang:1.19.1-bullseye as build-env
WORKDIR /go/src/app
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o /go/bin/app
FROM gcr.io/distroless/static:latest-arm64
COPY /go/bin/app /
# ここが重要
COPY /lambda-adapter /opt/extensions/lambda-adapter
# ここが重要
ENV PORT=3000
EXPOSE 3000
CMD ["/app"]
5. SAM CLIでビルド
続いて、 sam build
でビルドします。
実行結果
-> sam build
Building image for HelloWorldFunction function
Setting DockerBuildArgs: {} for HelloWorldFunction function
Step 1/8 : FROM arm64v8/golang:1.19.1-bullseye as build-image
---> c5e98f0fcc5e
Step 2/8 : WORKDIR /go/src
---> Running in 4ff29f303df8
Removing intermediate container 4ff29f303df8
---> e989a42012df
Step 3/8 : COPY go.mod go.sum main.go ./
---> c6feca10f658
Step 4/8 : RUN go build -o ../bin
---> Running in 1d6af16cb47c
go: downloading github.com/gofiber/fiber/v2 v2.41.0
go: downloading github.com/mattn/go-colorable v0.1.13
go: downloading github.com/mattn/go-isatty v0.0.17
go: downloading github.com/mattn/go-runewidth v0.0.14
go: downloading github.com/valyala/bytebufferpool v1.0.0
go: downloading github.com/valyala/fasthttp v1.43.0
go: downloading golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
go: downloading github.com/rivo/uniseg v0.2.0
go: downloading github.com/valyala/tcplisten v1.0.0
go: downloading github.com/andybalholm/brotli v1.0.4
go: downloading github.com/klauspost/compress v1.15.9
Removing intermediate container 1d6af16cb47c
---> 52564d727bae
Step 5/8 : FROM public.ecr.aws/lambda/go:1
1: Pulling from lambda/go
796e5774e352: Pull complete
1a89c28fd085: Pull complete
64543d447bd8: Pull complete
072730bdc14d: Pull complete
fd7ea672a291: Pull complete
WARNING: Pulled image with specified platform (linux/arm64), but the resulting image's configured platform (linux/amd64) does not match.
This is most likely caused by a bug in the build system that created the fetched image (public.ecr.aws/lambda/go:1).
Please notify the image author to correct the configuration. ---> f5017fea382b
Step 6/8 : COPY --from=build-image /go/bin/ /var/task/
---> 4c9083ccf4b8
Step 7/8 : COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.6.0 /lambda-adapter /opt/extensions/lambda-adapter
---> da80197cb166
Step 8/8 : CMD ["hello-world"]
---> [Warning] The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
---> Running in 1a2ad95d2950
Removing intermediate container 1a2ad95d2950
---> e08321a2014c
Successfully built e08321a2014c
Successfully tagged helloworldfunction:go1.x-v1
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
6. SAM CLIでデプロイ
最後に、 sam deploy --guided
でデプロイします。
基本的提示メッセージに従って進めば、問題なくデプロイできます。
7. 動作確認
デプロイが完了したら、アウトプットに表示されたURLにアクセスしてみます。
-> curl https://xxx.lambda-url.ap-northeast-1.on.aws/
{"Message":"Hello , World 👋!"}
8. リソースの削除
sam delete
で作ったリソースを削除します。
まとめ
今回は、Lambda Web Adapter + SAMで既存のGo Fiberアプリをサーバーレス化する方法を紹介しました。
Lambda Web Adapterを使って、Go FiberのようなGoのWebフレームワークをそのまま使えるので、 既存のGoアプリをサーバーレス化するのに便利です。
Discussion