SAMを使ってAPI GatewayとLambdaでサーバーレスアプリケーションを作成する
以下のページで、API GatewayとLambdaを使ったサーバーレスアプリを作成する方法を取り上げましたが、今回はSAMを使用して、同様の構成を再現したいと思います。
SAM(Serverless Application Model)とは、AWSが提供するサーバーレスアプリケーションアプリ開発用フレームワークです。
AWS サーバーレスアプリケーションモデル (SAM、Serverless Application Model) は、サーバーレスアプリケーション構築用のオープンソースフレームワークです。迅速に記述可能な構文で関数、API、データベース、イベントソースマッピングを表現できます。 (中略) デプロイ中、SAM が SAM 構文を AWS CloudFormation 構文に変換および拡張することで、サーバーレスアプリケーションの構築を高速化することができます。
AWS サーバーレスアプリケーションモデル
本ページでは、AWS公式のチュートリアル「チュートリアル: Hello World アプリケーションのデプロイ」の手順に従ってSAMを確認します。
構築する環境

「LambdaとAPI Gatewayを使用したサーバーレスアプリ」と同様の環境を構築します。
今回作成するアプリケーションは以下の設定で作成します。
- アプリケーション名:fa-006
- Lambda関数のランタイム環境:Python3.8
SAMアプリ作成
AWS SAM CLIでアプリケーションを作成します。
同ツールを未インストールの場合は「AWS SAM CLI のインストール」をご確認ください。
$ 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 - nodejs12.x
2 - python3.8
3 - ruby2.7
4 - go1.x
5 - java11
6 - dotnetcore3.1
7 - nodejs10.x
8 - python3.7
9 - python3.6
10 - python2.7
11 - ruby2.5
12 - java8.al2
13 - java8
14 - dotnetcore2.1
Runtime: 2
Project name [sam-app]: fa-006
Cloning app templates 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: fa-006
Runtime: python3.8
Dependency Manager: pip
Application Template: hello-world
Output Directory: .
Code language: Bash (bash)
SAM設定ファイル
以下のURLに、SAMのテンプレートファイルを配置します。
テンプレートファイルのポイント解説
今回の環境を構成するための、テンプレートファイルのポイントを取り上げます。
Lambda関数はAWS::Serverless::Functionで定義する
SAMでLambda関数を定義します。
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.8
Events:
HelloWorld:
#Type: Api
Type: HttpApi
Properties:
Path: /hello
Method: get
Code language: YAML (yaml)
CloudFormationでは、Typeプロパティで作成するリソースのタイプを指定します。通常、Lambda関数を定義する場合は「AWS::Lambda::Function」を使用しますが、SAMの記法に従うと「AWS::Serverless::Function」となります。
CodeUriプロパティで実行するスクリプトが設置されているディレクトリPATHを指定します。PATHの指定は本テンプレートファイルからの相対PATHで指定することが可能です。今回は本テンプレートファイルと同じ位置に設置されている「hello_world」ディレクトリにスクリプトを設置するという意味となります。
HandlerプロパティはLambda関数が呼び出された際に、実際に実行する関数を指定するパラメータです。「app.lambda_handler」と指定することで、先述の「hello_world」ディレクトリに設置されているapp.pyファイル内の、lamabda_handler関数を実行するという意味になります。
Runtimeプロパティは関数のランタイム環境を指定するパラメータです。今回はPython3.8で実行するように指定します。
Eventsプロパティは本関数に関連付くイベントを定義することができます。
この関数をトリガーするイベントを指定します。イベントは、1 つのタイプと、そのタイプに依存する一連のプロパティで構成されます。
AWS::Serverless::Function
今回は本プロパティを使用して、本関数に紐づくAPI Gatewayを定義します。
Eventsプロパティ内のTypeプロパティで、作成するリソースを指定できます。
このイベントマッピングのプロパティを記述するオブジェクト。プロパティのセットは、定義された Type に準拠する必要があります。
Type: S3|SNS|Kinesis|DynamoDB|SQS|Api|Schedule|CloudWatchEvent|EventBridgeRule|CloudWatchLogs|IoTRule|AlexaSkill|Cognito|HttpApi|MSK|MQ
EventSource
今回はHTTP APIタイプのAPI Gatewayを作成しますので、「HttpApi」を指定します。
Propertiesプロパティで作成するリソースの各種パラメータを指定します。
PathおよびMethodプロパティはAPI Gatewayのルートに関するパラメータです。今回は作成されるエンドポイントの「/hello」にGETでアクセスされた場合、本関数が実行されるように指定します。
なお実行する関数の内容ですが、「LambdaとAPI Gatewayを使用したサーバーレスアプリ」と同様ですので、そちらをご確認ください。
SAMアプリケーションデプロイ
AWS SAM CLIでビルドおよびデプロイします。
まずビルドします。
$ sam build --use-container
Starting Build inside a container
Building codeuri: hello_world/ runtime: python3.8 metadata: {} functions: ['HelloWorldFunction']
Fetching amazon/aws-sam-cli-build-image-python3.8 Docker container image...................................................................................................................................................................................................................................................................
Mounting /your-directory/hello_world as /tmp/samcli/source:ro,delegated inside runtime container
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided
Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource
Code language: Bash (bash)
use-containerオプションを使用することで、アプリケーションのビルドをDockerの専用コンテナ内で実施することができます。
関数がネイティブにコンパイルされた依存関係を持つパッケージに依存する場合は、このオプションを使用して Lambda に似た Docker コンテナ内で関数を構築します。
sam build
今回で言いますと、ランタイム環境にPython3.8をしていますので、CLIを実行するローカルに同環境が用意されている必要があります。しかし本オプションを使用することで、ビルドはコンテナ内で完結しますので、ローカル環境を気にする必要はありません。
続いてアプリケーションのデプロイに進みます。
$ sam deploy --guided
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [sam-app]: fa-006
AWS Region [us-east-1]: 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
HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
Save arguments to configuration file [Y/n]: y
SAM configuration file [samconfig.toml]:
SAM configuration environment [default]:
....
Deploying with following values
===============================
Stack name : fa-006
Region : ap-northeast-1
Confirm changeset : True
Deployment s3 bucket : [bucket-name]
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
Initiating deployment
=====================
HelloWorldFunction may not have authorization defined.
Waiting for changeset to be created..
CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Add HelloWorldFunctionHelloWorldPermission AWS::Lambda::Permission N/A
+ Add HelloWorldFunctionRole AWS::IAM::Role N/A
+ Add HelloWorldFunction AWS::Lambda::Function N/A
+ Add ServerlessHttpApiApiGatewayDefaultStage AWS::ApiGatewayV2::Stage N/A
+ Add ServerlessHttpApi AWS::ApiGatewayV2::Api N/A
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:[accound-id]:changeSet/samcli-deploy1641285770/e5418e4b-792d-4e96-93ae-5d10a45ee858
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
...
CloudFormation outputs from deployed stack
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Key HelloWorldFunctionIamRole
Description Implicit IAM Role created for Hello World function
Value arn:aws:iam::[account-id]:role/fa-006-HelloWorldFunctionRole-VBT1AK5MRG5J
Key HelloWorldApi
Description API Gateway endpoint URL for Prod stage for Hello World function
Value https://25ypbm7one.execute-api.ap-northeast-1.amazonaws.com/hello
Key HelloWorldFunction
Description Hello World Lambda Function ARN
Value arn:aws:lambda:ap-northeast-1:[account-id]:function:fa-006-HelloWorldFunction-qajs4WIMSN60
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - fa-006 in ap-northeast-1
Code language: Bash (bash)
正常にデプロイが完了しました。
デプロイのログから、今回は5つのリソースが生成されたことが確認できます。
またOutputsセクションに記載した出力内容から、今回作成されたAPI Gatewayエンドポイントは「https://25ypbm7one.execute-api.ap-northeast-1.amazonaws.com/hello」であることがわかります。
SAMアプリにアクセス
それでは実際にアクセスします。
$ curl https://25ypbm7one.execute-api.ap-northeast-1.amazonaws.com/hello
{"message": "hello world from Awstut !"}
Code language: Bash (bash)
正常にLambda関数の実行結果を取得することができました。
このようにSAMを使用することによって、通常のCloudFormationの記法に比べ、より簡潔にサーバーレスアプリケーションを構築することができることがわかりました。
(参考)SAMで作成された各種リソースの詳細
SAMでデプロイしたアプリケーションの各種リソースの状況を確認します。
冒頭で確認した通り、SAMはCloudFormationの変形版とも言えるサービスですので、SAMアプリケーションをデプロイすると、CloudFormationスタックが作成されます。
AWS Management Consoleからスタックの作成状況を確認します。

テンプレートファイルでは直接定義していませんが、API GatewayがLambda関数を呼び出すためのPermissionと、Lambda関数に付与するIAMロールも、自動的に作成されていることがわかります。
Permissionの中身は以下の通りです。

Lambda関数の呼び出しに関する条件が定義されています。今回作成したAPI Gatewayで、かつURLが/helloのGETメソッドからの呼び出しの場合のみに許可されるという内容です。
以下は作成されたIAMロールです。

AWS管理ポリシーAWSLambdaBasicExecutionRoleがアタッチされていることがわかります。Lambda関数を実行する上での最低限の権限を付与しているということになります。
本スタックには含まれていませんが、API Gatewayのルート・統合リソースも作成されています。
まず統合です。

作成したLambda関数が関連づいていることがわかります。
次にルートです。

/helloのGETメソッドに先ほどの統合が関連づいています。これらの設定が合わさってAPI Gatewayにアクセスがあった場合に、Lambda関数が実行されるという動作を実現することができます。
まとめ
SAMを使用することによって、通常のCloudFormationの記法に比べ、より簡潔にサーバーレスアプリケーションを構築することができることがわかりました。