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 で管理、とかでも良いかもしれません(同じこと前も書いた気がする)。