5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Serverless Framework の DynamoDB + lambda でコードを変えずにローカル開発環境を構築する方法

Posted at

はじめに

serverless framework はサーバーレス構成に特化したIaCで、手軽にLambdaなどを使ったアプリケーションを構築することができます。
しかしながら、構築は比較的簡単に行なえますが実際に触ってみるとテストがやや大変です。
今回はそんな悩みを解決するために、Lambda + DynamoDB の環境をコードを変更せずにローカルで作成する方法について紹介します。

構成

serverlessframework-dynamodb-local-sample.drawio.png

DynamoDB local

DynamoDBはローカル環境で実行できるパッケージがAWS公式から提供されています。

Serverless Framework はこちらのパッケージを用いてローカル環境を構築します。
ドキュメントにも書かれているように、実行にはJRE(Javaの実行環境)が必要になるため、インストールしてから環境構築をしていきましょう(1敗)。

環境

  • aws-cli/2.15.19
  • serverless framework 3.38.0
  • java 21.0.2 (JREがDynamoDB localの実行に必要)
  • node.js v21.6.1
  • python 3.11(Lambda のランタイム)

構築

Serverless Framework や aws cli の設定されていることが前提で進みます。

ディレクトリ構成

こちらの構成で進めていきます。
各ファイルの中身を詳しく見たい方はGitHubのリンクを貼っておくのでそちらを見てください。

.
├── handler
│   └── hello.py      # Lambda handler
├── seeds
│   └── seed.json  # DynamoDB Local 用データセット
├── package-lock.json
├── package.json
├── requirements.txt  # Lambdaで使用するパッケージ
├── .env
└── serverless.yml

各種インストール

プラグイン

serverless framework で DynamoDB local を動かすためのプラグインとして、serverless-dynamodb-localがあります。
しかし、こちらは最新の DynamoDB local に対応していないため、serverless-dynamodb-localをフォークして作られたserverless-dynamodbを使用します。

serverless plugin install -n serverless-dynamodb
# 他の必要プラグインもインストールします
serverless plugin install -n serverless-python-requirements
serverless plugin install -n serverless-offline

DynamoDB local のインストール

serverlessコマンドでインストールできます。
JREはインストールされないので、別途インストールしておきましょう。

serverless dynamodb install

各ファイルの編集

serverless.yml

serverless.yml
service: dynamodb-local-sample
# .envファイルからの環境変数読み込みを有効化
useDotenv: true

custom:
  stage: ${opt:stage, 'dev'}
  dynamodbTableName: ${env:DYNAMODB_TABLE_NAME}-${self:custom.stage}
  # DynamoDB Local
  serverless-dynamodb:
    docker: false
    start:
      migrate: true
      host: 127.0.0.1
      port: 8000
      seed: true
    seed:
      sampleDatabese:
        sources:
          - table: ${self:custom.dynamodbTableName}
            sources: [./seeds/seed.json]
    stages:
      - dev

provider:
  name: aws
  stage: ${self:custom.stage}
  region: ap-northeast-1
  runtime: python3.10
  environment:
    TZ: Asia/Tokyo

functions:
  hello:
    name: ${self:service}-handler
    handler: handler/hello.lambda_handler
    environment:
      DYNAMODB_TABLE_NAME: ${self:custom.dynamodbTableName}
    events:
      - http:
          path: /hello
          method: get

resources:
  Resources:
    myDynamoDBTable:
      Type: AWS::DynamoDB::Table
      Properties:
        AttributeDefinitions:
          - AttributeName: UserID
            AttributeType: S
        KeySchema:
          - AttributeName: UserID
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 5
          WriteCapacityUnits: 5
        TableName: ${self:custom.dynamodbTableName}

plugins:
  - serverless-python-requirements
  - serverless-offline
  - serverless-dynamodb

.env

serverless framework から呼び出す環境変数を設定します。

.env
DYNAMODB_TABLE_NAME=sampleTable

seed.json

ローカルで作成した DynamoDB へ手動でデータを入れるのは非常に大変です。
そこで、serverless-dynamodbに備えられているseed機能を使います。
これは、テーブル作成時に自動でjsonデータを挿入してくれる機能です。
パーティションキーが含まれていないデータを入れるとエラーになるので注意しましょう。

seeds/seed.json
[
    {
        "UserID": "1",
        "UserName": "John",
        "UserEmail": "xxx@example.com"
    },
    {
        "UserID": "2",
        "UserName": "Doe",
        "UserEmail": ""
    }
]

hello.py

handler/hello.py
import json
import boto3
import os


# 実行環境によって分岐
if os.environ.get("IS_OFFLINE", "") == "true":
    dynamodb_client = boto3.client("dynamodb", endpoint_url="http://localhost:8000")
else:
    dynamodb_client = boto3.client("dynamodb")

table_name = os.environ.get("DYNAMODB_TABLE_NAME", "")


def lambda_handler(event, context):
    # DynamoDB からデータを取得
    response = dynamodb_client.scan(TableName=table_name)
    items = response.get("Items", [])
    # ページネーション
    while "LastEvaluatedKey" in response:
        response = dynamodb_client.scan(
            TableName=table_name, ExclusiveStartKey=response["LastEvaluatedKey"]
        )
        items.extend(response["Items"])

    return {
        "statusCode": 200,
        "body": json.dumps(items),
    }

boto3クライアントの宣言方法が今回の記事で一番重要な部分です。
severless local で実行した場合、環境変数IS_OFFLINEtrueになります。この値を見てclientで使うエンドポイントを分けることで、ローカルとaws上で同じコードを使って動かすことができます。
今回はサンプルなので、テーブルのデータをすべて返す処理としています。

必要パッケージのインストール

ローカルで実行するためにはLambdaで使うpythonのパッケージをインストールする必要があります。
今回はboto3が必要なので、requirements.txtに追記します。

requirements.txt
boto3==1.34.54
botocore==1.34.54
jmespath==1.0.1
python-dateutil==2.9.0.post0
s3transfer==0.10.0
six==1.16.0
urllib3==2.0.7
python -m venv .venv
source ./.venv/bin/activate
# powershellの場合
# ./.venv/bin/activate.ps1
pip install -r requirements.txt

実行

serverless offline start

エンドポイントが表示されれば成功です。
image.png

試しに叩いてみます。

curl "http://localhost:3000/dev/hello"
[{"UserEmail": {"S": "xxx@example.com"}, "UserID": {"S": "1"}, "UserName": {"S": "John"}}, {"UserEmail": {"S": ""}, "UserID": {"S": "2"}, "UserName": {"S": "Doe"}

ちゃんとDynamoDB に入れたデータが取得できていますね。

まとめ

今回はローカルで Lambda + DynamoDB の開発環境を作成する方法を紹介しました。ローカル開発環境があることでテストやテストデータの準備のハードルが大きく下がります。

コード全文はリポジトリに上げたので、全文読みたい方はこちらを参考にしてください。

参考

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?