2018年2月17日土曜日

AWS SAM Local と DynamoDB Local で API Gateway + Lambda を試す


下記のAWSのブログで紹介されていたサーバレスアプリケーションを試してみました。
AWS SAM Local(ベータ版) – サーバーレスアプリケーションをローカルに構築してテストする
AWS SAM Local と DynamoDB Local は、VirtualBoxの同じサーバ(CentOS7)で実行します。

1. ローカルにLambda の用意


AWS SAM Local のインストールは、こちらを参照。
template.yaml は以下のとおり。
AWSブログのものを、少し変えています。
Events に GetVotes を追加しました。
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  VotesTable:
    Type: "AWS::Serverless::SimpleTable"
  VoteSpacesTabs:
    Type: "AWS::Serverless::Function"
    Properties:
      Runtime: python2.7
      Handler: vote_function.vote_handler
      Policies: AmazonDynamoDBFullAccess
      Environment:
        Variables:
          TABLE_NAME: !Ref VotesTable
      Events:
        GetVotes:
          Properties:
            Method: get
            Path: /
          Type: Api
        Vote:
          Type: Api
          Properties:
            Path: /
            Method: post

lambdaのソースコードは、以下のとおり。
AWS SAM Local で lambda を実行した場合は、DynamoDB の接続先(endpoint) をローカルのDynamoDB しています。
192.168.56.102 は、DynamoDBが起動するローカルのIPアドレスです。
ここを、localhost にすると、AWS SAM Local で lambda が実行される Dockerコンテナ を意味し、ローカルのDynamoDB に接続できないので注意。
import os
import json
import boto3

def vote_handler(event, context):

    if os.getenv("AWS_SAM_LOCAL"):
        votes_table = boto3.resource(
            'dynamodb',
            endpoint_url="http://192.168.56.102:8000/"
        ).Table("spaces-tabs-votes")
    else:
        votes_table = boto3.resource('dynamodb').Table(os.getenv('TABLE_NAME'))

    if event['httpMethod'] == 'GET':
        resp = votes_table.scan()
        return {'body': json.dumps({item['id']: int(item['votes']) for item in resp['Items']})}
    elif event['httpMethod'] == 'POST':
        try:
            body = json.loads(event['body'])
        except:
            return {'statusCode': 400, 'body': 'malformed json input'}
        if 'vote' not in body:
            return {'statusCode': 400, 'body': 'missing vote in request body'}
        if body['vote'] not in ['spaces', 'tabs']:
            return {'statusCode': 400, 'body': 'vote value must be "spaces" or "tabs"'}

        resp = votes_table.update_item(
            Key={'id': body['vote']},
            UpdateExpression='ADD votes :incr',
            ExpressionAttributeValues={':incr': 1},
            ReturnValues='ALL_NEW'
        )
        return {'body': "{} now has {} votes".format(body['vote'], resp['Attributes']['votes'])}

用意したファイルは以下のとおり。
[root@centos702 lambda5]# ls
template.yaml  vote_function.py

2. ローカルに DynamoDB の用意


dynamodb ディレクトリを作成して、そこにDynamoDBをインストールします。
[root@centos702 ~]# mkdir dynamodb
[root@centos702 ~]# cd dynamodb/
[root@centos702 dynamodb]# curl -OL https://s3-ap-northeast-1.amazonaws.com/dynamodb-local-tokyo/dynamodb_local_latest.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 16.2M  100 16.2M    0     0  3551k      0  0:00:04  0:00:04 --:--:-- 4084k
[root@centos702 dynamodb]# tar xfz dynamodb_local_latest.tar.gz
[root@centos702 dynamodb]# ls
DynamoDBLocal.jar  DynamoDBLocal_lib  LICENSE.txt  README.txt  dynamodb_local_latest.tar.gz  third_party_licenses

以下のようにコマンドを実行して、DynamoDBを起動します。
[root@centos702 dynamodb]# java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
Initializing DynamoDB Local with the following configuration:
Port:   8000
InMemory:       false
DbPath: null
SharedDb:       true
shouldDelayTransientStatuses:   false
CorsParams:     *



別のターミナルを開きます。
以下のようにコマンドを実行して、DynamoDBにテーブルを作成します。
[root@centos702 lambda5]# aws dynamodb create-table --table-name spaces-tabs-votes --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1 --output table --endpoint-url http://localhost:8000
------------------------------------------------------------------------------------------
|                                       CreateTable                                      |
+----------------------------------------------------------------------------------------+
||                                   TableDescription                                   ||
|+------------------+-------------------------------------------------------------------+|
||  CreationDateTime|  1518857833.28                                                    ||
||  ItemCount       |  0                                                                ||
||  TableArn        |  arn:aws:dynamodb:ddblocal:000000000000:table/spaces-tabs-votes   ||
||  TableName       |  spaces-tabs-votes                                                ||
||  TableSizeBytes  |  0                                                                ||
||  TableStatus     |  ACTIVE                                                           ||
|+------------------+-------------------------------------------------------------------+|
|||                                AttributeDefinitions                                |||
||+--------------------------------------------------------------+---------------------+||
|||  AttributeName                                               |  id                 |||
|||  AttributeType                                               |  S                  |||
||+--------------------------------------------------------------+---------------------+||
|||                                      KeySchema                                     |||
||+--------------------------------------------------------+---------------------------+||
|||  AttributeName                                         |  id                       |||
|||  KeyType                                               |  HASH                     |||
||+--------------------------------------------------------+---------------------------+||
|||                                ProvisionedThroughput                               |||
||+------------------------------------------------------------------+-----------------+||
|||  LastDecreaseDateTime                                            |  0.0            |||
|||  LastIncreaseDateTime                                            |  0.0            |||
|||  NumberOfDecreasesToday                                          |  0              |||
|||  ReadCapacityUnits                                               |  1              |||
|||  WriteCapacityUnits                                              |  1              |||
||+------------------------------------------------------------------+-----------------+||
[root@centos702 lambda5]#

3. ローカルに API Gateway の用意


以下のようにコマンドを実行して AMS SAM Local の API Gateway を起動します。
API Gateway にアクセスすると、ここに、ログが表示されます。
[root@centos702 lambda5]# sam local start-api
A newer version of the AWS SAM CLI is available!
Your version:   0.2.4
Latest version: 0.2.6
See https://github.com/awslabs/aws-sam-local for upgrade instructions

2018/02/17 18:15:27 Connected to Docker 1.35
2018/02/17 18:15:27 Fetching lambci/lambda:python2.7 image for python2.7 runtime...
python2.7: Pulling from lambci/lambda
Digest: sha256:87a3b9bb1ba6ae666b0087107852b67415cae0660669ae5633a0ab828aea2c69
Status: Image is up to date for lambci/lambda:python2.7

Mounting vote_function.vote_handler (python2.7) at http://127.0.0.1:3000/ [GET]
Mounting vote_function.vote_handler (python2.7) at http://127.0.0.1:3000/ [POST]

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.



4. 動作確認


API Gateway に POST メソッドでアクセスし、"tabs" に投票します。
lambda が実行されて、DynamoDBが更新されます。
[root@centos702 lambda5]# curl -XPOST -d '{"vote": "tabs"}' http://127.0.0.1:3000/
tabs now has 3 votes

API Gateway には、以下のようにログが表示されます。
2018/02/17 18:16:22 Invoking vote_function.vote_handler (python2.7)
2018/02/17 18:16:22 Mounting /root/lambda5 as /var/task:ro inside runtime container
START RequestId: 6e24834b-e125-413a-ac8b-bbd219b7b77d Version: $LATEST
END RequestId: 6e24834b-e125-413a-ac8b-bbd219b7b77d
REPORT RequestId: 6e24834b-e125-413a-ac8b-bbd219b7b77d Duration: 1068 ms Billed Duration: 0 ms Memory Size: 0 MB Max Memory Used: 23 MB

API Gateway に GET メソッドでアクセスし、"tabs" の投票数を参照します。
[root@centos702 lambda5]# curl -XGET -d '{"vote": "tabs"}' http://127.0.0.1:3000/
{"tabs": 3}