はじめに
フューチャーAdvent Calendar 2022 の2日目です。
この記事は今年2022年4月にリリースされた「Lambda Function URLs」を実務で利用したところ非常に簡単だったので、そのやり方を紹介する記事です。
SAM初心者の方向けの内容になります。
こんにちは、Future CSIG所属の二瓶賢です。
今年からFutureVulsという脆弱性管理サービスの開発チームで働いています。
さて、FutureVulsでは決済サービスにStripeや、メール送信サービスにSendgridといったwebサービスを利用しています。
そういったサービスではwebhookで異常を通知するケースが多いかと思いますが、それらwebhook受信用エンドポイントを都度用意するのが面倒という気持ちもあるかと思います。(私はそうです、特にサービスの検証段階だと猶更です。)
そこで、この記事ではAWS Lambda Function URLs とSAMを利用し、(API Gatewayを構築せず、)シンプルで簡単に、エンドポイントを構築する流れをご紹介します。
手順
SAM インストール
まずはAWS SAM CLIをインストールします
https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
プロジェクト作成
下記コマンドを使うと良しなにテンプレートを作ってくれます。
sam init
全出力
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]: N
Which runtime would you like to use?
1 - dotnet6
2 - dotnet5.0
3 - dotnetcore3.1
4 - go1.x
5 - graalvm.java11 (provided.al2)
6 - graalvm.java17 (provided.al2)
7 - java11
8 - java8.al2
9 - java8
10 - nodejs16.x
11 - nodejs14.x
12 - nodejs12.x
13 - python3.9
14 - python3.8
15 - python3.7
16 - ruby2.7
17 - rust (provided.al2)
Runtime: 4
What package type would you like to use?
1 - Zip
2 - Image
Package type: 1
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]: y
X-Ray will incur an additional cost. View https://aws.amazon.com/xray/pricing/ for more details
Project name [sam-app]: sample-sam
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)
-----------------------
Generating application:
-----------------------
Name: sample-sam
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 ./sample-sam/README.md
Commands you can use next
=========================
[*] Create pipeline: cd sample-sam && sam pipeline init --bootstrap
[*] Validate SAM template: cd sample-sam && sam validate
[*] Test Function in the Cloud: cd sample-sam && sam sync --stack-name {stack-name} --watch
SAM CLI update available (1.65.0); (1.60.0 installed)
To download: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
作成されたディレクトリにはtemplate.yaml
が作成されます。
これを修正し、(APIGatewayを使用せず)FunctionURLsのみを使う最小構成はこんな感じです。
デフォルトから変更している箇所にはコメントを入れてます。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-sample
Sample SAM Template for sam-sample
Globals:
Function:
Timeout: 5
Tracing: Active
Api:
TracingEnabled: True
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: hello-world
Runtime: go1.x
- 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
Architectures:
- x86_64
+ FunctionUrlConfig:
+ AuthType: NONE
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
- 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
+ HelloWorldFunctionUrl:
+ Description: "Function URLs endpoint"
+ Value: !GetAtt HelloWorldFunctionUrl.FunctionUrl
変更ポイントは下記です
-
AWS::Serverless::Function
のProperties
からEvents
を消す(これがあるとAPIGatewayが暗黙的に作成されてしまう) -
AWS::Serverless::Function
のProperties
にFunctionUrlConfig
を足す(認証にIAMリクエストを要求するか否かを選択できます 詳細) -
Outputs
からHelloWorldAPIを消す -
Outputs
にFunctionUrlを足す(これがFunctionURLsのエンドポイントとなります)
※ goで実装する場合
goで実装する場合、デフォルトで生成されたソースにビジネスロジックを追記することかと思います。
デフォルトで生成されるソースではAPIGateway経由のリクエスト/レスポンスを想定されているので修正します。
具体的にはLambdaFunctionURLRequestとLambdaFunctionURLResponseを使って下記のように変更します。
実装例
package main
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
var (
// DefaultHTTPGetAddress Default Address
DefaultHTTPGetAddress = "https://checkip.amazonaws.com"
// ErrNoIP No IP found in response
ErrNoIP = errors.New("No IP in HTTP response")
// ErrNon200Response non 200 status code in response
ErrNon200Response = errors.New("Non 200 Response found")
)
- func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
+ func handler(request events.LambdaFunctionURLRequest) (events.LambdaFunctionURLResponse, error) {
resp, err := http.Get(DefaultHTTPGetAddress)
if err != nil {
return events.LambdaFunctionURLResponse{}, err
}
if resp.StatusCode != 200 {
return events.LambdaFunctionURLResponse{}, ErrNon200Response
}
ip, err := ioutil.ReadAll(resp.Body)
if err != nil {
return events.LambdaFunctionURLResponse{}, err
}
if len(ip) == 0 {
return events.LambdaFunctionURLResponse{}, ErrNoIP
}
return events.LambdaFunctionURLResponse{
Body: fmt.Sprintf("Hello, %v", string(ip)),
StatusCode: 200,
}, nil
}
func main() {
lambda.Start(handler)
}
ビルド
sam build
ローカルテスト
ローカルでLambdaを起動し、動作確認をします。
sam local start-api
デプロイ
sam deploy
これで終わりです。本当にシンプル。
ロジックに修正・機能追加が入った場合も同様にビルド・デプロイするだけです。この時、FunctionURLsのエンドポイントは変わりません。
終わりに
SAMを利用するのは初めてだったのですが、簡単・シンプルにデプロイできたためかなり便利でした。
この記事が誰かの役に立てば幸いです。