🐶

Go EchoサーバーをLambda Web Adapter + SAM + Lambda Function URLsで公開する

2024/08/24に公開

背景

Goで作ったWebアプリケーションをLambdaで動かす方法を調べていた際にAWS Lambda Web Adapterという技術の存在を知りました。

https://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter/

AWS Lambda Web Adapterを使えば様々なWebアプリケーション・フレームワークで構築されたwebアプリをLambdaで動かすことができます。(Express.js、Flask、Laravel、Next.js、...etc.)

https://github.com/awslabs/aws-lambda-web-adapter?tab=readme-ov-file#examples

公式のExamplesに載っていないRuby on RailsなどのWebアプリケーション・フレームワークもLambdaで動かせると推測できます。

Runtime埋め込み系のAdapterというのもいくつか存在するようですが、それを使うデメリットとLambda Web Adapterを使う場合のメリットはこちらのスライドがとても参考になりました。
https://speakerdeck.com/_kensh/web-frameworks-on-lambda?slide=29
https://speakerdeck.com/_kensh/web-frameworks-on-lambda?slide=67

今回は、GoのWebアプリケーション・フレームワークであるEchoで作った簡易的なサーバーアプリをAWS Lambda Web AdapterとSAM(serverless-application-model)を使ってLambda Function URLs (関数URL)で公開してみました。

私が作成したコードはGitHubでテンプレートリポジトリとして公開しています。

https://github.com/ryichk/go-echo-lambda-function-url-template

自分が調査した際はAWS Lambda Web Adapterの公式ExamplesでGinを使ったサンプルやZennの記事でFiberを使ったサンプルを見つけることはできましたが、Echoを使ったサンプルを見つけることができなかったのでこうして記事として公開することにしました。

https://github.com/awslabs/aws-lambda-web-adapter/tree/main/examples/gin

https://zenn.dev/the_exile/articles/75269a81f49f62

手順・解説

1. Echoサーバーの構築

EchoでWebアプリケーションを構築します。
今回は簡単にlocalhost:8000/へアクセスしたらHello!と表示されるだけのアプリを構築しました。

package main

import (
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

func main() {
	e := echo.New()
	e.GET("/", hello)
	e.Logger.Fatal(e.Start(":8000"))
}

func hello(c echo.Context) error {
	return c.String(http.StatusOK, "Hello!")
}

2. Dockerfileを作成

DockerfileはLambda Web Adapterの公式Examplesで公開されているginのDockerfileを参考にしました。

FROM golang:1.23-alpine AS build_base
RUN apk add --no-cache git
WORKDIR /app

COPY . .
RUN go mod download

RUN GOOS=linux CGO_ENABLED=0 go build -o bootstrap ./app
FROM alpine:3.9
RUN apk add ca-certificates

# Lambda Web AdapterをDockerイメージで使用するために必要なのはこの1行を追加するだけです
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.8.4 /lambda-adapter /opt/extensions/lambda-adapter
COPY --from=build_base /app/bootstrap /app/bootstrap

ENV PORT=8000 AWS_LWA_ASYNC_INIT=true
EXPOSE 8000

CMD ["/app/bootstrap"]

Lambda Web Adapterはデフォルトで8080番ポートをWebアプリがリッスンしていると想定しています。
そのため、8080番ポート以外を使用したい場合は環境変数PORTAWS_LWA_PORTで任意のポート番号を指定する必要があります。

しかし、Lambda実行環境でWebアプリは非rootユーザーとして実行されるため1024以下のポート番号をリッスンすることはできません。
また、3000番と9001番もポートとして利用することは避けた方が良さそうです。
3000番は、CloudWatch Lambda Insight extensionが使用し、9001番はLambda Runtime APIが使用するためです。

参考:https://github.com/awslabs/aws-lambda-web-adapter?tab=readme-ov-file#configurations:~:text=AWS_LWA_PORT / PORT - Lambda,uses port 3000.

以下の環境変数はなくても問題ありません。

AWS_LWA_ASYNC_INIT=true

Lambda関数が10秒以内に初期化を完了できなかった場合、Lambdaは関数を再起動し、初期化の料金を請求します。
Lambda Web Adapterは、この10秒間の初期化時間を有効に使って初期化の再起動を回避するために非同期初期化をサポートしています。
この機能を有効にすると、最大9.8秒の準備完了チェックを行い、それまでにWebアプリの準備が完了しなかったらLambdaサービスに初期化が完了したことを通知し、ハンドラ内で準備完了チェックを継続します。
この機能はデフォルトで無効になっていますが、環境変数AWS_LWA_ASYNC_INITまたはASYNC_INITをtrueに設定することで有効にできます。

参考:https://github.com/awslabs/aws-lambda-web-adapter?tab=readme-ov-file#configurations:~:text=AWS_LWA_ASYNC_INIT / ASYNC_INIT - Lambda,to true.

3. SAMのtemplate.yamlを作成

Lambda Function URLsを使うにあたって、Lambda Web Adapterの公式Examplesで公開されているnextjs-response-streamingのtemplate.yamlを参考にしました。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for Go's Echo Web application.

Globals:
  Function:
    Timeout: 5
    MemorySize: 128

Resources:
  GoEchoAppFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: app/
      PackageType: Image
      Architectures:
      - x86_64
      FunctionUrlConfig:
        AuthType: NONE
    Metadata:
      DockerTag: v1
      DockerContext: .
      Dockerfile: Dockerfile

Outputs:
  GoEchoAppFunctionOutput:
    Description: "Go's Echo App Function ARN"
    Value: !GetAtt GoEchoAppFunction.Arn
  GoEchoAppFunctionUrlOutput:
    Description: "Go's Echo App Function URL"
    Value: !GetAtt GoEchoAppFunctionUrl.FunctionUrl

FunctionUrlConfigのAuthTypeをNONEに設定しているため、誰でもアクセス可能なURLとして公開されます。

5. SAM CLIでビルド

$ sam build
Building codeuri: /Pathto/go-echo-lambda-function-url-template runtime: None architecture: x86_64 functions: GoEchoAppFunction
Building image for GoEchoAppFunction function
Setting DockerBuildArgs for GoEchoAppFunction function
Step 1/13 : FROM golang:1.23-alpine AS build_base

~ 省略 ~

Successfully tagged goechoappfunction:v1


Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

6. SAM CLIでデプロイ

$ sam deploy

~ 省略 ~

Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y

2024-08-24 21:45:24 - Waiting for stack create/update to complete

CloudFormation events from stack operations (refresh every 5.0 seconds)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus                                                 ResourceType                                                   LogicalResourceId                                              ResourceStatusReason
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
UPDATE_IN_PROGRESS                                             AWS::CloudFormation::Stack                                     go-echo-app                                                    User Initiated
UPDATE_IN_PROGRESS                                             AWS::Lambda::Function                                          GoEchoAppFunction                                              -
UPDATE_COMPLETE                                                AWS::Lambda::Function                                          GoEchoAppFunction                                              -
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS                            AWS::CloudFormation::Stack                                     go-echo-app                                                    -
UPDATE_COMPLETE                                                AWS::CloudFormation::Stack                                     go-echo-app                                                    -
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

CloudFormation outputs from deployed stack
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Key                 GoEchoAppFunctionUrlOutput
Description         Go's Echo App Function URL
Value               https://xxx.lambda-url.ap-northeast-1.on.aws/

Key                 GoEchoAppFunctionOutput
Description         Go's Echo App Function ARN
Value               arn:aws:lambda:ap-northeast-1:000000000000:function:go-echo-app-GoEchoAppFunction-XXXXXXXX
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Successfully created/updated stack - go-echo-app in ap-northeast-1

7. 動作確認

$ curl https://xxx.lambda-url.ap-northeast-1.on.aws/
Hello!%

8. リソースの削除

$ sam delete

その他参考にした資料

https://aws.amazon.com/jp/builders-flash/202402/lambda-container-runtime/
https://echo.labstack.com/docs/quick-start

Discussion