はじめに
serverless framework はサーバーレス構成に特化したIaCで、手軽にLambdaなどを使ったアプリケーションを構築することができます。
しかしながら、構築は比較的簡単に行なえますが実際に触ってみるとテストがやや大変です。
今回はそんな悩みを解決するために、Lambda + DynamoDB の環境をコードを変更せずにローカルで作成する方法について紹介します。
構成
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
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 から呼び出す環境変数を設定します。
DYNAMODB_TABLE_NAME=sampleTable
seed.json
ローカルで作成した DynamoDB へ手動でデータを入れるのは非常に大変です。
そこで、serverless-dynamodb
に備えられているseed機能を使います。
これは、テーブル作成時に自動でjsonデータを挿入してくれる機能です。
パーティションキーが含まれていないデータを入れるとエラーになるので注意しましょう。
[
{
"UserID": "1",
"UserName": "John",
"UserEmail": "xxx@example.com"
},
{
"UserID": "2",
"UserName": "Doe",
"UserEmail": ""
}
]
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_OFFLINE
がtrue
になります。この値を見てclientで使うエンドポイントを分けることで、ローカルとaws上で同じコードを使って動かすことができます。
今回はサンプルなので、テーブルのデータをすべて返す処理としています。
必要パッケージのインストール
ローカルで実行するためにはLambdaで使うpythonのパッケージをインストールする必要があります。
今回はboto3が必要なので、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
試しに叩いてみます。
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 の開発環境を作成する方法を紹介しました。ローカル開発環境があることでテストやテストデータの準備のハードルが大きく下がります。
コード全文はリポジトリに上げたので、全文読みたい方はこちらを参考にしてください。
参考