💭

LocalStack上にServerlessでAPI Gateway、Lambda、DynamoDBを構築してみる

2021/12/17に公開

はじめに

LocalStack上にServerless Frameworkを使用して「LocalStack上にAPI Gateway、Lambda、DynamoDB環境を構築してみる」と同じ環境を構築する。
CurlでApiを叩き。LambdaでDynamoDBからデータを取得しレスポンスを返す処理を実装する。

動作確認環境

WSL2上のUbuntu 20.04でLocalStackを使ってみる」で構築した環境を使い作成する。

Guest OS (WSL2)

追加アイテムだけ記載する。

  • Ubuntu 20.04
    • node: 16.13.1
    • npm: 8.1.2
    • serverless: 2.69.1
    • serverless-localstack: 0.4.35

準備

作業フォルダー作成

hoge@AA:~$ mkdir aws-serverless
hoge@AA:~$ cd aws-serverless

node.jsを準備する

nvmインストールする
必要に応じて、nvmのインストール手順を確認して指定を変更する。

hoge@AA:~/aws-serverless$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
hoge@AA:~/aws-serverless$ source ~/.bashrc
hoge@AA:~/aws-serverless$ command -v nvm
nvm

node.jsのLTS版をインストールする。[1]

hoge@AA:~/aws-serverless$ nvm install --lts
Installing latest LTS version.
Downloading and installing node v16.13.1...
Downloading https://nodejs.org/dist/v16.13.1/node-v16.13.1-linux-x64.tar.xz...
##################################################################################################### 100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v16.13.1 (npm v8.1.2)
Creating default alias: default -> lts/* (-> v16.13.1)
hoge@AA:~/aws-serverless$ node --version
v16.13.1
hoge@AA:~/aws-serverless$ npm --version
8.1.2

npmをバージョンアップする。[2]

hoge@AA:~/aws-serverless$ npm install -g npm@8.3.0

Serverless Frameworkを準備する

Serverless Frameworkをインストールする。

hoge@AA:~/aws-serverless$ npm i -g serverless
hoge@AA:~/aws-serverless$ serverless --version
Framework Core: 2.69.1
Plugin: 5.5.1
SDK: 4.3.0
Components: 3.18.1

Serverless Framework向けのLocalStackプラグインをインストールする。

hoge@AA:~/aws-serverless$ npm i -D serverless-localstack
hoge@AA:~/aws-serverless$ cat node_modules/serverless-localstack/package.json | jq .version
"0.4.35"

docker-composeを準備する

docker-compose.ymlファイルを作成する。
必要なSERVICESを指定する。
serverlessが使用する、s3,iam,sts,cloudformationをSERVICESに追加している。[3]

serverless.ymlを編集する。

hoge@AA:~/aws-serverless$ vi docker-compose.yml
docker-compose.yml 編集後の状態
version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
    image: localstack/localstack:0.13.1
    network_mode: bridge
    ports:
      - "4566:4566"
      - "4571:4571"
    environment:
      - SERVICES=apigateway,lambda,dynamodb,s3,iam,sts,cloudformation
      - DEBUG=${DEBUG- }
      - DATA_DIR=${DATA_DIR- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY- }  # only required for Pro
      - HOST_TMP_FOLDER=${TMPDIR:-/tmp/}localstack
      - DOCKER_HOST=unix:///var/run/docker.sock
      - LAMBDA_EXECUTOR=docker-reuse
    volumes:
      - "${TMPDIR:-/tmp}/localstack:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"

docker-compose.ymlの編集を保存して終了する。(escキーを押す。「:wq」を入力してエンターキーを押す。)

LocalStackを立ち上げる。[4]

hoge@AA:~/aws-serverless$ docker-compose up -d

Serverless Frameworkの設定

プロジェクト作成

serverlessコマンドでPython3用のプロジェクトを作成する。

hoge@AA:~/aws-serverless$ serverless create --template aws-python3 --path python3-localstack
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/home/hoge/aws-serverless/python3-localstack"
Serverless: Successfully generated boilerplate for template: "aws-python3"
hoge@AA:~/aws-serverless$ cd python3-localstack
hoge@AA:~/aws-serverless/python3-localstack$

serverless.yml

serverless.ymlを編集する。

hoge@AA:~/aws-serverless/python3-localstack$ vi serverless.yml

API Gatewayにするため、「functions→users→events→http」にする必要がある。
httpApiはAPI Gateway V2になるためProが必要。

serverless.yml
service: python3-localstack

frameworkVersion: '2'

provider:
  name: aws
  runtime: python3.8
  lambdaHashingVersion: 20201221
  region: us-east-1
  profile: localstack
  environment:
    DYNAMODB_TABLE: myUserTable
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}"

functions:
  users:
    handler: handler.getUsers
    events:
      - http:
          path: users
          method: get
          cors: true


resources:
  Resources:
    usersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:provider.environment.DYNAMODB_TABLE}
        AttributeDefinitions:
          - AttributeName: Name
            AttributeType: S
          - AttributeName: Index
            AttributeType: N
        KeySchema:
          - AttributeName: Name
            KeyType: HASH
          - AttributeName: Index
            KeyType: RANGE
        ProvisionedThroughput:
          ReadCapacityUnits: 10
          WriteCapacityUnits: 5

plugins:
  - serverless-localstack

custom:
  localstack:
    stages:
      - local

serverless.ymlの編集を保存して終了する。(escキーを押す。「:wq」を入力してエンターキーを押す。)

handler.py

handler.pyを編集する。

hoge@AA:~/aws-serverless/python3-localstack$ vi handler.py
handler.py
import os
import boto3
from boto3.session import Session
from datetime import datetime

session = Session(
    aws_access_key_id='dummy',
    aws_secret_access_key='dummy',
    region_name='us-east-1'
)

if os.getenv('LOCALSTACK_HOSTNAME') is None:
    endpoint = 'http://localhost:4566'
else:
    endpoint=f"http://{os.environ['LOCALSTACK_HOSTNAME']}:4566"

dynamodb = session.resource(
    service_name='dynamodb', 
    endpoint_url=endpoint
)

def getUsers(event, context):
    table = dynamodb.Table('myUserTable')
    data = []
    response = table.scan()
    while True:
        data.extend(response["Items"])
        if "LastEvaluatedKey" not in response:
            break
        response = table.scan(ExclusiveStartKey=response["LastEvaluatedKey"])

    return {
        'statusCode': 200,
        'body':{ 
            'users':data
        }
    }

handler.pyの編集を保存して終了する。(escキーを押す。「:wq」を入力してエンターキーを押す。)

動作確認

デプロイ

serverlessコマンドでLocalStackへデプロイする。

hoge@AA:~/aws-serverless/python3-localstack$ serverless deploy -s local
Serverless: Using serverless-localstack
Serverless: Packaging service...
〜中略〜
Serverless: Checking Stack update progress...
...............
Serverless: Stack update finished...
Service Information
service: python3-localstack
stage: local
region: us-east-1
stack: python3-localstack-local
resources: 13
api keys:
  None
endpoints:
  http://localhost:4566/restapis/XXXXXXXX/local/_user_request_
functions:
  users: python3-localstack-local-users
layers:
  None

アイテムの追加

DynamoDBアイテム追加する。

hoge@AA:~/aws-serverless/python3-localstack$ awslocal dynamodb put-item --table-name myUserTable \
--item '{"Name":{"S": "Ishida Mio"}, "Index":{"N": "1000"}, "Gender":{"S": "Female"}, "Tel":{"S": "0769625106"}, "Mail":{"S": "mio1094@ccfrevk.ll"}, "Birthday":{"S": "1982/02/26"}}' \
--profile=localstack
hoge@AA:~/aws-serverless/python3-localstack$ awslocal dynamodb put-item --table-name myUserTable \
--item '{"Name":{"S": "Shimura Kokoro"}, "Index":{"N": "1001"}, "Gender":{"S": "Female"}, "Tel":{"S": "0829490871"}, "Mail":{"S": "kokoro414@bevhlk.xju"}, "Birthday":{"S": "1990/02/17"}}' \
--profile=localstack
hoge@AA:~/aws-serverless/python3-localstack$ awslocal dynamodb put-item --table-name myUserTable \
--item '{"Name":{"S": "Ishimura Masaki"}, "Index":{"N": "1002"}, "Gender":{"S": "Male"}, "Tel":{"S": "0848254480"}, "Mail":{"S": "masaki_ishimura@kokn.qgj"}, "Birthday":{"S": "1992/06/20"}}' \
--profile=localstack
hoge@AA:~/aws-serverless/python3-localstack$ awslocal dynamodb put-item --table-name myUserTable \
--item '{"Name":{"S": "Shirai Tomoko"}, "Index":{"N": "1003"}, "Gender":{"S": "Female"}, "Tel":{"S": "0742216492"}, "Mail":{"S": "tomoko225@vlyqs.pxt"}, "Birthday":{"S": "1982/09/29"}}' \
--profile=localstack
hoge@AA:~/aws-serverless/python3-localstack$ awslocal dynamodb put-item --table-name myUserTable \
--item '{"Name":{"S": "Yasui Tomoyuki"}, "Index":{"N": "1004"}, "Gender":{"S": "Male"}, "Tel":{"S": "095953680"}, "Mail":{"S": "tomoyuki772@jheauwpl.lyo"}, "Birthday":{"S": "1996/04/23"}}' \
--profile=localstack

API呼び出し

APIを呼び出してレスポンスを確認する。
APIはデプロイのendpointsとfunctionsを組み合わせたものになります。

curl -X GET -s http://localhost:4566/restapis/XXXXXXXX/local/_user_request_/users | jq
{
  "users": [
    {
      "Index": 1000,
      "Tel": "0769625106",
      "Birthday": "1982/02/26",
      "Gender": "Female",
      "Mail": "mio1094@ccfrevk.ll",
      "Name": "Ishida Mio"
    },
    {
      "Index": 1002,
      "Tel": "0848254480",
      "Birthday": "1992/06/20",
      "Gender": "Male",
      "Mail": "masaki_ishimura@kokn.qgj",
      "Name": "Ishimura Masaki"
    },
    {
      "Index": 1001,
      "Tel": "0829490871",
      "Birthday": "1990/02/17",
      "Gender": "Female",
      "Mail": "kokoro414@bevhlk.xju",
      "Name": "Shimura Kokoro"
    },

削除

serverless.yml設定の問題かECRのエラーが出て使えない。[5]

hoge@AA:~/aws-serverless/python3-localstack$ serverless remove -s local

参考にしたサイト

脚注
  1. lts指定のためインストールタイミングによりバージョンが変わる。 ↩︎

  2. 実行時期によりさらに新しいバージョンが出ている可能性はある ↩︎

  3. どのサービスが足りなくてServerlessが動かないのかわからななくて。DEBUG=1へ変更して、docker-compose upでログを見ながら確認した。 ↩︎

  4. エラーが出る場合はdockerサービス立ち上がっているかを確認する。「sudo service docker start」 ↩︎

  5. ECRがPRO向けの機能でうまく動かない。 ↩︎

Discussion