AWS SAM (Serverless Application Model) の始め方
AWS SAM とは
AWS SAM は AWS 上でサーバーレスアプリケーションを構築するための IaC (Infrastructure as Code) ツールです。
テンプレート仕様と CLI から構成されます。
- テンプレート仕様
- CloudFormation テンプレートの拡張版です。
- サーバーレスな AWS リソースをより簡潔に記述できるようになっています。
- CLI
- 以下のようなコマンドが用意されています。
- sam init
- SAM アプリケーションの雛形構築
- sam build
- Lambda関数コードのビルド、SAMテンプレートをデプロイ用に変換するなど、ワークフローの後続ステップのための準備
- sam validate
- SAMテンプレートの検証
- sam deploy
- AWS環境へのデプロイ
- sam local
- ローカル環境での動作確認
環境構築
以下を準備します。
- AWS アカウントと IAM ユーザーの作成
- IAM ユーザーの認証情報(アクセスキーID・シークレットアクセスキー)を発行
- AWS CLI をインストール
- 参考: AWS CLI の最新バージョンを使用してインストールまたは更新を行う - AWS Command Line Interface
- 筆者の場合 M2 Mac で利用しようとしましたが、本記事作成時点で AWS CLI を Apple Silicon の Mac にインストールする公式の方法は無いようです。
- AWS CLI に IAM ユーザーの認証情報を設定
- SAM CLI をインストール
SAM のワークフロー
以降、チュートリアル: Hello World アプリケーションのデプロイ - AWS Serverless Application Model をベースにワークフローを確認します。
雛形からの IaC ベース作成 (sam init)
$ sam init
SAM CLI now collects telemetry to better understand customer needs.
You can OPT OUT and disable telemetry collection by setting the
environment variable SAM_CLI_TELEMETRY=0 in your shell.
Thanks for your help!
Learn More: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-telemetry.html
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 - Data processing
3 - Hello World Example with Powertools for AWS Lambda
4 - Multi-step workflow
5 - Scheduled task
6 - Standalone function
7 - Serverless API
8 - Infrastructure event management
9 - Lambda Response Streaming
10 - Serverless Connector Hello World Example
11 - Multi-step workflow with Connectors
12 - GraphQLApi Hello World Example
13 - Full Stack
14 - Lambda EFS example
15 - Hello World Example With Powertools
16 - DynamoDB Example
17 - Machine Learning
Template: 1
Lambdaのランタイムとパッケージタイプを選択
Use the most popular runtime and package type? (Python and zip) [y/N]: N
Which runtime would you like to use?
1 - aot.dotnet7 (provided.al2)
2 - dotnet6
3 - go1.x
4 - go (provided.al2)
5 - graalvm.java11 (provided.al2)
6 - graalvm.java17 (provided.al2)
7 - java17
8 - java11
9 - java8.al2
10 - java8
11 - nodejs18.x
12 - nodejs16.x
13 - nodejs14.x
14 - python3.9
15 - python3.8
16 - python3.7
17 - python3.11
18 - python3.10
19 - ruby3.2
20 - ruby2.7
21 - rust (provided.al2)
Runtime: 11
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 npm.
We will proceed copying the template using npm.
各種オプション
Select your starter template
1 - Hello World Example
2 - Hello World Example TypeScript
Template: 2
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: 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]: N
Project name [sam-app]:
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)
-----------------------
Generating application:
-----------------------
Name: sam-app
Runtime: nodejs18.x
Architectures: x86_64
Dependency Manager: npm
Application Template: hello-world-typescript
Output Directory: .
Configuration file: sam-app/samconfig.toml
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
プロジェクト構成
$ cd sam-app
$ tree
.
├── README.md
├── events
│ └── event.json
├── hello-world
│ ├── app.ts
│ ├── jest.config.ts
│ ├── package.json
│ ├── tests
│ │ └── unit
│ │ └── test-handler.test.ts
│ └── tsconfig.json
├── samconfig.toml
└── template.yaml
template.yaml
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: 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.lambdaHandler
Runtime: nodejs18.x
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
Metadata: # Manage esbuild properties
BuildMethod: esbuild
BuildProperties:
Minify: true
Target: "es2020"
Sourcemap: true
EntryPoints:
- app.ts
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
GET /hello
という API エンドポイントのバックグラウンドとして Lambda 関数が1つだけ定義されたテンプレートが出力されました。
テンプレートの検証 (sam validate)
$ sam validate
./sam-app/template.yaml is a valid SAM Template
template.yaml を適当に変更してみると、エラーとなる
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs18.x
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
- Method: get
$ sam validate
E0001 Error transforming template: Resource with id [HelloWorldFunction] is invalid. Event with id [HelloWorld] is invalid. Property 'Method' is required.
./sam-app/template.yaml:1:1
Error: Linting failed. At least one linting rule was matched to the provided template.
ビルド (sam build)
ビルド
$ sam build
Starting Build use cache
Manifest file is changed (new hash: 2188d26bd084f69dde27148437a18531) or dependency folder
(.aws-sam/deps/054fea8b-50d2-47b5-b560-b35b887b0364) is missing for (HelloWorldFunction), downloading dependencies and copying/building
source
Building codeuri: ./sam-app/hello-world runtime: nodejs18.x metadata: {'BuildMethod': 'esbuild', 'BuildProperties':
{'Minify': True, 'Target': 'es2020', 'Sourcemap': True, 'EntryPoints': ['app.ts']}} architecture: x86_64 functions: HelloWorldFunction
Running NodejsNpmEsbuildBuilder:CopySource
Running NodejsNpmEsbuildBuilder:NpmInstall
Running NodejsNpmEsbuildBuilder:EsbuildBundle
Running NodejsNpmEsbuildBuilder:CleanUp
Running NodejsNpmEsbuildBuilder:MoveDependencies
Sourcemap set without --enable-source-maps, adding --enable-source-maps to function HelloWorldFunction NODE_OPTIONS
You are using source maps, note that this comes with a performance hit! Set Sourcemap to false and remove NODE_OPTIONS: --enable-source-maps
to disable source maps.
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
$ tree .aws-sam/ -L 3
.aws-sam/
├── build
│ ├── HelloWorldFunction
│ │ ├── app.js
│ │ └── app.js.map
│ └── template.yaml
├── build.toml
├── cache
└── deps
└── 054fea8b-50d2-47b5-b560-b35b887b0364
└── node_modules
7 directories, 4 files
デプロイ (sam deploy)
デプロイ開始(sam deploy --guided)
# 初回は --guided をつけてインタラクティブに設定を行う。
$ sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Found
Reading default arguments : Success
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]:
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
HelloWorldFunction has no authentication. Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]: Y
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
デプロイを開始すると、初回はビルドコードなどを保持するS3バケットを作成する。
デプロイ準備
Looking for resources needed for deployment:
Creating the required resources...
Successfully created!
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-p9y6ngjl7raj
A different default S3 bucket can be set in samconfig.toml and auto resolution of buckets turned off by setting resolve_s3=False
Parameter "stack_name=sam-app" in [default.deploy.parameters] is defined as a global parameter [default.global.parameters].
This parameter will be only saved under [default.global.parameters] in ./sam-app/samconfig.toml.
Saved arguments to config file
Running 'sam deploy' for future deployments will use the parameters saved above.
The above parameters can be changed by modifying samconfig.toml
Learn more about samconfig.toml syntax at
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
Uploading to sam-app/4da1b77c01e1dde3cda23928fcb199a5 1324 / 1324 (100.00%)
Deploying with following values
===============================
Stack name : sam-app
Region : ap-northeast-1
Confirm changeset : True
Disable rollback : False
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-p9y6ngjl7raj
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
Initiating deployment
=====================
Uploading to sam-app/b804bea4cec12b244df6df6bf1259b20.template 1421 / 1421 (100.00%)
Waiting for changeset to be created..
デプロイ準備が完了すると、CloudFormation スタックの変更確認を行い、問題なければスタックの作成/更新を行う。
変更確認とデプロイ実行
CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
-----------------------------------------------------------------------------------------------------------------------------------------
+ Add HelloWorldFunctionHelloWorldPerm AWS::Lambda::Permission N/A
issionProd
+ Add HelloWorldFunctionRole AWS::IAM::Role N/A
+ Add HelloWorldFunction AWS::Lambda::Function N/A
+ Add ServerlessRestApiDeployment47fc2 AWS::ApiGateway::Deployment N/A
d5f9d
+ Add ServerlessRestApiProdStage AWS::ApiGateway::Stage N/A
+ Add ServerlessRestApi AWS::ApiGateway::RestApi N/A
-----------------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:{アカウントID}:changeSet/samcli-deploy1692002833/bb9b4198-fcd7-4a45-ac01-db1755c4eeab
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
2023-08-14 17:49:33 - Waiting for stack create/update to complete
SAM テンプレートの記述としては Lambda 関数についての定義がほとんどでしたが、API用のリソースや Role, Lambda Permission など Lambda 関数に付随して必要なリソースも自動で作成されているのが分かります。
この記事の冒頭で、サーバーレスな AWS リソースをより簡潔に記述できる、と説明したのはこのことを指していました。
デプロイした API のデフォルトエンドポイントにアクセスしてみると、ちゃんとレスポンスが返ってきます。
$ curl "https://{APIのID}.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/"
{"message":"hello world"}
ローカル環境で動作確認 (sam local)
sam local invoke
ローカルのDocker環境でLambda 関数を起動し、1回だけ呼び出します。
$ sam local invoke HelloWorldFunction
Error: Running AWS SAM projects locally requires Docker. Have you got it installed and running?
$ sam local invoke HelloWorldFunction
Invoking app.lambdaHandler (nodejs18.x)
Local image was not found.
Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs18.x
Building image.....................................................................................................................................................................................................................................................................................................................................
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.
Mounting ./sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
START RequestId: 387371be-324a-443d-995c-a19c1dd9ff92 Version: $LATEST
END RequestId: 387371be-324a-443d-995c-a19c1dd9ff92
REPORT RequestId: 387371be-324a-443d-995c-a19c1dd9ff92 Init Duration: 0.82 ms Duration: 965.25 ms Billed Duration: 966 ms Memory Size: 128 MB Max Memory Used: 128 MB
{"statusCode": 200, "body": "{\"message\":\"hello world\"}"}%
sam local start-lambda
ローカルの Docker 環境で Lambda 関数をエミュレートするエンドポイントを起動します。
このローカルエンドポイントに CLI や SDK で接続することによって、
ローカル環境で Lambda 関数の動作を確認することができます。
$ sam local start-lambda
Initializing the lambda functions containers.
Local image is up-to-date
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.
Mounting ./sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
Containers Initialization is done.
Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint.
2023-08-15 01:31:52 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:3001
2023-08-15 01:31:52 Press CTRL+C to quit
$ aws lambda invoke --function-name "HelloWorldFunction" --endpoint-url "http://127.0.0.1:3001" --no-verify-ssl out.txt
{
"StatusCode": 200
}
$ cat out.txt
{"statusCode": 200, "body": "{\"message\":\"hello world !!!\"}"}%
sam local start-api
ローカルの Docker 環境で Lambda 関数 + API Gateway をエミュレートするエンドポイントを起動します。
このローカルエンドポイントによって HTTP サーバーホストが提供されるため、
HTTP クライアントによって、ローカル環境で API の動作を確認することができます。
$ sam local start-api
Initializing the lambda functions containers.
Local image is up-to-date
Using local image: public.ecr.aws/lambda/nodejs:18-rapid-x86_64.
Mounting ./sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
Containers Initialization is done.
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your
functions, changes will be reflected instantly/automatically. If you used sam build before running local commands, you will need to re-run
sam build for the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template
2023-08-15 01:42:56 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:3000
2023-08-15 01:42:56 Press CTRL+C to quit
$ curl http://127.0.0.1:3000/hello
{"message":"hello world"}
サーバーレスアプリケーションを削除
SAM でデプロイした環境が不要になったら削除できます。
$ sam delete
Are you sure you want to delete the stack sam-app in the region ap-northeast-1 ? [y/N]: y
Are you sure you want to delete the folder sam-app in S3 which contains the artifacts? [y/N]: y
- Deleting S3 object with key sam-app/d0fc9b8b19b413ef42deeaaaea6d380f
- Deleting S3 object with key sam-app/4a36372aab5a71b08809638d800685db.template
- Deleting S3 object with key sam-app/4da1b77c01e1dde3cda23928fcb199a5
- Deleting S3 object with key sam-app/b804bea4cec12b244df6df6bf1259b20.template
- Deleting Cloudformation stack sam-app
Deleted successfully
SAM 設定ファイル (samconfig.toml) の書き方
sam init
の際に samconfig.toml
という設定ファイルができました。
このファイルは sam コマンドの各種パラメータのデフォルト設定を保持しておくものです。
AWS SAM CLI の設定ファイル - AWS Serverless Application Model
基本構造
version = 0.1
[{environment}]
[{environment}.{command}]
[{environment}.{command}.parameters]
{option} = {parameter value}
-
{environment}
: 環境名- 具体的には dev とか prod とかを入れる。
-
sam init
した直後だと、デフォルトの環境名としてdefault
が設定されている。 - sam の
--config-env
オプションで使用する環境名を指定すると samconfig.toml に記載の設定の中で該当する環境の設定が使われる。
-
{command}
: コマンド- 具体的には
build
,deploy
,local_invoke
などの sam のサブコマンドを入れる。 - スペースやハイフン (
-
) は、アンダースコア (_
) に置き換える。
- 具体的には
-
{option} = {parameter value}
: パラメータのキーと値のペア- 使用可能なパラメータキーは各コマンドのオプション参照。
- ハイフン (
-
) は、アンダースコア (_
) に置き換える。
参考資料
以上
Discussion