MAGAZINE
ルーターマガジン
SAMを使ったAWSサーバーレスサービスのローカル実行方法
お疲れさまです。福島です。今回はAWS SAM(Serverless Application Model)を利用して、AWSのサービス(lambdaやAPI Gatewayなど)をローカルで実行する方法を紹介します。
AWS SAM(Serverless Application Model)とは
SAMとはAWSが提供するIaC(Infrastructure as Code)で、インフラをコードとして記述・管理できるサービスです。
他のIaCサービスとして有名なのは、TerraformやServerless Frameworkが挙げられます。
SAMの強み
SAMが他のIaCツールより優れている点は以下の2つが考えられます。
1.AWSが提供している
AWS公式が開発を行っているためコミュニティが廃れる心配や陳腐化する可能性が低いです。
2.ローカルで実行できる
Terraformなどの他のIaCを利用しても個別にテストをすることは可能ですが、結合テストを行うことはできません。しかしSAMは、Dockerを利用して本番と同等の環境がローカルに構築されるため、デプロイをしなくても全体の動作確認が行えます。ローカルで十分にテストが行えるので、デプロイの回数を最小限に抑えることができ、ダウンタイムの減少、精神的安心感などのメリットがあります。
使ってみる
SAMは内部でaws cliとDockerを使用しているため、前提としてこれらがインストール済みである必要があります。
aws cliのインストールはこちら
Dockerのインストールはこちら
iam ユーザーの設定を行っておく必要があります。
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html
samのインストール
MacOSの例を示します
brew tap aws/tap
brew install aws-sam-cli
インストール確認
$ sam --version
SAM CLI, version 1.61.0
ディレクトリ作成
$ mkdir sam_development && cd sam_development
sam initをすると、公式が用意したテンプレートを利用することができるのですが、今回は理解促進のため、自分で実装します。
また、ローカルにdynamodbを構築するためdocker-compose.ymlも作成します。
$ touch template.yaml
$ mkdir src && touch src/app.py
$ touch docker-compose.yml
src/app.rbにビジネスロジック、template.yamlにインフラの構成を記述します。
作るもの
本記事でどんなものを作るのか確認します。
登場するサービスは API Gateway, Lambda, DynamoDBの3つです。ユーザーはAPI Gatewayに対してHTTPリクエストを送信することでDynamoDBを操作します。
名簿のような役割を担うテーブルを想定し、テーブル名はmembers, パーティションキーはnameとします。
エンドポイントに対してPOSTリクエストをし、テーブルに新たにデータを登録することを目標とします。
app.py
import json
import boto3
client = boto3.client('dynamodb', endpoint_url = "http://dynamodb-local:8000")
def post_handler(event, context):
# リクエストbody取得
body = json.loads(event["body"])
name = body["name"]
# db登録
result = client.put_item(TableName="members", Item={"name": {"S": name}})
response = {
"statusCode": 200,
"body": json.dumps(result)
}
return response
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
dva-01-001
Sample SAM Template for dva-01-001
Globals:
Function:
Timeout: 10
Resources:
PostMemberFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: app.post_handler
Role: !GetAtt PostMemberFunctionRole.Arn
Runtime: python3.9
Events:
HelloWorld:
Type: Api
Properties:
Path: /members
Method: post
PostMemberFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: PostMemberFunctionPolicies
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- dynamodb:List*
- dynamodb:DescribeReservedCapacity*
- dynamodb:DescribeLimits
- dynamodb:DescribeTimeToLive
Resource: "*"
- Effect: Allow
Action:
- dynamodb:PutItem
Resource: !GetAtt Table.Arn
Table:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: name
AttributeType: S
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: name
KeyType: HASH
TableName: members
docker-compose.yml
version: '3.8'
services:
dynamodb-local:
command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
image: "amazon/dynamodb-local:latest"
container_name: dynamodb-local
ports:
- "8000:8000"
volumes:
- "./docker/dynamodb:/home/dynamodblocal/data"
working_dir: /home/dynamodblocal
networks:
- lambda-local
networks:
lambda-local:
external: true
これら3つのファイルが作成できたら、ビルド、ネットワーク作成、dynamodbの立ち上げを行います。
$ sam build
$ docker network create lambda-local
$ docker-compose up -d
dynamodbが正常に起動しているのか確かめるためにテーブルの作成をaws dnamodbコマンドから行います。
$ aws dynamodb create-table --table-name members \
--attribute-definitions AttributeName=name,AttributeType=S \
--key-schema AttributeName=name,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 \
--endpoint-url "http://localhost:8000"
テーブルの作成ができています。
$ aws dynamodb list-tables --endpoint-url 'http://localhost:8000'
{
"TableNames": [
"members"
]
}
次にAPI Gatewayをローカルで起動させます。ローカルのdynamodbを使用するために、先程作成したネットワークを指定しています。
$ sam local start-api --docker-network lambda-local
POSTリクエストを送信して登録を試してみます。
$ curl "http://localhost:3000/members" -X POST -d '{"name": "fukushima"}'
登録できているか確認します。
$ aws dynamodb scan --table-name members --endpoint-url "http://localhost:8000"
{
"Items": [
{
"name": {
"S": "fukushima"
}
}
],
"Count": 1,
"ScannedCount": 1,
"ConsumedCapacity": null
}
期待通りデータが登録できました。
最後に
AWS公式が提供しているSAMを使い、ローカル上でクラウドサービスを実行する方法をご紹介しました。
SAMはデプロイまでカバーしており
sam deploy --guided
とすることで、実際にローカルで作成したサービスをAWSにデプロイすることができます。
コードベースでインフラを管理することで、属人化しにくくできます。
今回紹介したlambdaやDynamoDB以外にもS3やEC2なども同様にSAMで記述できます。積極的に取り入れていきましょう!
CONTACT
お問い合わせ・ご依頼はこちらから