この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS Serverless Application Model (AWS SAM) とは
AWS Serverless Application Model (以降 AWS SAM) は、AWS が公式で提供しているサーバーレスアプリケーションを構築するためのフレームワーク (モデル) です。Lambda, API Gateway, DynamoDB のリソースをひとまとめに管理 (作成 / 更新 / 削除) することができます。
AWS SAM は、元々 Flourish という名前で知られていたものです。今年5月に開催された Serverless Conf にて Tim Wagner から発表されて以来、期待が集まっていました。re:Invent 2016 を目前に、ついにリリースされました。
本記事では AWS SAM がどういったものなのか ざっくり調べてまとめてみました。
AWS SAM は AWS CloudFormation の拡張である
AWS SAM は言うならば、 AWS CloudFormation (以降 CFn) のサーバーレス拡張 です。AWS SAM の各リソースは CFn のこれまでのリソース定義と似たフォーマットで定義されており、このフォーマットで記述されたテンプレートを CFn に渡すことで、スタックとして AWS 上に展開することができます。具体的には以下のような感じです。YAML または JSON で記述することができます。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
FunctionName:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs4.3
Transform: AWS::Serverless-2016-10-31
というところがミソで、これを書くことで CFn はテンプレートを AWS SAM フォーマットのテンプレートとして読むようです。
AWS SAM でサーバーレスアプリケーションを構築してみよう
本記事では、GitHub で公開されているサンプルの api_backend を参考に構築してみたいと思います。こちらは、DynamoDB テーブルの CRUD を API Gateway + Lambda で構築するといったものです。
作業用ディレクトリの作成
まずは AWS SAM で取り扱うファイルをまとめるため、ローカルの適当な場所にディレクトリを作成しておきましょう。
$ mkdir api_backend
$ cd api_backend
以降、このディレクトリ配下で作業します。
Lambda ファンクション
まずは DynamoDB テーブルの CRUD を提供する Lambda ファンクションを用意します *1。dynamodb-doc を使って DynamoDB の API を呼び出しています。また、先日 AWS SAM と同時にリリースされた Lambda の環境変数を使って、DynamoDB のテーブル名を取得するようにしています。
index.js
'use strict';
console.log('Loading function');
let doc = require('dynamodb-doc');
let dynamo = new doc.DynamoDB();
const tableName = process.env.TABLE_NAME;
const createResponse = (statusCode, body) => {
return {
"statusCode": statusCode,
"body": body
}
};
exports.get = (event, context, callback) => {
var params = {
"TableName": tableName,
"Key": {
"id": event.pathParameters.resourceId
}
};
dynamo.getItem(params, (err, data) => {
var response;
if (err)
response = createResponse(500, err);
else
response = createResponse(200, data.Item.doc);
callback(null, response);
});
};
exports.put = (event, context, callback) => {
var item = {
"id": event.pathParameters.resourceId,
"doc": event.body
};
var params = {
"TableName": tableName,
"Item": item
};
dynamo.putItem(params, (err, data) => {
var response;
if (err)
response = createResponse(500, err);
else
response = createResponse(200, null);
callback(null, response);
});
};
exports.delete = (event, context, callback) => {
var params = {
"TableName": tableName,
"Key": {
"id": event.pathParameters.resourceId
}
};
dynamo.deleteItem(params, (err, data) => {
var response;
if (err)
response = createResponse(500, err);
else
response = createResponse(200, null);
callback(null, response);
});
};
こちらの Lambda ファンクションを index.js
という名前で保存し、app.zip
という名前の Zip ファイルに圧縮しておきます。なお、これらのファイル名は何でも構いません。
$ zip app.zip index.js -x "*.DS_Store"
SAM テンプレートの作成
次に、SAM 形式のテンプレートを作成しましょう。
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Simple CRUD webservice. State is stored in a SimpleTable (DynamoDB) resource.
Resources:
GetFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.get
Runtime: nodejs4.3
Policies: AmazonDynamoDBReadOnlyAccess
Environment:
Variables:
TABLE_NAME: !Ref Table
Events:
GetResource:
Type: Api
Properties:
Path: /resource/{resourceId}
Method: get
PutFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.put
Runtime: nodejs4.3
Policies: AmazonDynamoDBFullAccess
Environment:
Variables:
TABLE_NAME: !Ref Table
Events:
PutResource:
Type: Api
Properties:
Path: /resource/{resourceId}
Method: put
DeleteFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.delete
Runtime: nodejs4.3
Policies: AmazonDynamoDBFullAccess
Environment:
Variables:
TABLE_NAME: !Ref Table
Events:
DeleteResource:
Type: Api
Properties:
Path: /resource/{resourceId}
Method: delete
Table:
Type: AWS::Serverless::SimpleTable
`` と書かれている部分は、Lambda ファンクションの Zip ファイルのアップロード先となる S3 バケットの名前に変更してください。S3 バケットを作成するには、次のようなコマンドを叩きます。
$ aws s3 mb s3://<bucket-name> --region <region>
パッケージ & デプロイ
次に、パッケージとデプロイを行います。ここからは AWS CLI を使うことが必須となるので、念のため最新バージョンにしておきましょう。
$ aws --version
aws-cli/1.11.19 Python/2.7.11 Darwin/16.3.0 botocore/1.4.76
パッケージ
パッケージは、Lambda ファンクションの Zip ファイルを S3 バケットにアップロードすることを指します。AWS CLI の CloudFormation の package コマンド を叩くだけで、コマンドを実行したディレクトリにある Zip ファイルをアップロードすることができます。
$ aws cloudformation package \
--template-file template.yaml \
--output-template-file serverless-output.yaml \
--s3-bucket <bucket-name>
Uploading to d922f3e6e1268d5923d0746c1996de62 2482 / 2482.0 (100.00%)
Successfully packaged artifacts and wrote output template to file serverless-output.yaml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file /Users/User/work/api_backend/serverless-output.yaml --stack-name <YOUR STACK NAME>
S3 に Zip ファイルがアップロードされ、そのファイルの Uri が --template-file
で指定したテンプレートに書き足され、--output-template-file
で指定したファイル名で出力されます。抜粋すると、以下のようになっています。
AWSTemplateFormatVersion: '2010-09-09'
Description: Simple CRUD webservice. State is stored in a SimpleTable (DynamoDB) resource.
Resources:
DeleteFunction:
Properties:
CodeUri: s3://<bucket-name>/d922f3e6e1268d5923d0746c1996de62
Environment:
Variables:
TABLE_NAME:
Ref: Table
Events:
DeleteResource:
Properties:
Method: delete
Path: /resource/{resourceId}
Type: Api
Handler: index.delete
Policies: AmazonDynamoDBFullAccess
Runtime: nodejs4.3
Type: AWS::Serverless::Function
デプロイ
パッケージで書き出したテンプレートファイルを元に、AWS CLI の CloudFormation の deploy コマンド を叩きます。これで、テンプレートに記述されている通りのリソースが構築されます。
$ aws cloudformation deploy \
--template-file serverless-output.yaml \
--stack-name <new-stack-name> \
--capabilities CAPABILITY_IAM
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - api-backend
`` には、適当なスタック名を付けてください。
処理が完了すると、各リソースが構築されていることが確認できます。
CFn Stack が完了しており、
DynamoDB がいい感じで構築されており、
Lambda がいい感じで構築されており、
API Gateway がいい感じで構築されています。
試す
まずは PUT から。DynamoDB を考慮し、Upsert を行うような API になっています。
$ curl -H "Accept: application/json" -H "Content-type: application/json" \
-X PUT https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/resource/hoge \
-d '{"name":"hoge", "description":"I am hoge."}'
上記で作成したものを GET します。
$ curl -H "Content-type: application/json" \
-X GET https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/resource/hoge
{"name":"hoge", "description":"I am hoge."}
最後に DELETE で消してみます。
$ curl -H "Content-type: application/json" \
-X DELETE https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/resource/hoge
いい感じに動作しています! DynamoDB のマネジメントコンソールで、アイテムの変化を確認しながら試すと良いでしょう。
まとめ
AWS SAM についてざっくりと触れてみました。サーバーレスアプリケーションは適用可能範囲が多岐に渡るので、用途によってテンプレートの構成は変わってきます。今後は AWS SAM を使ってサーバーレスアプリケーションを構築する方法を、用途別にご紹介できればと思います。
参考
- awslabs/serverless-application-model (GitHub)
- AWS Serverless Application Model (SAM) - awslabs/serverless-application-model
- Deploying Lambda-based Applications - AWS Lambda
- Create Your Own Serverless Application - AWS Lambda
- Introducing Simplified Serverless Application Deployment and Management | AWS Compute Blog
脚注
- 筆者が試した時点では、このソースコードの20行目の「"id"」の部分が GitHub のサンプルコードでは「id」となっており GET でエラーが発生します。ご留意を。 ↩