AWS SAM Lambda関数だけを単体でデプロイしたいときのテンプレート設定例

2021.12.04

AWS SAM(Serverless Application Model)を利用してAPI Gateway + Lambdaなどのリソース一式をデプロイすることはよくあるかと思います。理由あって他のリソースに一切紐付かない単体のLambda関数をデプロイする機会がありましたのでテンプレートの例を紹介します。

Lambda単体が必要ならマネージメントコンソールから作成した方が早いのでは?というのは至極当然のご意見なのですが、検証の都合SAMから生み出す必要がありました。

実行環境

$ sam --version
SAM CLI, version 1.36.0

本検証で利用したコード一式は以下に置いてあります。

初期設定

sam initコマンドでHello World Exampleのサンプルテンプレートで初期化しました。

$ sam init
Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1
What package type would you like to use?
	1 - Zip (artifact is a zip uploaded to S3)
	2 - Image (artifact is an image uploaded to an ECR image repository)
Package type: 1

Which runtime would you like to use?
	1 - nodejs14.x
	2 - python3.9
	3 - ruby2.7
	4 - go1.x
	5 - java11
	6 - dotnetcore3.1
	7 - nodejs12.x
	8 - nodejs10.x
	9 - python3.8
	10 - python3.7
	11 - python3.6
	12 - python2.7
	13 - ruby2.5
	14 - java8.al2
	15 - java8
	16 - dotnetcore2.1
Runtime: 2

Project name [sam-app]: lambda-alone

Cloning from https://github.com/aws/aws-sam-cli-app-templates

AWS quick start application templates:
	1 - Hello World Example
	2 - EventBridge Hello World
	3 - EventBridge App from scratch (100+ Event Schemas)
	4 - Step Functions Sample App (Stock Trader)
	5 - Elastic File System Sample App
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: lambda-alone
    Runtime: python3.9
    Architectures: x86_64
    Dependency Manager: pip
    Application Template: hello-world
    Output Directory: .

    Next application steps can be found in the README file at ./lambda-alone/README.md


    Commands you can use next
    =========================
    [*] Create pipeline: cd lambda-alone && sam pipeline init --bootstrap
    [*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch

Lambda単体をデプロイするまで

サンプルのテンプレートをベースにテンプレートの修正と、関数を編集してAWSへデプロイしてみます。

テンプレート修正

Events:Type: APIでAPI Gatewayと連携する設定が入っています。何も紐付けたくないLambdaの場合はEvetns:以下を削除すればOKです。あとはEventsに関連したOutputがあるので忘れずに削除。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  hellow-sample

  Sample SAM Template for hellow-sample

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

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:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          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

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 stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

変更箇所

少し手直してシンプルなテンプレートにしました。

  • FunctionName: を追加しLambda関数名を任意の名前に設定
  • arm64 Graviton2を採用
  • Events: の丸ごと削除
  • CodeUriのディレクトリ名を変更
  • OutputsEvents内のAPI Gatewayに関する出力内容があっため削除

template.yaml

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
  lambda-alone

  Sample SAM Template for lambda-alone

Globals:
  Function:
    Timeout: 5

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: "lambda-alone"
      CodeUri: ./lambda-alone
      Handler: app.lambda_handler
      Runtime: python3.9
      Architectures:
        - arm64

Outputs:
  HelloWorldFunction:
    Description: "Lambda Function ARN"
    Value: !GetAtt Function.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for function"
    Value: !GetAtt FunctionRole.Arn

Memo

最小限のテンプレートを目指してOutputs:の内容は欲していなかったので全削除しました。sam deployすると以下のエラーのメッセージが表示されました。SAMのテンプレートはOutputnullになることを許さないことを知りました。

Error: Failed to create changeset for the stack: lambda-test, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: [/Outputs] 'null' values are not allowed in templates

sam local start-lambdaコマンドでローカルのエンドポイントから呼び出せるのか試してみました。

$ sam local start-lambda
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint.
2021-12-04 11:55:45  * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)
2021-12-04 11:55:57 127.0.0.1 - - [04/Dec/2021 11:55:57] "GET / HTTP/1.1" 404 -

API Gatewayも何もないのでLambdaを呼び出しようがないですよね。

$ curl http://127.0.0.1:3001/
{"Type": "LocalService", "Message": "PathNotFoundException"}⏎

sam local invokeコマンドでLambdaを呼べるか試してみます。こちらはLambdaを実行して意図したレスポンスが返ってきました。

$ sam local invoke
...snip...
REPORT RequestId: 090ff64d-7513-43b5-8594-a4150280d2af	Init Duration: 3.18 ms	Duration: 1342.88 ms	Billed Duration: 1343 msMemory Size: 128 MB	Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\": \"Welcome to ABASHIRI city.\"}"}⏎
...snip...

Build & Deploy

ビルドしてデプロイします。

$ sam build
Building codeuri: /Users/ohmura.yasutaka/aws/ohmura.yasutaka/work/sam/targetgroup-lambda/lambda-alone/lambda-alone runtime: python3.9 metadata: {} architecture: arm64 functions: ['Function']
requirements.txt file not found. Continuing the build without dependencies.
Running PythonPipBuilder:CopySource

Build Succeeded

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

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
[*] Deploy: sam deploy --guided

初回のデプロイなのでガイドに従い設定してデプロイします。

$ sam deploy --guided
...snip...
	Setting default arguments for 'sam deploy'
	=========================================
	Stack Name [sam-app]: lambda-alone
	AWS Region [ap-northeast-1]:
	#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
	Confirm changes before deploy [y/N]: y
	#SAM needs permission to be able to create roles to connect to the resources in your template
	Allow SAM CLI IAM role creation [Y/n]: y
	#Preserves the state of previously provisioned resources when an operation fails
	Disable rollback [y/N]: n
	Save arguments to configuration file [Y/n]: y
	SAM configuration file [samconfig.toml]:
	SAM configuration environment [default]:
...snip...

マネージメントコンソールから確認

単体のLambdaがデプロイされています。

リソースベースポリシーも何もありません。これだとテスト実行しか呼び出しようがないです。

テスト実行は成功しました。SAMから単体のLambdaをデプロイできることを確認できました。

おわりに

Lambda単体をSAMから生み出したかったのですが情報がなく「もしやそんな使い方しないからできなかったりする?」と不安になりました。普通はやらなさそうなので「できました」という結果を書き留めておきます。どなたかの不安解消につながれば幸いです。