AWS Serverless Application Model (AWS SAM) を素振りしたメモ

Serverless Framework を素振り したので次は AWS SAM を素振りしてみました。

インストール

まずは AWS SAM CLI をインストールします。 Linux へのインストール手順でおもむろに Homebrew が出てきます。

たまたま Homebrew が入ってたので良いですけど、もし入れてなければこの時点でやる気がだいぶ減衰してそうです。

brew tap aws/tap
brew install aws-sam-cli

pip でもインストール出来るようなので Homebrew が入っていないなら pip でも良いかもしれません(未確認)。

pip install -U aws-sam-cli

チュートリアル

とりあえず下記のチュートアリアルを試してみます。

# プロンプトでいろいろ聞かれるので適当に入力します
sam init

# サブディレクトリが作成されているので移動
cd sam-app

# .aws-sam/build/ にいろいろ作成されます
sam build

# またプロンプトでいろいろ聞かれるので適当に入力します
sam deploy --guided

AWS SAM も CloudFormation を使ってデプロイを行います。デプロイ後にマネジメントコンソールで CloudFormation を見ると 2 つのスタックが作成されていました。

aws-sam-cli-managed-default スタックでは下記のリソースが作成されていました。Lambda のコードをアップロードするための S3 バケットなのでしょう。

  • AWS::S3::Bucket
  • AWS::S3::BucketPolicy

sam-app スタック(名前は sam deploy --guided で入力したもの)では下記のリソースが作成されていました。

  • AWS::Lambda::Function
  • AWS::Lambda::Permission
  • AWS::IAM::Role
  • ApiGateway::RestApi
  • ApiGateway::Deployment
  • ApiGateway::Stage

sam deploy --guided の実行結果にエンドポイントの URL が表示されています。curl で叩くと応答が返ってきます。

curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/
#=> {"message":"hello world"}

sam local start-api でローカルで API を実行できます。

sam local start-api

別のターミナルから curl http://127.0.0.1:3000/hello すると、sam local start-api を実行している方のターミナルで Docker の lambci/lambda:nodejs12.x というイメージがダウンロードされ、コンテナが起動し、次のように同じ内容が応答されます。

curl http://127.0.0.1:3000/hello
#=> {"message":"hello world"}

sam local invoke で関数を直接実行できます。これも Docker コンテナが作られてその中で実行されます。

sam local invoke "HelloWorldFunction" -e events/event.json
# Invoking app.lambdaHandler (nodejs12.x)
#
# Fetching lambci/lambda:nodejs12.x Docker container image......
# Mounting /path/to/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
# START RequestId: ...
# END RequestId: ...
# REPORT RequestId: ...
#
# {"statusCode":200,"body":"{\"message\":\"hello world\"}"}

-e events/event.json で入力を指定しています。このファイルは sam init で作成されたものです。

sam local generate-event でそれっぽい入力ファイルを生成できます。

sam local generate-event apigateway aws-proxy --body "" --path "hello" --method GET > events/api-event.json

最後に、sam コマンドだけではスタックの削除はできないようなので、要らなくなったらマネジメントコンソールで CloudFormation を開いて削除するか、AWS CLI でも削除できます。

aws cloudformation delete-stack --stack-name sam-app

CloudFormation と SAM CLI

SAM も Serverless Framework と同じように CloudFormation で Lambda をデプロイしますが、SAM のテンプレートは CloudFormation の拡張なので、SAM のテンプレートはそのまま CloudFormation でデプロイできます。

例えば次のようなテンプレートを、

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs12.x
      InlineCode: |
          exports.handler = (event) => {
            console.log(JSON.stringify({event}, null, 2));
            return {
              statusCode: 200,
              body: JSON.stringify({event}, null, 2),
            };
          }

AWS CLI でデプロイできます。

aws cloudformation deploy --template-file template.yaml --stack-name minimum --capabilities CAPABILITY_IAM

この例では InlineCode でインラインにコードを記述しています。CodeUri で s3 バケット上のアーカイブも指定できます。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: s3://ore-no-sam-deploy/sam-minimum.zip

この場合はあらかじめ Lambda のコードを zip で S3 バケットにアップロードしておく必要があります。

cd src
find ./ -name '*.js' | zip ../sam-minimum.zip -@
cd ..
aws s3 cp sam-minimum.zip s3://ore-no-sam-deploy/sam-minimum.zip
aws cloudformation deploy --template-file template.yaml --stack-name minimum --capabilities CAPABILITY_IAM

SAM CLI を使えばコードのパッケージングや S3 バケットへのアップロードを自動化できます。まずテンプレートの CodeUri でアップロードするコードのディレクトリを指定します。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: src/

SAM CLI で S3 バケットを指定して実行します。

sam deploy --stack-name minimum --template-file template.yaml --capabilities CAPABILITY_IAM --s3-bucket=ore-no-sam-deploy

テンプレートファイルが template.yaml なら省略できます。

sam deploy --stack-name minimum --capabilities CAPABILITY_IAM --s3-bucket=ore-no-sam-deploy

sam deploy --guided ならプロンプトで確認しつつ必要なオプションをいろいろ指定できます。

sam deploy --guided

このときはデプロイ用の S3 バケットも自動的に作成されます。また、プロンプトの最後で samconfig.toml を保存していれば、以後はオプション指定なしで実行できます。

sam deploy

SAM テンプレート

SAM テンプレートは CloudFormation テンプレートの拡張です。なので CloudFormation テンプレートのリソースがそのまま記述できます。

例えば SQS のキューを作成して Lambda の環境変数に入れたりできます。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: src/
      Environment:
        Variables:
          HELLO_SQS_QUEUE: !Ref HelloWorldQueue
  HelloWorldQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: hello

AWS::Serverless::Function は SAM 特有のリソースです。例えば次のようにイベントソースを指定できます。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs12.x
      CodeUri: src/
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
        CWSchedule:
          Type: Schedule
          Properties:
            Schedule: 'rate(1 minute)'

SAM 特有のリソースには他に次のものがあるようです。

  • AWS::Serverless::Api
  • AWS::Serverless::Application
  • AWS::Serverless::HttpApi
  • AWS::Serverless::LayerVersion
  • AWS::Serverless::SimpleTable
  • AWS::Serverless::StateMachine

詳細は下記あたりのドキュメントを参照。

さいごに

テンプレートが CloudFormation の拡張なので、Serverless Framework のように CloudFormation の記法が使えたり使えなかったりすることもなく、よりスマートに記述できそうです。

Serverless Framework だと destinations で Ref が使えなくて ARN をベタが気にする必要があったりしました。Serverless Framework のテンプレートと CloudFormation のテンプレートの2段になっているがゆえの問題なので他にも色々ありそうです。

ただ、Serverless Framework なら Lambda コードのパッケージングで include/exclude で細かい指定ができたり、devDependencies が自動的に除外されたり、便利なところがありました。AWS SAM だと CodeUri でディレクトリを指定するぐらいしかできないので、Lambda のコードをパッケージングしてデプロイするだけを見れば Serverless Framework の方が便利そうです。

Serverless Framework 固有の記述だけのリソースで完結できて CloudFormation の記述がほとんど出てこなさそうなら Serverless Framework が便利そうで、それ以外のリソースがガッツリ出てきそうなら SAM が便利そうでしょうか。

そもそものところ、AWS 環境の構成管理は基本的に Terraform をメインに使っているので、CloudFormation が出てこない範囲で Serverless Framework を使いつつ、それ以外は Terraform で管理、とかでも良いかもしれません(同じこと前も書いた気がする)。