AWS SAM でサーバレスアプリケーション(WebAPI)を動かす

AWS SAM(Serverless Application Model)は、LambdaやAPI GatewayなどのAWSのサービスを使ったサーバレスアプリケーションを構築可能なフレームワークです。

今回は SAM を使って、サンプルアプリケーションを動かしてみます。

事前準備

AWS CLI, AWS SAM CLI, Node.js のインストールを済ませます。
AWS CLIaws configure などを実行し初期設定を済ませておきます。

% aws --version
aws-cli/2.1.23 Python/3.9.1 Darwin/19.6.0 source/x86_64 prompt/off

% sam --version
SAM CLI, version 1.17.0

% node --version
v14.15.4

アプリケーションの作成

sam init でアプリケーションを作成します。

% 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.8
    3 - ruby2.7
    4 - go1.x
    5 - java11
    6 - dotnetcore3.1
    7 - nodejs12.x
    8 - nodejs10.x
    9 - python3.7
    10 - python3.6
    11 - python2.7
    12 - ruby2.5
    13 - java8.al2
    14 - java8
    15 - dotnetcore2.1
Runtime: 1

Project name [sam-app]: sam-hello-app

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

AWS quick start application templates:
    1 - Hello World Example
    2 - Step Functions Sample App (Stock Trader)
    3 - Quick Start: From Scratch
    4 - Quick Start: Scheduled Events
    5 - Quick Start: S3
    6 - Quick Start: SNS
    7 - Quick Start: SQS
    8 - Quick Start: Web Backend
Template selection: 1

    -----------------------
    Generating application:
    -----------------------
    Name: sam-hello-app
    Runtime: nodejs14.x
    Dependency Manager: npm
    Application Template: hello-world
    Output Directory: .

    Next steps can be found in the README file at ./sam-hello-app/README.md

次の構造のアプリケーションが作成されます。

sam-hello-app
├── README.md
├── events
│   └── event.json
├── hello-world
│   ├── app.js
│   ├── package.json
│   └── tests
│       └── unit
│           └── test-handler.js
└── template.yaml

基本的には template.yaml にアプリケーションの構成を、hello-world/app.js に実際の処理(Lambda関数の実装)をします。

template.yaml は次の内容です(ここではテンプレートのrepositoryを参照しています)。
Resources のセクションに必要なリソース(例えば、Lambda、API Gateway、S3やDynamoDB)を記述していきます。
今回は、Lambda関数としてHelloWorldFunctionが、Lambda関数をAPIとして利用できるようAPI GatewayとしてHelloWorldApiが定義されています。

また、hello-world/app.js は次のとおりです。

ローカル環境での動作確認

まずは手元のPC上で動作確認してみましょう。

npm install

Lambda 関数は、Amazon Linux 上で実行されます。macOSなどの異なる環境上で npm install を行うとパッケージによっては動作しないことがあります。 そこで次のようにプラットフォームを指定して npm install を行います。

% cd hello-world
% npm install --arch=x64 --platform=linux

dev.classmethod.jp

起動(sam local start-api

sam local start-api で Lambda 関数をホストするローカルの HTTP サーバが起動します。

% cd ..
% sam local start-api # template.yaml と同じ階層に移動                    
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. You only need to restart SAM CLI if you update your AWS SAM template
2021-02-07 22:20:50  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)

docs.aws.amazon.com

動作確認

curl を使って先ほど起動したHTTPサーバにアクセスしてみます。

% curl http://127.0.0.1:3000/hello
{"message":"hello world"}

APIとしてLambda関数を実行することができました。

Lambda関数の実装を変更してみましょう

sam local start-api はホットリロードに対応しています。
したがって、Lambda 関数の実装を変更した場合でも、HTTPサーバを再起動せず(sam local start-apiを再実行せずに)、変更が反映されます。

Lambda関数の実装 hello-world/app.js の実装を変更して表示されるメッセージを変更してみましょう。

response = {
    'statusCode': 200,
    'body': JSON.stringify({
        message: 'EZ DO DANCE', // こちらを変更
        // location: ret.data.trim()
    })

では、先ほどと同様に curl でアクセスしてみます。

% curl http://127.0.0.1:3000/hello
{"message":"EZ DO DANCE"}

表示内容が変更されました!

実際の開発の際は、ローカルサーバを起動したままLambda関数の実装を変更し動作確認する流れになりそうです(とても便利ですね)。

AWS環境へデプロイする(sam buildsam deploy

ローカル環境での動作確認が済んだので、実際にAWS環境へデプロイしてみましょう。

ビルドとデプロイ

sam build--use-container を指定すると実際の動作環境に近い Docker コンテナ上でビルドを行うことができます。

sam deploy--guided を指定するとデプロイに関わる設定を行いながらデプロイが行えます。

# アプリケーションのビルド
% sam build --use-container
Starting Build inside a container
Building codeuri: hello-world/ runtime: nodejs14.x metadata: {} functions: ['HelloWorldFunction']

Fetching amazon/aws-sam-cli-build-image-nodejs14.x Docker container image......
Mounting /Users/yutokubo/projects/sam-hello-app/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 NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
# デプロイ
% sam deploy --guided

Configuring SAM deploy
======================

    Looking for config file [samconfig.toml] :  Not found

    Setting default arguments for 'sam deploy'
    =========================================
    Stack Name [sam-app]: sam-hello-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
    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]:

    Looking for resources needed for deployment: Not found.
    Creating the required resources...
    Successfully created!

        Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-xxxxxx
        A different default S3 bucket can be set in 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-hello-app/6d165a6f8e6b1f14d69a5f59807a9541  121586 / 121586.0  (100.00%)

    Deploying with following values
    ===============================
    Stack name                   : sam-hello-app
    Region                       : ap-northeast-1
    Confirm changeset            : True
    Deployment s3 bucket         : aws-sam-cli-managed-default-samclisourcebucket-xxxxxx
    Capabilities                 : ["CAPABILITY_IAM"]
    Parameter overrides          : {}
    Signing Profiles             : {}

Initiating deployment
=====================
HelloWorldFunction may not have authorization defined.
Uploading to sam-hello-app/a9c5be03ba9e86b1f09de6c10ede13d7.template  1108 / 1108.0  (100.00%)

Waiting for changeset to be created..

CloudFormation stack changeset
-------------------------------------------------------------------------------------------------
Operation                LogicalResourceId        ResourceType             Replacement            
-------------------------------------------------------------------------------------------------
+ Add                    HelloWorldFunctionHell   AWS::Lambda::Permissio   N/A                    
                         oWorldPermissionProd     n                                               
+ Add                    HelloWorldFunctionRole   AWS::IAM::Role           N/A                    
+ Add                    HelloWorldFunction       AWS::Lambda::Function    N/A                    
+ Add                    ServerlessRestApiDeplo   AWS::ApiGateway::Deplo   N/A                    
                         yment0000000000          yment                                           
+ Add                    ServerlessRestApiProdS   AWS::ApiGateway::Stage   N/A                    
                         tage                                                                     
+ Add                    ServerlessRestApi        AWS::ApiGateway::RestA   N/A                    
                                                  pi                                              
-------------------------------------------------------------------------------------------------

Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:000000:changeSet/samcli-deploy000000/9db63ba3-ddf4-4c2c-9af0-095e65510000


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

2021-02-07 22:51:42 - Waiting for stack create/update to complete

CloudFormation events from changeset
-------------------------------------------------------------------------------------------------
ResourceStatus           ResourceType             LogicalResourceId        ResourceStatusReason   
-------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS       AWS::IAM::Role           HelloWorldFunctionRole   -                      
CREATE_IN_PROGRESS       AWS::IAM::Role           HelloWorldFunctionRole   Resource creation      
                                                                           Initiated              
CREATE_COMPLETE          AWS::IAM::Role           HelloWorldFunctionRole   -                      
CREATE_IN_PROGRESS       AWS::Lambda::Function    HelloWorldFunction       -                      
CREATE_IN_PROGRESS       AWS::Lambda::Function    HelloWorldFunction       Resource creation      
                                                                           Initiated              
CREATE_COMPLETE          AWS::Lambda::Function    HelloWorldFunction       -                      
CREATE_IN_PROGRESS       AWS::ApiGateway::RestA   ServerlessRestApi        -                      
                         pi                                                                       
CREATE_IN_PROGRESS       AWS::ApiGateway::RestA   ServerlessRestApi        Resource creation      
                         pi                                                Initiated              
CREATE_COMPLETE          AWS::ApiGateway::RestA   ServerlessRestApi        -                      
                         pi                                                                       
CREATE_IN_PROGRESS       AWS::ApiGateway::Deplo   ServerlessRestApiDeplo   -                      
                         yment                    yment47fc2d5f9d                                 
CREATE_IN_PROGRESS       AWS::ApiGateway::Deplo   ServerlessRestApiDeplo   Resource creation      
                         yment                    yment47fc2d5f9d          Initiated              
CREATE_IN_PROGRESS       AWS::Lambda::Permissio   HelloWorldFunctionHell   Resource creation      
                         n                        oWorldPermissionProd     Initiated              
CREATE_IN_PROGRESS       AWS::Lambda::Permissio   HelloWorldFunctionHell   -                      
                         n                        oWorldPermissionProd                            
CREATE_COMPLETE          AWS::ApiGateway::Deplo   ServerlessRestApiDeplo   -                      
                         yment                    yment47fc2d5f9d                                 
CREATE_IN_PROGRESS       AWS::ApiGateway::Stage   ServerlessRestApiProdS   -                      
                                                  tage                                            
CREATE_IN_PROGRESS       AWS::ApiGateway::Stage   ServerlessRestApiProdS   Resource creation      
                                                  tage                     Initiated              
CREATE_COMPLETE          AWS::ApiGateway::Stage   ServerlessRestApiProdS   -                      
                                                  tage                                            
CREATE_COMPLETE          AWS::Lambda::Permissio   HelloWorldFunctionHell   -                      
                         n                        oWorldPermissionProd                            
CREATE_COMPLETE          AWS::CloudFormation::S   sam-hello-app            -                      
                         tack                                                                     
-------------------------------------------------------------------------------------------------

CloudFormation outputs from deployed stack
-------------------------------------------------------------------------------------------------
Outputs                                                                                         
-------------------------------------------------------------------------------------------------
Key                 HelloWorldFunctionIamRole                                                   
Description         Implicit IAM Role created for Hello World function                          
Value               arn:aws:iam::0000000:role/sam-hello-app-HelloWorldFunctionRole-        
BVZKC3SFRLYW                                                                                    

Key                 HelloWorldApi                                                               
Description         API Gateway endpoint URL for Prod stage for Hello World function            
Value               https://6xcmvqyqw4.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/     

Key                 HelloWorldFunction                                                          
Description         Hello World Lambda Function ARN                                             
Value               arn:aws:lambda:ap-northeast-1:0000000:function:sam-hello-app-          
HelloWorldFunction-00000000                                                                 
-------------------------------------------------------------------------------------------------

Successfully created/updated stack - sam-hello-app in ap-northeast-1

sam deploy を実行すると、CloudFormationを使ってデプロイや実行に必要な IAM role、リソースとしてLambdaやAPI Gatewayが追加されます。

また、今回指定した設定内容は samconfig.toml に保存されています。
次回以降のデプロイは --guided を指定せず sam deploy で実行可能です。

% sam deploy

動作確認

では動作確認してみましょう。

% curl https://6xcmvqyqw4.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
{"message":"EZ DO DANCE"}%     

AWS環境でも動作できることを確認できました!(とても簡単ですね!)

備考: sam local start-api でホットリロードが有効にならないケース

sam build を行った後に sam local start-api を実行すると、ホットリロードが利用できずLambda関数の変更が反映されないことがことがあります。
これは、sam local start-api では sam build の出力結果である .aws-sam 配下のプログラムが優先されることのようです。

この場合は、.aws-sam を削除してから sam local start-api を実行します。

% rm -rf .aws-sam
% sam local start-api

qiita.com

終わりに

今回は、AWS SAM を使って、サーバレスアプリケーションを動作させてみました。

SAM CLI を使うことでローカル環境での動作確認が可能なこと、AWSコンソールをポチポチすることなくデプロイまで実施できることを確認しました。
特に、各種デプロイやリソースがCloudFormationで管理されることで、開発環境や本番環境など複数の環境が存在する場合でも容易に環境構築やリリースが可能になりそうです。

次は、より実践的なWebAPIを開発してみようと思います。

参考文献